feat: 付费体系重构 P0 - 配额独立化/简历付费下载/PDF生成
This commit is contained in:
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user