feat: 付费体系重构 P0 - 配额独立化/简历付费下载/PDF生成

This commit is contained in:
yuzhiran
2026-06-12 09:31:11 +08:00
parent 5d407b4f79
commit 065fe7a186
23 changed files with 965 additions and 106 deletions
@@ -5,6 +5,7 @@ import { User, UserDocument } from '../user/user.schema'
import { PaymentOrder, PaymentOrderDocument } from '../payment/payment-order.schema'
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'
import { CurrentUser } from '../../common/decorators/current-user.decorator'
import { QuotaService } from '../user/quota.service'
import { Public } from '../../common/decorators/public.decorator'
const GROWTH_PRICE = 1990
@@ -24,14 +25,15 @@ const PLANS: Record<string, PlanConfig> = {
free: {
id: 'free', name: '免费版', price: 0, dailyLimit: FREE_DAILY_LIMIT,
features: [
'每日 2 次 AI 模拟面试', '基础面试报告', '通用题库随机出题', '简历诊断(限 3 次)',
'AI 模拟面试 1 次(体验)', '基础面试报告', '通用题库随机出题', '简历优化(限 3 次免费',
],
},
growth: {
id: 'growth', name: '成长版', price: GROWTH_PRICE, dailyLimit: 999,
features: [
'免费版全部权益', '无限面试次数', '详细面试报告(四维评分)',
'免费版全部权益', 'AI 数字人面试无限次', '详细面试报告(四维评分)',
'进步轨迹雷达图 + 打卡', '每日一题推送', '参考回答思路', '公司真题库',
'简历优化 20 次/月', '简历下载 10 次/月',
],
},
sprint: {
@@ -39,6 +41,7 @@ const PLANS: Record<string, PlanConfig> = {
features: [
'成长版全部权益', 'AI 语音分析(语气词/语速检测)', '技能缺口分析报告',
'学习路径推荐', '真人导师 1v1 点评(每月 1 次)', '简历精修(每月 1 次)', '内推优先',
'简历优化 50 次/月', '简历下载 30 次/月',
],
},
}
@@ -51,6 +54,7 @@ export class MemberController {
constructor(
@InjectModel(User.name) private userModel: Model<UserDocument>,
@InjectModel(PaymentOrder.name) private orderModel: Model<PaymentOrderDocument>,
private quotaService: QuotaService,
) {}
@Public()
@@ -82,6 +86,10 @@ export class MemberController {
vipExpireAt: user.vipExpireAt,
sprintExpireAt: user.sprintExpireAt,
sprintRemaining: user.sprintRemaining || 0,
interviewCredits: user.interviewCredits ?? 1,
resumeOptimizeCredits: user.resumeOptimizeCredits ?? 0,
resumeDownloadCredits: user.resumeDownloadCredits ?? 0,
freeOptimizeUsed: user.freeOptimizeUsed ?? 0,
isVip: user.plan !== 'free',
}
}
@@ -99,16 +107,19 @@ export class MemberController {
const expireAt = new Date()
expireAt.setDate(expireAt.getDate() + DURATION_DAYS)
const credits = { interview: 999, resumeOptimize: 20, resumeDownload: 10 }
if (order.plan === 'sprint') {
user.plan = 'sprint'
user.sprintExpireAt = expireAt
user.sprintRemaining = 10
credits.interview = 999
credits.resumeOptimize = 50
credits.resumeDownload = 30
} else {
user.plan = 'growth'
user.vipExpireAt = expireAt
}
user.remaining = 999
await user.save()
await this.quotaService.setPlanQuota(userId, order.plan, credits)
return { success: true, plan: user.plan, planName: PLANS[user.plan]?.name, expireAt }
}