# 职引 (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`