移除 fusion-auth-client、融合认证路由和阿里云 SDK 依赖, 同时保留 findOrCreatePhone、appSettings 表、auth-providers 管理端和 /auth/providers 端点等基础设施。
7.5 KiB
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
bun install # 安装依赖
cp .env.example .env # 复制环境变量模板,填入实际值
bun run dev # 启动开发服务器,默认端口 3000
必填环境变量:DATABASE_URL, JWT_SECRET, ADMIN_TOKEN
开发命令
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 # 全部 16 张表定义(唯一真相源)
├── 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 <jwt>(公开端点:/v1/auth/*,/v1/health) - Admin 认证:
Authorization: Bearer <admin_token>(/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.
Agent skills
Issue tracker
Issues live in Gitea (git.duoqi.me). Uses the tea CLI. See docs/agents/issue-tracker.md.
Triage labels
Five canonical labels: needs-triage, needs-info, ready-for-agent, ready-for-human, wontfix. See docs/agents/triage-labels.md.
Domain docs
Single-context repo: one CONTEXT.md + docs/adr/ at the repo root. See docs/agents/domain.md.