145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
import type { SubscriptionPlan } from '@prisma/client'
|
||
|
||
/**
|
||
* 订阅相关的工具函数
|
||
*/
|
||
|
||
// 支持部分套餐数据的类型
|
||
type PlanLike = { price: number } | SubscriptionPlan | null | undefined
|
||
|
||
/**
|
||
* 判断套餐是否为 Pro 级别(价格超过 19)
|
||
* 这是一个纯函数,可以在客户端和服务端使用
|
||
*/
|
||
export function isPlanPro(plan: PlanLike): boolean {
|
||
if (!plan) return false
|
||
return plan.price > 19
|
||
}
|
||
|
||
/**
|
||
* 判断套餐是否为免费套餐(价格为 0)
|
||
*/
|
||
export function isPlanFree(plan: PlanLike): boolean {
|
||
if (!plan) return true
|
||
return plan.price === 0
|
||
}
|
||
|
||
/**
|
||
* 获取套餐的显示级别
|
||
*/
|
||
export function getPlanTier(plan: PlanLike): 'free' | 'pro' | 'enterprise' {
|
||
if (!plan || plan.price === 0) return 'free'
|
||
if (plan.price > 50) return 'enterprise' // 为未来的企业版预留
|
||
if (plan.price > 19) return 'pro'
|
||
return 'free'
|
||
}
|
||
|
||
/**
|
||
* 获取套餐的图标组件名称
|
||
*/
|
||
export function getPlanIcon(plan: PlanLike): 'Star' | 'Crown' | 'Building' {
|
||
const tier = getPlanTier(plan)
|
||
switch (tier) {
|
||
case 'enterprise':
|
||
return 'Building'
|
||
case 'pro':
|
||
return 'Crown'
|
||
case 'free':
|
||
default:
|
||
return 'Star'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取套餐的颜色主题
|
||
*/
|
||
export function getPlanTheme(plan: PlanLike): {
|
||
gradient: string
|
||
iconGradient: string
|
||
textColor: string
|
||
borderColor?: string
|
||
} {
|
||
const tier = getPlanTier(plan)
|
||
|
||
switch (tier) {
|
||
case 'enterprise':
|
||
return {
|
||
gradient: 'bg-gradient-to-r from-purple-50/60 to-indigo-50/60 dark:from-purple-950/10 dark:to-indigo-950/10',
|
||
iconGradient: 'bg-gradient-to-br from-purple-500 to-indigo-500 dark:from-purple-400 dark:to-indigo-400',
|
||
textColor: 'text-purple-700 dark:text-purple-300',
|
||
borderColor: 'border-purple-200 dark:border-purple-800'
|
||
}
|
||
case 'pro':
|
||
return {
|
||
gradient: 'bg-gradient-to-r from-amber-50/60 to-orange-50/60 dark:from-amber-950/10 dark:to-orange-950/10',
|
||
iconGradient: 'bg-gradient-to-br from-amber-500 to-orange-500 dark:from-amber-400 dark:to-orange-400',
|
||
textColor: 'text-orange-700 dark:text-orange-300',
|
||
borderColor: 'border-primary'
|
||
}
|
||
case 'free':
|
||
default:
|
||
return {
|
||
gradient: 'bg-gradient-to-r from-slate-50/60 to-gray-50/60 dark:from-slate-950/5 dark:to-gray-950/5',
|
||
iconGradient: 'bg-gradient-to-br from-slate-400 to-gray-500 dark:from-slate-500 dark:to-gray-400',
|
||
textColor: 'text-slate-600 dark:text-slate-400'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化套餐价格显示
|
||
*/
|
||
export function formatPlanPrice(plan: PlanLike & { interval?: string }, t?: (key: string) => string): string {
|
||
if (!plan || plan.price === 0) {
|
||
return t?.('free.price') || 'Free'
|
||
}
|
||
|
||
const price = `$${plan.price}`
|
||
const planWithInterval = plan as { interval?: string }
|
||
const interval = planWithInterval.interval === 'year' ? '/year' : '/month'
|
||
|
||
return `${price}${interval}`
|
||
}
|
||
|
||
/**
|
||
* 获取套餐限制的类型安全访问器
|
||
*/
|
||
export function getPlanLimits(plan: SubscriptionPlan | null | undefined): {
|
||
promptLimit: number
|
||
maxVersionLimit: number
|
||
creditMonthly: number
|
||
} {
|
||
if (!plan) {
|
||
return {
|
||
promptLimit: 500,
|
||
maxVersionLimit: 3,
|
||
creditMonthly: 0
|
||
}
|
||
}
|
||
|
||
const limits = plan.limits as Record<string, unknown>
|
||
return {
|
||
promptLimit: (limits?.promptLimit as number) || 500,
|
||
maxVersionLimit: (limits?.maxVersionLimit as number) || 3,
|
||
creditMonthly: (limits?.creditMonthly as number) || 0
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取套餐功能的类型安全访问器
|
||
*/
|
||
export function getPlanFeatures(plan: SubscriptionPlan | null | undefined): string[] {
|
||
if (!plan) return []
|
||
|
||
const features = plan.features as Record<string, unknown>
|
||
return Object.keys(features).filter(key => features[key] === true)
|
||
}
|
||
|
||
/**
|
||
* 检查套餐是否包含特定功能
|
||
*/
|
||
export function planHasFeature(plan: SubscriptionPlan | null | undefined, feature: string): boolean {
|
||
const features = getPlanFeatures(plan)
|
||
return features.includes(feature)
|
||
}
|