# 产品改造方案：付费体系重构 + 数字人面试

> 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": 1900,
      "durationDays": 30,
      "credits": {
        "interview": 999,
        "resumeOptimize": 20,
        "resumeDownload": 10
      },
      "features": [
        "免费版全部权益",
        "AI 数字人面试无限次",
        "详细面试报告（四维评分）",
        "进步轨迹雷达图 + 打卡",
        "每日一题推送",
        "参考回答思路",
        "公司真题库"
      ]
    },
    "sprint": {
      "price": 4900,
      "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 ] /月          │
│  面试额度              [ 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 不过期
