feat: interview review module with whisper.cpp ASR + AI analysis + frontend page
New backend module 'interview-review' provides: - Audio upload (50MB limit, MP3/M4A/WAV/AAC/OGG/MP4/WebM) - Text transcript submission - whisper.cpp local ASR integration (tiny + base models) - AI analysis (4-dimension scoring: logic/expression/professionalism/stability) - Speech analysis (filler words detection, pace, duration) - Async processing pipeline with status polling - Graceful fallback to mock ASR when whisper unavailable New frontend page 'pages/review/review.vue' with 3 modes: - List mode: review history with status indicators - Upload mode: audio file upload or text paste - Report mode: score radar, dimension bars, analysis details Docs updated: PROJECT-STATUS.md v4.4, FEATURE-LIST.md v4.2, ROADMAP.md v4.2
This commit is contained in:
@@ -1,56 +1,261 @@
|
||||
## 开发交付流程
|
||||
# 职引 (ZhiYin) — AGENTS.md
|
||||
|
||||
每次实施/开发完功能后,必须按以下步骤执行:
|
||||
> AI 模拟面试教练,专注校招。NestJS + uni-app(Vue3) + MongoDB。
|
||||
|
||||
### Step 1: 代码评审
|
||||
- 检查变更是否符合现有代码模式(schema、service、controller、module 的结构一致性)
|
||||
- 检查是否有未使用的变量、import、参数
|
||||
- 检查命名规范是否与项目一致
|
||||
- 检查是否有重复代码或可复用逻辑
|
||||
---
|
||||
|
||||
### Step 2: 安全评审
|
||||
- **注入防护**:检查所有外部输入(请求 body、query、params)是否有注入风险(HTML、Shell、NoSQL)
|
||||
- **认证/授权**:检查端点是否正确地加了 JWT guard 或 `@Public()`;管理端接口是否有 AdminGuard
|
||||
- **并发安全**:检查所有修改用户额度/积分的操作是否使用原子操作(`findOneAndUpdate + $inc`),而非 read-modify-write
|
||||
- **敏感信息**:检查是否误将密钥、凭据、token 暴露到响应或日志中
|
||||
- **XSS/CSRF**:检查用户内容输出到 HTML/PDF 时是否做了转义
|
||||
## 一、项目结构
|
||||
|
||||
### Step 3: 性能优化
|
||||
- 检查是否有 N+1 查询(循环内查数据库),应使用批量查询或聚合
|
||||
- 检查大表查询是否有索引覆盖
|
||||
- 检查是否有不必要的 `.lean()` 缺失(读操作用 `exec()` 但不需要 mongoose document 方法时应加 `.lean()`)
|
||||
- 检查是否有内存泄漏风险(如 puppeteer browser 未在 finally 中 close)
|
||||
- Throttler/限流是否合理
|
||||
|
||||
### Step 4: 完整测试
|
||||
```bash
|
||||
# 构建检查(注意内存限制,服务器 OOM 时加 --max-old-space-size)
|
||||
cd /root/opencode-workspace/zhiyin/backend && NODE_OPTIONS="--max-old-space-size=2048" npx nest build 2>&1
|
||||
|
||||
# 单元测试
|
||||
npm test -- --forceExit --detectOpenHandles 2>&1
|
||||
|
||||
# 如果有变更的模块,验证关键 endpoint curl 可访问
|
||||
```
|
||||
zhiyin/
|
||||
├── backend/ # NestJS 10.x 后端 (端口 3006, 前缀 /api)
|
||||
│ └── src/
|
||||
│ ├── main.ts # 入口:DOMMatrix polyfill → NestFactory → CORS → ValidationPipe
|
||||
│ ├── app.module.ts # 根模块:导入全部子模块 + JWT/Throttler/Mongoose
|
||||
│ ├── common/
|
||||
│ │ ├── guards/ # JwtAuthGuard (全局), admin.guard.ts
|
||||
│ │ ├── strategies/ # JwtStrategy
|
||||
│ │ ├── decorators/ # @CurrentUser, @Public()
|
||||
│ │ └── filters/ # AllExceptionsFilter
|
||||
│ └── modules/ # 19 个模块(详见下文)
|
||||
├── zhiyin-app/ # uni-app 3.x 前端 (H5 + 微信小程序)
|
||||
│ └── src/
|
||||
│ ├── pages/ # 18 个页面 (pages.json 路由)
|
||||
│ ├── services/api.ts # API 调用封装 (uni.request)
|
||||
│ ├── config.ts # 端点定义 + api() 辅助函数
|
||||
│ └── App.vue # 设计 Token + 全局样式
|
||||
└── docs/ # 产品/架构/部署/路线图文档
|
||||
```
|
||||
|
||||
### Step 5: 同步修复
|
||||
- 上述步骤发现的问题必须修复后再发布
|
||||
- 修复后重新执行 Step 4 验证构建通过
|
||||
### 后端模块清单
|
||||
|
||||
### Step 6: 部署到生产
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| `user` | 手机/邮箱/密码/微信登录, JWT, 配额 |
|
||||
| `interview` | AI 面试核心(多轮对话 + 评分 + 报告 + 进度) |
|
||||
| `ai` | AI 调用封装(deepseek-v4-flash 主 + step-3.5-flash 备) |
|
||||
| `analyze` | 简历诊断 / 优化 / 技能缺口分析 |
|
||||
| `resume` | 简历 CRUD |
|
||||
| `member` | 会员套餐 / 权益扣减 |
|
||||
| `payment` | 微信支付 v3(Native + JSAPI + 回调) |
|
||||
| `progress` | 进步轨迹雷达图 / 打卡日历 / 行业基准 / 岗位匹配 |
|
||||
| `contribution` | 面经贡献 + 公司题库(数据飞轮核心) |
|
||||
| `schedule` | 定时任务:VIP 过期降级、每日一题推送、微信 token 刷新 |
|
||||
| `share` | 分享链接生成 / 访问追踪 / 积分奖励 |
|
||||
| `tts` | 语音合成(TTS) |
|
||||
| `admin` | 管理后台 API |
|
||||
| `positions` | 热门岗位维护 |
|
||||
| `interview-review` | 面试复盘(音频上传 -> whisper.cpp ASR -> AI 评析 -> 口语分析) |
|
||||
| `upload` | 文件上传(PDF/图片) |
|
||||
| `email` | 邮件发送 |
|
||||
| `daily-question` | 每日一题 API |
|
||||
| `schemas/` | 共享 Schema(pricing 定价、site-config、company-bank 等) |
|
||||
|
||||
### 前端页面(3 Tab + 16 子页)
|
||||
|
||||
- **Tab1 面试**: pages/index/index → interview → report
|
||||
- **Tab2 面经**: pages/history/history → contribute → company-bank
|
||||
- **Tab3 我的**: pages/user/user → login/member/progress/resume/review/about/agreement/privacy/admin/share
|
||||
- 其他: internship, result
|
||||
|
||||
---
|
||||
|
||||
## 二、架构约定(必须遵守)
|
||||
|
||||
### 模块模式
|
||||
每个业务模块遵循 NestJS 标准结构:
|
||||
```
|
||||
模块名/
|
||||
├── 模块名.module.ts # @Module({ imports: [MongooseModule.forFeature(...)], controllers, providers, exports })
|
||||
├── 模块名.controller.ts # @Controller('prefix'),注入 service
|
||||
├── 模块名.service.ts # @Injectable(),注入 Model
|
||||
└── 模块名.schema.ts # @Schema({ timestamps: true }),class + SchemaFactory
|
||||
```
|
||||
|
||||
### 认证体系
|
||||
- **全局守卫**: `JwtAuthGuard` 默认拦截所有路由(在 `app.module.ts` 中 `APP_GUARD` 注册)
|
||||
- **白名单**: 公开接口加 `@Public()` 装饰器(登录、注册、支付回调、分享访问等)
|
||||
- **管理员**: 管理接口加 `@UseGuards(AdminGuard)`(admin controller 内部)
|
||||
- **当前用户**: `@CurrentUser('userId')` 从 JWT payload 提取用户 ID
|
||||
- **JWT 过期**: 7 天,在 `app.module.ts` 和每个模块的 `JwtModule.register` 中配置
|
||||
|
||||
### 安全硬性要求
|
||||
1. **JWT_SECRET 必须来自环境变量**,不允许任何硬编码 fallback(已有历史漏洞修复)
|
||||
2. **所有外部输入**经过 class-validator `ValidationPipe({ whitelist: true, forbidNonWhitelisted: true })`
|
||||
3. **修改用户额度/积分**使用 `findOneAndUpdate + $inc` 原子操作,禁止 read-modify-write
|
||||
4. **支付订单查询**校验 userId 归属,防止 IDOR
|
||||
5. **CORS** 生产环境必须配置白名单(当前在 `main.ts` 中从 `CORS_ORIGINS` 环境变量读取)
|
||||
6. **用户内容输出到响应**时避免泄漏敏感信息(验证码、密钥等)
|
||||
7. **MongoDB 查询**中对外部输入的字符串做特殊字符转义(尤其在 admin 模块)
|
||||
|
||||
### AI 调用
|
||||
- 主模型: `opencode-go` (deepseek-v4-flash)
|
||||
- 备用模型: NVIDIA (stepfun-ai/step-3.5-flash)
|
||||
- 主用不可用时自动切换(在 `ai` 模块处理)
|
||||
- 环境变量: `AI_PRIMARY_KEY`, `AI_BACKUP_KEY`
|
||||
|
||||
### 支付(微信支付 v3)
|
||||
- Native 支付(H5 扫码): `POST /payment/create`
|
||||
- JSAPI 支付(小程序内): `POST /payment/jsapi`
|
||||
- 支付回调: `POST /payment/notify`(@Public,验签 + 解密 + 自动开会员)
|
||||
- 需要微信商户证书文件(通过 postbuild 复制到 dist)
|
||||
|
||||
---
|
||||
|
||||
## 三、开发命令
|
||||
|
||||
### 后端
|
||||
```bash
|
||||
# 构建
|
||||
cd /root/opencode-workspace/zhiyin/backend && npx nest build
|
||||
# 路径: backend/
|
||||
npm run start:dev # 开发模式(watch)
|
||||
npm run build # 编译到 dist/
|
||||
npm test # 单元测试(43 个,jest --forceExit --detectOpenHandles)
|
||||
npm run test:e2e # 集成测试(11 个,需 MongoDB 运行)
|
||||
npm run test:cov # 覆盖率报告
|
||||
npm run test:browser # Playwright API 测试(需后端运行)
|
||||
```
|
||||
|
||||
# 同步到生产目录
|
||||
### 前端
|
||||
```bash
|
||||
# 路径: zhiyin-app/
|
||||
npm run dev:mp-weixin # 微信小程序开发(uni -p mp-weixin)
|
||||
npm run build:mp-weixin # 构建小程序(输出 dist/build/mp-weixin/)
|
||||
npm run dev:h5 # H5 开发(端口 8888,带 /api 代理到 localhost:3006)
|
||||
npm run build:h5 # 构建 H5(输出 dist/build/h5/)
|
||||
npm test # 前端单元测试(vitest,7 个)
|
||||
```
|
||||
|
||||
### 构建检查
|
||||
```bash
|
||||
# 后端构建(注意 OOM:需 NODE_OPTIONS="--max-old-space-size=2048")
|
||||
cd backend && NODE_OPTIONS="--max-old-space-size=2048" npx nest build
|
||||
```
|
||||
|
||||
### 部署
|
||||
```bash
|
||||
cd backend && npx nest build
|
||||
cp -rf dist/* /www/wwwroot/server/zhiyin/backend/dist/
|
||||
|
||||
# 复制证书(postbuild 替代)
|
||||
cp -r certs /www/wwwroot/server/zhiyin/backend/dist/src/certs
|
||||
|
||||
# 重启
|
||||
pm2 restart yhl-backend
|
||||
|
||||
# 验证
|
||||
sleep 3 && curl -s http://localhost:3006/api/user/wx-login -X POST -H "Content-Type: application/json" -d '{"code":"test"}'
|
||||
```
|
||||
|
||||
### 小程序上传
|
||||
```bash
|
||||
cd zhiyin-app && npm run build:mp-weixin && node scripts/upload-mp.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、测试注意事项
|
||||
|
||||
- **e2e 测试**需要 MongoDB 运行 + `jest-setup.ts` 设置测试 JWT_SECRET
|
||||
- `payment.controller.spec.ts` 涉及微信支付,需 mock 外部依赖
|
||||
- `benchmark.service.spec.ts` 涉及行业基准计算
|
||||
- Playwright 测试需要后端已在运行(测试 `api.browser.spec.ts`)
|
||||
- 测试文件位置:`backend/src/**/*.spec.ts`(单元),`backend/test/*.e2e-spec.ts`(集成),`backend/test/*.browser.spec.ts`(浏览器)
|
||||
- 前端测试:`zhiyin-app/src/**/*.spec.ts`(vitest + jsdom)
|
||||
|
||||
---
|
||||
|
||||
## 五、定时任务(3 个 cron,在 schedule 模块)
|
||||
|
||||
| 服务 | 周期 | 职责 |
|
||||
|------|------|------|
|
||||
| `VipExpiryService` | 每日 00:00 | 扫描过期 VIP 并降级为 free 计划 |
|
||||
| `DailyQuestionPushService` | 每日 09:00 | 通过微信订阅消息推送每日一题(需配置模板 ID) |
|
||||
| `WechatTokenService` | 每 2 小时 | 刷新微信 access_token(缓存到 Redis) |
|
||||
|
||||
---
|
||||
|
||||
## 六、项目状态与开发阶段
|
||||
|
||||
**当前**: Phase 0.5 完成,Phase 1(MVP 上线)进行中
|
||||
|
||||
| 阶段 | 状态 | 关键交付 |
|
||||
|------|------|---------|
|
||||
| Phase 0: 战略升级 | ✅ 完成 | 定价重构(免费 + ¥19.9/月),三层壁垒设计 |
|
||||
| Phase 0.5: 壁垒构建 | ✅ 完成 | 数据飞轮(面经贡献+题库),留存入围(进步轨迹+打卡日历+每日一题) |
|
||||
| Phase 1: MVP 上线 | 🚧 当前 | 小程序审核提交、微信登录联调、生产部署、100 人内测 |
|
||||
| Phase 1.5: 商业化 | 📋 规划 | 冲刺版 ¥49.9/月、每日一题定时推送、PMF 验证 |
|
||||
| Phase 2: 增强 + 题库 | 📋 规划 | 50+ 校招岗位、技能缺口分析、公司真题库建设 |
|
||||
| Phase 3: 秋招冲刺 | 📋 规划 | 高校合作、B 端服务、KOC 推广 |
|
||||
|
||||
详细产品规划见 `docs/PRODUCT-PLAN.md`,路线图见 `docs/ROADMAP.md`。
|
||||
|
||||
---
|
||||
|
||||
## 七、环境变量
|
||||
|
||||
### 后端(`backend/.env`,不提交 git,在 `.gitignore` 中)
|
||||
```
|
||||
MONGODB_URI=mongodb://localhost:27017/zhiyin
|
||||
JWT_SECRET=your-strong-secret-at-least-32-chars
|
||||
PORT=3006
|
||||
AI_PRIMARY_KEY=xxx
|
||||
AI_BACKUP_KEY=xxx
|
||||
WECHAT_APPID=wxf466b3c3bc411ffc
|
||||
WECHAT_MCHID=xxx
|
||||
WECHAT_API_KEY=xxx
|
||||
WECHAT_SERIAL_NO=xxx
|
||||
WECHAT_PRIVATE_KEY_PATH=/path/to/apiclient_key.pem
|
||||
WX_DAILY_QUESTION_TMPL=微信订阅消息模板 ID
|
||||
CORS_ORIGINS=http://localhost:8888,https://zhiyin.yzrcloud.cn
|
||||
WHISPER_CPP_PATH=/home/wlt/whisper.cpp # whisper.cpp 路径
|
||||
WHISPER_MODEL=base # ASR 模型:tiny / base / small
|
||||
WHISPER_LANGUAGE=zh # ASR 语言
|
||||
WHISPER_THREADS=4 # ASR CPU 线程数
|
||||
```
|
||||
|
||||
### 前端(`zhiyin-app/.env.production`,已提交 git)
|
||||
```
|
||||
VITE_API_BASE_URL=https://zhiyinwx.yzrcloud.cn/api
|
||||
VITE_APP_NAME=AI磁场
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、技术细节与坑
|
||||
|
||||
1. **DOMMatrix polyfill**: `main.ts` 顶部有 pdf-parse 所需的浏览器 API polyfill(DOMMatrix / DOMPoint),新增 PDF 相关功能时注意兼容性
|
||||
2. **postbuild**: `backend/package.json` 中的 `postbuild` 脚本自动复制 `certs/` 到 `dist/src/certs/`,这是微信支付证书的必要步骤
|
||||
3. **微信小程序 appid**: `zhiyin-app/manifest.json` 中 `mp-weixin.appid = wxf466b3c3bc411ffc`;开发模式 `appid = __UNI__DEV__`
|
||||
4. **前端 API 调用**: `zhiyin-app/src/services/api.ts` 封装了 `uni.request`,自动处理 token 注入(从 `uni.getStorageSync('token')`)和 401 过期跳转
|
||||
5. **前端环境判断**: `config.ts` 中使用 `// #ifdef H5` / `// #ifdef MP-WEIXIN` 条件编译区分 H5 和小程序
|
||||
6. **API 限流**: 100 次/60 秒(在 `app.module.ts` 中配置),注意避免在定时任务和批量操作中被限
|
||||
7. **验证码**: 开发模式下手机验证码固定为 `123456`(`user.service.ts` 中实现),生产环境需移除
|
||||
8. **MongoDB**: 8 个核心集合 + 2 个分享集合
|
||||
|
||||
---
|
||||
|
||||
## 九、交付检查清单(每次实施/修改后执行)
|
||||
|
||||
### Step 1: 代码评审
|
||||
- [ ] 是否符合现有模块模式(schema→service→controller→module)
|
||||
- [ ] 是否有多余变量、import、参数
|
||||
- [ ] 命名是否与项目一致
|
||||
- [ ] 是否有重复代码或可复用逻辑
|
||||
|
||||
### Step 2: 安全评审
|
||||
- [ ] 外部输入是否有注入风险(HTML、Shell、NoSQL)
|
||||
- [ ] 接口是否正确加了 JWT guard 或 `@Public()`
|
||||
- [ ] 管理端接口是否有 AdminGuard
|
||||
- [ ] 修改用户额度/积分是否用 `$inc` 原子操作
|
||||
- [ ] 敏感信息是否泄漏到响应或日志中
|
||||
- [ ] 用户内容输出是否做了转义
|
||||
|
||||
### Step 3: 性能评审
|
||||
- [ ] 是否存在 N+1 查询
|
||||
- [ ] 大表查询是否有索引覆盖
|
||||
- [ ] 读操作是否该加 `.lean()`
|
||||
- [ ] 是否有内存泄漏风险(puppeteer 等资源未释放)
|
||||
|
||||
### Step 4: 测试验证
|
||||
```bash
|
||||
cd backend && NODE_OPTIONS="--max-old-space-size=2048" npx nest build
|
||||
npm test -- --forceExit --detectOpenHandles
|
||||
```
|
||||
|
||||
### Step 5: LSP 诊断
|
||||
- [ ] Changed files diagnostics clean
|
||||
- [ ] 无 `as any` / `@ts-ignore` / `@ts-expect-error`
|
||||
Reference in New Issue
Block a user