4cd889c081
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
261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
# 职引 (ZhiYin) — AGENTS.md
|
||
|
||
> AI 模拟面试教练,专注校招。NestJS + uni-app(Vue3) + MongoDB。
|
||
|
||
---
|
||
|
||
## 一、项目结构
|
||
|
||
```
|
||
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/ # 产品/架构/部署/路线图文档
|
||
```
|
||
|
||
### 后端模块清单
|
||
|
||
| 模块 | 职责 |
|
||
|------|------|
|
||
| `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
|
||
# 路径: 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/
|
||
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` |