300 lines
9.8 KiB
Markdown
300 lines
9.8 KiB
Markdown
# 产品改造方案:付费体系重构 + 数字人面试
|
||
|
||
> 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 不过期
|