# CLAUDE.md — duoqi-api > 多奇服务端 API,基于 Fastify + TypeScript + Drizzle ORM + MySQL 8.0+ > 包管理器:**bun**(禁止使用 npm) ## 项目概述 多奇(Duoqi)是游戏化知识闯关学习平台。duoqi-api 是三端(HarmonyOS / Flutter / Web)共享的后端服务,从 Phase 1 起即为 HarmonyOS 客户端提供 API 支持。 ## 相关项目 - **duoqi-flutter**: `../duoqi-flutter/`, Flutter 客户端代码库. ## 技术栈 | 层面 | 选型 | 说明 | |------|------|------| | 后端框架 | **Fastify 5** | 高性能,内置 JSON Schema 验证,TypeScript 友好 | | ORM | **Drizzle ORM** | 轻量、类型安全,`src/db/schema.ts` 为唯一真相源 | | 数据库 | MySQL 8.0+ | 阿里云 RDS | | 认证 | @fastify/jwt | 自建 JWT(华为 ID Kit + 游客模式) | | 校验 | Zod | 环境变量 + 请求体校验(所有路由已接入 Zod) | | 测试 | Vitest | 单元测试框架,19 个测试全部通过 | | 运行时 | Node.js (ESM) | `"type": "module"`,import 使用 `.js` 后缀 | ## Quick Start ```bash bun install # 安装依赖 cp .env.example .env # 复制环境变量模板,填入实际值 bun run dev # 启动开发服务器,默认端口 3000 ``` 必填环境变量:`DATABASE_URL`, `JWT_SECRET`, `ADMIN_TOKEN` ## 开发命令 ```bash bun run dev # 启动开发服务器(tsx watch 热重载) bun run typecheck # 类型检查(tsc --noEmit) bun run build # 编译到 dist/ bun run test # 运行测试(vitest run) bun run db:push # 推送 schema 到数据库(开发用) bun run db:generate # 生成迁移文件 bun run db:migrate # 执行迁移 bun run db:seed # 导入种子数据(分类 → 技能树 → 题目 → 成就) bun run db:studio # Drizzle Studio(数据库可视化浏览器) bun run lint # ESLint 检查 ``` ## 项目结构 ``` src/ ├── index.ts # 入口:Fastify 实例 + 插件注册 + 路由挂载 ├── db/ │ ├── client.ts # 数据库连接(mysql2 pool + drizzle) │ └── schema.ts # 全部 15 张表定义(唯一真相源) ├── types/ # TypeScript 类型(auth, quiz, user, api) ├── utils/ │ ├── config.ts # 环境变量(Zod 校验,启动时 fail-fast) │ └── errors.ts # AppError 层级 + 统一错误处理器 ├── middleware/ │ ├── auth.ts # JWT 认证(排除公开路径和 admin 路径) │ ├── admin-auth.ts # Admin token 认证(/v1/admin/* 路径) │ ├── audit-log.ts # Admin 操作审计日志 │ └── request-logger.ts # 请求耗时日志 ├── services/ # 业务逻辑(按领域分目录) │ ├── auth/ # jwt, guest, huawei-id-kit, phone │ ├── quiz/ # quiz-service(出题引擎 + 答题验证) │ ├── progress/ # progress, streak, xp, hearts │ ├── gamification/ # leaderboard, achievement │ ├── payment/ # huawei-iap, subscription-service │ └── admin/ # 7 个管理端 CRUD 服务 │ └── question, category, knowledge-card, skill-tree, │ user, stats, feedback ├── routes/ # 路由(薄层,调 service + Zod 校验) │ ├── health.ts, auth.ts, quiz.ts, progress.ts │ ├── gamification.ts, payment.ts │ └── admin/ # 管理端路由(duoqi-admin 调用) │ └── index, auth, questions, categories, knowledge-cards, │ skill-tree, users, stats, feedback └── __tests__/ # 测试(Vitest + DB mock) ├── setup.ts, smoke.test.ts ├── helpers/db-mock.ts └── services/ # 单元测试(auth, quiz, progress) content/ # 种子数据(JSON) ├── categories.json, skill-tree.json, achievements.json ├── history.json, drama.json, crosstalk.json db/seeds/index.ts # 幂等种子导入脚本 ``` ## 编码约定 - **路由只做参数提取和响应格式化**,业务逻辑在 `services/` - **响应格式统一**:`{ success: boolean, data: T | null, error: { code, message } | null }` - **分页响应**:额外包含 `pagination: { total, page, limit }` - **不可变数据**:`Object.freeze()` 或返回新对象,不修改入参 - **错误处理**:抛出 `AppError` 子类(`NotFoundError`, `ValidationError` 等),由 `errorHandler` 统一捕获 - **环境变量**:所有配置通过 `src/utils/config.ts` 读取(Zod 校验),禁止直接读 `process.env` - **导入后缀**:ESM 项目,本地导入必须带 `.js` 后缀(`import { x } from './foo.js'`) ## API 约定 - Base URL: `/v1` - 认证:`Authorization: Bearer `(公开端点:`/v1/auth/*`, `/v1/health`) - Admin 认证:`Authorization: Bearer `(`/v1/admin/*`) - JWT 有效期:access_token 1h, refresh_token 30d ## 其他约定 Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed. **Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. ### 1. Think Before Coding **Don't assume. Don't hide confusion. Surface tradeoffs.** Before implementing: - State your assumptions explicitly. If uncertain, ask. - If multiple interpretations exist, present them - don't pick silently. - If a simpler approach exists, say so. Push back when warranted. - If something is unclear, stop. Name what's confusing. Ask. ### 2. Simplicity First **Minimum code that solves the problem. Nothing speculative.** - No features beyond what was asked. - No abstractions for single-use code. - No "flexibility" or "configurability" that wasn't requested. - No error handling for impossible scenarios. - If you write 200 lines and it could be 50, rewrite it. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. ### 3. Surgical Changes **Touch only what you must. Clean up only your own mess.** When editing existing code: - Don't "improve" adjacent code, comments, or formatting. - Don't refactor things that aren't broken. - Match existing style, even if you'd do it differently. - If you notice unrelated dead code, mention it - don't delete it. When your changes create orphans: - Remove imports/variables/functions that YOUR changes made unused. - Don't remove pre-existing dead code unless asked. The test: Every changed line should trace directly to the user's request. ### 4. Goal-Driven Execution **Define success criteria. Loop until verified.** Transform tasks into verifiable goals: - "Add validation" → "Write tests for invalid inputs, then make them pass" - "Fix the bug" → "Write a test that reproduces it, then make it pass" - "Refactor X" → "Ensure tests pass before and after" For multi-step tasks, state a brief plan: ``` 1. [Step] → verify: [check] 2. [Step] → verify: [check] 3. [Step] → verify: [check] ``` Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.