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-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-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) {
|
|
|
|
|
throw new Error('没有套餐数据')
|
|
|
|
|
}
|
|
|
|
|
|
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-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
|
|
|
|
|
|
|
|
items.push({
|
|
|
|
|
code,
|
|
|
|
|
mode,
|
|
|
|
|
live: liveValue,
|
|
|
|
|
expire: expireValue,
|
|
|
|
|
price: sku.price,
|
2026-04-20 15:36:36 +08:00
|
|
|
count_min: countMin,
|
2026-04-18 15:47:20 +08:00
|
|
|
})
|
|
|
|
|
priceMap.set(code, sku.price)
|
|
|
|
|
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) {
|
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|