Files
web/src/components/composites/purchase/shared/sku.ts

196 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-04-18 14:30:30 +08:00
import {ProductItem} from '@/actions/product'
import {PurchaseKind, PurchaseMode} from './resource'
2026-04-18 15:47:20 +08:00
export type PurchaseSkuItem = {
code: string
mode: PurchaseMode
live: string
expire: string
price: string
2026-04-20 15:36:36 +08:00
count_min: number
2026-06-18 15:11:28 +08:00
discount: number
2026-04-18 15:47:20 +08:00
}
2026-04-18 14:30:30 +08:00
export type PurchaseSkuData = {
2026-04-18 15:47:20 +08:00
items: PurchaseSkuItem[]
2026-04-18 14:30:30 +08:00
priceMap: Map<string, string>
2026-04-20 15:36:36 +08:00
countMinMap: Map<string, number>
2026-06-18 15:11:28 +08:00
discountMap: Map<string, number>
2026-04-18 15:47:20 +08:00
modeList: PurchaseMode[]
2026-04-18 14:30:30 +08:00
liveList: string[]
expireList: string[]
}
export function parsePurchaseSkuList(kind: PurchaseKind, skuList: ProductItem['skus']): PurchaseSkuData {
if (!skuList?.length) {
2026-06-02 13:19:28 +08:00
throw new Error('没有套餐数据')
2026-04-18 14:30:30 +08:00
}
2026-04-18 15:47:20 +08:00
const items: PurchaseSkuItem[] = []
2026-04-18 14:30:30 +08:00
const priceMap = new Map<string, string>()
2026-04-20 15:36:36 +08:00
const countMinMap = new Map<string, number>()
2026-06-18 15:11:28 +08:00
const discountMap = new Map<string, number>()
2026-04-18 15:47:20 +08:00
const modeSet = new Set<PurchaseMode>()
2026-04-18 14:30:30 +08:00
const liveSet = new Set<number>()
const expireSet = new Set<number>()
for (const sku of skuList) {
const params = new URLSearchParams(sku.code)
2026-04-18 15:47:20 +08:00
const mode = parsePurchaseSkuMode(params.get('mode'))
2026-04-18 14:30:30 +08:00
const live = Number(params.get('live') || '0')
const expire = Number(params.get('expire') || '0')
2026-04-18 15:47:20 +08:00
if (!mode || live <= 0) {
continue
2026-04-18 14:30:30 +08:00
}
2026-04-18 15:47:20 +08:00
const liveValue = String(live)
const expireValue = mode === '1' ? String(expire || '0') : '0'
const code = getPurchaseSkuKey({
mode,
live: liveValue,
expire: expireValue,
})
2026-04-20 15:36:36 +08:00
const countMin = typeof sku.count_min === 'number' ? sku.count_min : Number(sku.count_min) || 0
countMinMap.set(code, countMin)
2026-04-18 15:47:20 +08:00
2026-06-18 15:11:28 +08:00
const skuDiscount = sku.discount ?? 100
2026-04-18 15:47:20 +08:00
items.push({
code,
mode,
live: liveValue,
expire: expireValue,
price: sku.price,
2026-04-20 15:36:36 +08:00
count_min: countMin,
2026-06-18 15:11:28 +08:00
discount: skuDiscount,
2026-04-18 15:47:20 +08:00
})
priceMap.set(code, sku.price)
2026-06-18 15:11:28 +08:00
discountMap.set(code, skuDiscount)
2026-04-18 15:47:20 +08:00
modeSet.add(mode)
liveSet.add(live)
2026-04-18 14:30:30 +08:00
if (kind === 'short') {
2026-04-18 15:47:20 +08:00
if (mode === '1' && expire > 0) {
2026-04-18 14:30:30 +08:00
expireSet.add(expire)
}
}
else if (expire > 0) {
expireSet.add(expire)
}
2026-04-18 15:47:20 +08:00
}
2026-04-18 14:30:30 +08:00
2026-04-18 15:47:20 +08:00
if (items.length === 0) {
2026-06-02 13:19:28 +08:00
throw new Error('没有可用的套餐数据')
2026-04-18 14:30:30 +08:00
}
return {
2026-04-18 15:47:20 +08:00
items,
2026-04-18 14:30:30 +08:00
priceMap,
2026-04-20 15:36:36 +08:00
countMinMap,
2026-06-18 15:11:28 +08:00
discountMap,
2026-04-18 15:47:20 +08:00
modeList: (['2', '1'] as const).filter(mode => modeSet.has(mode)),
liveList: sortNumericValues(liveSet),
expireList: sortNumericValues(expireSet),
2026-04-18 14:30:30 +08:00
}
}
2026-04-18 15:47:20 +08:00
function parsePurchaseSkuMode(mode: string | null): PurchaseMode | null {
if (mode === 'time') {
return '1'
}
if (mode === 'quota') {
return '2'
}
return null
}
function sortNumericValues(values: Iterable<number>) {
return Array.from(values).sort((a, b) => a - b).map(String)
}
export function getPurchaseSkuKey(props: {
2026-04-18 14:30:30 +08:00
mode: PurchaseMode
live: string
expire: string
}) {
const params = new URLSearchParams()
params.set('mode', props.mode === '1' ? 'time' : 'quota')
params.set('live', props.live || '0')
params.set('expire', props.mode === '1' ? props.expire || '0' : '0')
2026-04-18 15:47:20 +08:00
return params.toString()
}
export function getAvailablePurchaseLives(skuData: PurchaseSkuData, props: {
mode: PurchaseMode
expire?: string
}) {
return sortNumericValues(new Set(
skuData.items
.filter(item => item.mode === props.mode)
.filter(item => !props.expire || item.expire === props.expire)
.map(item => Number(item.live)),
))
}
export function getAvailablePurchaseExpires(skuData: PurchaseSkuData, props: {
mode: PurchaseMode
live?: string
}) {
return sortNumericValues(new Set(
skuData.items
.filter(item => item.mode === props.mode)
.filter(item => !props.live || item.live === props.live)
.filter(item => item.expire !== '0')
.map(item => Number(item.expire)),
))
}
export function hasPurchaseSku(skuData: PurchaseSkuData, props: {
mode: PurchaseMode
live: string
expire: string
}) {
return skuData.priceMap.has(getPurchaseSkuKey(props))
}
export function getPurchaseSkuPrice(priceMap: Map<string, string>, props: {
mode: PurchaseMode
live: string
expire: string
}) {
return priceMap.get(getPurchaseSkuKey(props))
2026-04-18 14:30:30 +08:00
}
2026-06-18 15:11:28 +08:00
export function getPurchaseSkuDiscount(discountMap: Map<string, number>, props: {
mode: PurchaseMode
live: string
expire: string
}) {
return discountMap.get(getPurchaseSkuKey(props))
}
2026-04-18 14:30:30 +08:00
export function formatPurchaseLiveLabel(live: string, kind: PurchaseKind) {
const minutes = Number(live)
if (kind === 'long') {
return `${minutes / 60} 小时`
}
if (minutes % 60 === 0) {
return `${minutes / 60} 小时`
}
return `${minutes} 分钟`
}
2026-04-20 15:36:36 +08:00
export function getPurchaseSkuCountMin(
skuData: PurchaseSkuData,
props: {mode: PurchaseMode, live: string, expire: string},
): number {
const key = getPurchaseSkuKey(props)
return skuData.countMinMap.get(key) ?? 0
}