Prmbr/src/lib/subscription-utils.ts
2025-08-05 22:43:18 +08:00

145 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}