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

235 lines
5.6 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 { 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 // 免费版没有月度额度
}
}
}