235 lines
5.6 KiB
TypeScript
235 lines
5.6 KiB
TypeScript
import { prisma } from '@/lib/prisma'
|
||
|
||
export type CreditType = 'system_gift' | 'subscription_monthly' | 'user_purchase'
|
||
|
||
export interface CreditSummary {
|
||
totalBalance: number
|
||
activeCredits: {
|
||
id: string
|
||
amount: number
|
||
type: CreditType
|
||
note: string | null
|
||
expiresAt: Date | null
|
||
createdAt: Date
|
||
}[]
|
||
expiredCredits: {
|
||
id: string
|
||
amount: number
|
||
type: CreditType
|
||
note: string | null
|
||
expiresAt: Date | null
|
||
createdAt: Date
|
||
}[]
|
||
}
|
||
|
||
/**
|
||
* 获取用户的信用额度总览
|
||
*/
|
||
export async function getUserCreditSummary(userId: string): Promise<CreditSummary> {
|
||
const now = new Date()
|
||
|
||
// 获取所有信用记录
|
||
const allCredits = await prisma.credit.findMany({
|
||
where: { userId, isActive: true },
|
||
orderBy: { createdAt: 'desc' }
|
||
})
|
||
|
||
const activeCredits = []
|
||
const expiredCredits = []
|
||
let totalBalance = 0
|
||
|
||
for (const credit of allCredits) {
|
||
const creditData = {
|
||
id: credit.id,
|
||
amount: credit.amount,
|
||
type: credit.type as CreditType,
|
||
note: credit.note,
|
||
expiresAt: credit.expiresAt,
|
||
createdAt: credit.createdAt
|
||
}
|
||
|
||
// 检查是否过期
|
||
if (credit.expiresAt && credit.expiresAt < now) {
|
||
expiredCredits.push(creditData)
|
||
// 标记过期的信用为非激活状态
|
||
await prisma.credit.update({
|
||
where: { id: credit.id },
|
||
data: { isActive: false }
|
||
})
|
||
} else {
|
||
activeCredits.push(creditData)
|
||
totalBalance += credit.amount
|
||
}
|
||
}
|
||
|
||
return {
|
||
totalBalance,
|
||
activeCredits,
|
||
expiredCredits
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为新用户添加系统赠送的5USD信用额度(1个月后过期)
|
||
*/
|
||
export async function addSystemGiftCredit(userId: string): Promise<void> {
|
||
const expiresAt = new Date()
|
||
expiresAt.setMonth(expiresAt.getMonth() + 1) // 1个月后过期
|
||
|
||
await prisma.credit.create({
|
||
data: {
|
||
userId,
|
||
amount: 5.0,
|
||
type: 'system_gift',
|
||
note: '系统赠送 - 新用户礼包',
|
||
expiresAt,
|
||
isActive: true
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 为Pro用户添加月度信用额度
|
||
*/
|
||
export async function addMonthlySubscriptionCredit(userId: string): Promise<void> {
|
||
const expiresAt = new Date()
|
||
expiresAt.setMonth(expiresAt.getMonth() + 1) // 1个月后过期
|
||
|
||
await prisma.credit.create({
|
||
data: {
|
||
userId,
|
||
amount: 20.0,
|
||
type: 'subscription_monthly',
|
||
note: 'Pro套餐月度额度',
|
||
expiresAt,
|
||
isActive: true
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 用户购买信用额度(永久有效)
|
||
*/
|
||
export async function addUserPurchaseCredit(
|
||
userId: string,
|
||
amount: number,
|
||
note?: string
|
||
): Promise<void> {
|
||
await prisma.credit.create({
|
||
data: {
|
||
userId,
|
||
amount,
|
||
type: 'user_purchase',
|
||
note: note || '用户充值',
|
||
expiresAt: null, // 永久有效
|
||
isActive: true
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 检查Pro用户是否需要刷新月度额度
|
||
*/
|
||
export async function checkAndRefreshProMonthlyCredit(userId: string): Promise<boolean> {
|
||
// 获取用户信息
|
||
const user = await prisma.user.findUnique({
|
||
where: { id: userId },
|
||
select: { subscribePlan: true }
|
||
})
|
||
|
||
if (!user || user.subscribePlan !== 'pro') {
|
||
return false
|
||
}
|
||
|
||
// 查找最近的月度订阅额度
|
||
const lastMonthlyCredit = await prisma.credit.findFirst({
|
||
where: {
|
||
userId,
|
||
type: 'subscription_monthly',
|
||
isActive: true
|
||
},
|
||
orderBy: { createdAt: 'desc' }
|
||
})
|
||
|
||
const now = new Date()
|
||
|
||
// 如果没有月度额度记录,或者最近的已过期,则添加新的
|
||
if (!lastMonthlyCredit || (lastMonthlyCredit.expiresAt && lastMonthlyCredit.expiresAt < now)) {
|
||
await addMonthlySubscriptionCredit(userId)
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
/**
|
||
* 消费信用额度
|
||
*/
|
||
export async function consumeCredit(userId: string, amount: number): Promise<boolean> {
|
||
const creditSummary = await getUserCreditSummary(userId)
|
||
|
||
if (creditSummary.totalBalance < amount) {
|
||
return false // 余额不足
|
||
}
|
||
|
||
// 按优先级消费额度:先消费即将过期的,再消费永久的
|
||
const sortedCredits = creditSummary.activeCredits.sort((a, b) => {
|
||
// 有过期时间的排在前面,没有过期时间的排在后面
|
||
if (a.expiresAt && !b.expiresAt) return -1
|
||
if (!a.expiresAt && b.expiresAt) return 1
|
||
if (a.expiresAt && b.expiresAt) {
|
||
return a.expiresAt.getTime() - b.expiresAt.getTime()
|
||
}
|
||
return a.createdAt.getTime() - b.createdAt.getTime()
|
||
})
|
||
|
||
let remainingAmount = amount
|
||
|
||
for (const credit of sortedCredits) {
|
||
if (remainingAmount <= 0) break
|
||
|
||
const consumeFromThis = Math.min(credit.amount, remainingAmount)
|
||
const newAmount = credit.amount - consumeFromThis
|
||
|
||
if (newAmount <= 0) {
|
||
// 这个额度用完了,标记为非激活
|
||
await prisma.credit.update({
|
||
where: { id: credit.id },
|
||
data: { isActive: false }
|
||
})
|
||
} else {
|
||
// 更新剩余额度
|
||
await prisma.credit.update({
|
||
where: { id: credit.id },
|
||
data: { amount: newAmount }
|
||
})
|
||
}
|
||
|
||
remainingAmount -= consumeFromThis
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* 获取订阅计划的限制
|
||
* @deprecated 请使用 SubscriptionService.getUserPermissions() 替代
|
||
*/
|
||
export function getSubscriptionLimits(plan: string) {
|
||
// 保留向后兼容性,但建议使用新的订阅服务
|
||
switch (plan) {
|
||
case 'pro':
|
||
return {
|
||
promptLimit: 5000, // 修正为正确的 Pro 限制
|
||
maxVersionLimit: 10,
|
||
monthlyCredit: 20.0
|
||
}
|
||
case 'free':
|
||
default:
|
||
return {
|
||
promptLimit: 500, // 修正为正确的免费限制
|
||
maxVersionLimit: 3,
|
||
monthlyCredit: 0.0 // 免费版没有月度额度
|
||
}
|
||
}
|
||
} |