# 产品改造方案:付费体系重构 + 数字人面试 > 2026-06-11 · v1.0 ## 概述 将现有一刀切的 `remaining` 免费额度模式,改为**按产品线独立计费 + 会员套餐含额度 + 数据库配置化**的灵活付费体系。 ## 数据库模型变更 ### User Schema 新增字段 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `interviewCredits` | number | 1 | AI 数字人面试可用次数(含首次免费) | | `resumeOptimizeCredits` | number | 0 | 简历优化可用次数 | | `resumeDownloadCredits` | number | 0 | 简历下载可用次数 | | `freeOptimizeUsed` | number | 0 | 已使用免费优化次数(上限 3) | 旧的 `remaining` 字段保留,仅限老用户兼容,新逻辑不再使用。 ### Resume Schema 新增字段 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `version` | number | 1 | 版本号,每次 AI 改写 +1 | | `contentHash` | string | '' | 内容 MD5,用于判断是否改版 | | `paidDownload` | boolean | false | 是否已购买该版本的下载权 | `contentHash` 在 create 和 optimize 后自动更新。 ### PaymentOrder Schema 新增字段 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `type` | string | 'membership' | `membership` / `interview` / `optimize` / `download` | | `metadata` | Mixed | {} | 产品粒度信息(如 `{ resumeId, version }`) | ### 定价配置(SiteConfig key=`pricing`) 单一配置源,所有定价和套餐内容后台可编辑: ```json { "interview": { "firstFree": true, "pricePerSession": 500, "creditsPerPurchase": 1 }, "resumeOptimize": { "freeLimit": 3, "pricePerOptimize": 300, "creditsPerPurchase": 1 }, "resumeDownload": { "pricePerDownload": 200, "creditsPerPurchase": 1 }, "plans": { "growth": { "price": 1990, "durationDays": 30, "credits": { "interview": 999, "resumeOptimize": 20, "resumeDownload": 10 }, "features": [ "免费版全部权益", "AI 数字人面试无限次", "详细面试报告(四维评分)", "进步轨迹雷达图 + 打卡", "每日一题推送", "参考回答思路", "公司真题库" ] }, "sprint": { "price": 4990, "durationDays": 30, "credits": { "interview": 999, "resumeOptimize": 50, "resumeDownload": 30 }, "features": [ "成长版全部权益", "AI 语音分析(语气词/语速检测)", "技能缺口分析报告", "学习路径推荐", "真人导师 1v1 点评(每月 1 次)", "简历精修(每月 1 次)", "内推优先" ] } } } ``` ## 配额检查逻辑 所有配额检查收敛到统一的 `QuotaGuard` 或 `QuotaService`,不再散落在各处。 ```typescript // 配额服务 class QuotaService { checkInterview(user): // user.interviewCredits >= 1 checkOptimize(user): // user.resumeOptimizeCredits > 0 // || user.freeOptimizeUsed < freeLimit checkDownload(user, resume): // resume.paidDownload // || user.resumeDownloadCredits > 0 deductInterview(user) deductOptimize(user) deductDownload(user, resume) } ``` ### 各类用户行为对应的检查 | 行为 | 检查条件 | 扣减项 | |------|----------|--------| | 开始 AI 面试 | `interviewCredits >= 1` | `interviewCredits -= 1` | | AI 诊断/优化简历 | `resumeOptimizeCredits > 0 \|\| freeOptimizeUsed < 3` | 付费优先扣,免费次之 | | 下载简历 PDF | `resume.paidDownload \|\| resumeDownloadCredits > 0` | 标记 resume.paidDownload | ## 支付流程扩展 ### 现有:套餐购买(不变) ``` POST /payment/create { plan: 'growth' | 'sprint' } → 创建 Membership 订单(type='membership') → WeChat Pay → 回调/activate → 注入 plan 对应的全部 credits ``` ### 新增:按次购买 ``` POST /payment/create-product { type, resumeId?, count? } → 从 pricing config 读取单价 → 创建对应 type 订单 → WeChat Pay → 回调 → 根据 type 增加对应用户 credits / 标记下载 ``` 支持的产品: - `interview`:增加 `interviewCredits` - `optimize`:增加 `resumeOptimizeCredits` - `download`:标记 `resume.paidDownload=true` ## 数字人面试架构 ### 整体流程 ``` 用户选择岗位 → 配额检查 → AI 出题 → 回答 → AI 评分+下一题 → ... → 完成 → 报告 → 头像回顾视频(可选) ``` 每次 AI 回复时,同步调用 TTS 生成音频,返回给前端: ```typescript // 面试回答响应 { text: "请介绍一下你的项目经验", audioUrl: "/tts/cache/abc123.mp3", // edge-tts 生成 durationMs: 2800, // 音频时长 animationTimings: [], // 可选:逐字时间戳 } ``` ### TTS 服务 `backend/src/modules/tts/tts.service.ts` ```typescript class TtsService { async synthesize(text: string): Promise<{ audioPath: string; durationMs: number }> { // 1. 按 text MD5 检查缓存 // 2. 未命中 → child_process.exec('edge-tts ...') // 3. 保存到 /tmp/tts-cache/ 或 certs 同目录 // 4. 返回路径和时长 } } ``` edge-tts 命令示例: ```bash edge-tts --voice zh-CN-XiaoxiaoNeural --text "你好" --write-media /tmp/tts-cache/abc.mp3 ``` ### 前端数字人组件 `zhiyin-app/src/components/digital-human.vue` ``` ┌─────────────────────┐ │ ┌───────┐ │ │ │ 头像 │ │ ← 静态 PNG + CSS呼吸/眨眼 │ │ (嘴型) │ │ ← Canvas 覆盖嘴部区域 │ └───────┘ │ │ "请介绍你的项目" │ ← 字幕气泡 │ │ │ [▼ 音频波形] │ ← 可选 └─────────────────────┘ ``` 嘴型驱动: 1. `AudioContext.createMediaElementSource(audioEl)` → `AnalyserNode` 2. 每帧读取 `analyser.getByteTimeDomainData(dataArray)` 3. 计算 RMS → 映射到嘴型开合度 (0~1) 4. Canvas 绘制椭圆(开)或线(闭) ### 新接口 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/interview/create-avatar` | 创建数字人面试(含配额检查) | | POST | `/interview/:id/answer-avatar` | 回答+AI 回复(含 TTS audioUrl) | | GET | `/tts/:hash` | 获取 TTS 音频文件 | ## 简历下载流程图 ``` 用户查看优化结果 │ ├─ 点击「下载 PDF」 │ │ │ ├─ resume.paidDownload === true → 直接生成 → 返回 PDF │ │ │ └─ resume.paidDownload === false │ │ │ ├─ user.resumeDownloadCredits > 0 │ │ → 扣 1 credit → 标记 paidDownload → 生成 PDF │ │ │ └─ 无 credits │ → 弹出支付窗口(¥X/次) │ → WeChat Pay → 标记 paidDownload → 生成 PDF │ └─ 再次优化(内容改变) → contentHash 变化 → 新 resume 记录(version++) → paidDownload = false → 需重新付费下载 ``` Puppeteer PDF 生成 (`resume-pdf.service.ts`): 1. 读取 resume.content(markdown/HTML) 2. 套用模板(含姓名、岗位、日期等) 3. `puppeteer.launch()` → `page.setContent(html)` → `page.pdf({ format: 'A4' })` 4. 返回 PDF buffer 5. 可选:同时生成 Word(用 HTML → mammoth 或 libreoffice) ## 管理后台配置 在现有 `admin.vue` 的 Config Tab 中增加: ``` ┌─────────────────────────────────────────────┐ │ 定价配置 [保存] │ │ │ │ AI 面试单价 ¥ [ 5 ] /次 │ │ 简历优化单价 ¥ [ 3 ] /次 │ │ 简历下载单价 ¥ [ 2 ] /次 │ │ 免费优化次数 [ 3 ] 次 │ │ 首次面试免费 [✓] │ │ │ │ ─── 成长版 ─── │ │ 价格 ¥ [ 19.9 ] /月 │ │ 面试额度 [ 999 ] 次 │ │ 优化额度 [ 20 ] 次 │ │ 下载额度 [ 10 ] 次 │ │ 功能列表 [编辑] │ │ │ │ ─── 冲刺版 ─── │ │ ... │ └─────────────────────────────────────────────┘ ``` ## 实施优先级 | 阶段 | 内容 | 文件涉及 | |------|------|----------| | P0-1 | 定价配置化 + User 新字段 + 统一配额检查 | user.schema, SiteConfig, QuotaService | | P0-2 | 简历下载付费 + Puppeteer PDF | resume.schema, resume.service, resume.controller, ResumePdfService | | P0-3 | 支付扩展(按次购买) | payment-order.schema, payment.controller | | P1-1 | TTS 服务 | tts.service, tts.controller, tts.module | | P1-2 | 数字人面试前端组件 | digital-human.vue, interview.vue | | P1-3 | 面试接口改版 + 配额接入 | interview.service, interview.controller | | P2 | Admin 定价管理界面 | admin.vue, admin.controller | ## 兼容性注意事项 - `remaining` 字段保留,老用户数据不受影响 - 新用户注册默认 `interviewCredits=1`(首次免费) - 会员激活时注入 plan 定义的 credits - 过期降级时不清零已购买的 credits,仅阻止会员专属 privileges - 已购买的单次 credits 不过期