# 任务 03 检查记录:接口层(路由、校验、错误处理、OpenAPI) 日期:2026-08-31 ## 交付内容 - `server/src/schemas/entities.ts`:全部领域实体的 Zod Schema(题目、要闻、计划、模考、会话、错题等)。 - `server/src/schemas/api.ts`:34 个接口的请求/响应 Zod Schema(路由共 31 个路径,34 个操作)。 - `server/src/errors.ts`:`ApiError` 业务异常 + 统一错误响应格式 `{ error: { code, message, details } }`;AJV 校验信息聚合翻译为中文(如"缺少必填字段 module"、"取值不符合预设选项")。 - `server/src/handlers/`:dashboard、practice、review、plans、mock、news、questions、ai、profile 九个域的处理器。 - `server/src/routes.ts`:路由集中注册,Zod → JSON Schema(`z.toJSONSchema`,draft-7)转换,含 tags/summary/response。 - `server/src/server.ts`:`buildServer` 拆分(可测试/可导出),注册 `@fastify/swagger` + `@fastify/swagger-ui`,并挂载运行时端点 `GET /api/openapi.json`(返回 `app.swagger()`)。 ## 命令检查 - `pnpm --filter @gwy/server typecheck`:通过。 - `GET /api/openapi.json`:200,OpenAPI 3.0.3,登记接口 31 个、文档路径 31 个,全部路由含请求/响应 Schema。 - 服务启动:无 FSTWRN001 等 Schema 警告(grep 计数为 0)。 ## 接口实测(curl) | 接口 | 结果 | |---|---| | `GET /health` | 200,`{status:"ok",routes:31}` | | `GET /api/openapi.json` | 200,约 81 KB | | `GET /api/docs`(Swagger UI) | 200 | | `GET /api/dashboard/overview` | 200,学习天数/正确率/待复习等字段齐全 | | `GET /api/dashboard/trends?days=7` | 200,返回 7 天趋势数组 | | `GET /api/practice/modules` | 200,五大模块题量 | | `POST /api/practice/start`(正确参数) | 200,返回 sessionId 并持久化到 `data/practice-sessions.json` | | `GET /api/practice/{sessionId}/question` | 200,不含答案与解析 | | `POST /api/practice/{sessionId}/answer` | 200,`{accepted:true,answeredCount:0}`(记录落库留给任务08) | | `POST /api/practice/{sessionId}/finish`(无请求体) | 200,返回结果骨架(真实计算留给任务08) | | `GET /api/questions/list?module=言语理解` | 200,分页结构正确 | | `GET /api/questions/export` | 200,导出 JSON | | `GET /api/news/list`、`GET /api/news/{id}` | 200 | | `GET /api/review/list`、`GET /api/plans/today`、`GET /api/plans/week` | 200 | | `GET /api/mock-exams/analysis` | 200,模块均分数组齐全 | | `GET /api/profile`、`GET /api/settings` | 200 | | `POST /api/practice/start` 缺字段 | 400 `VALIDATION_ERROR`,details 中文提示"缺少必填字段 module" | | `POST /api/practice/start` duration=7 | 400,聚合为一条"取值不符合预设选项" | | `GET /api/questions/list?module=xingce` | 400,"取值必须是允许的枚举值之一" | | 未注册路径 | 404 `NOT_FOUND`,`接口不存在:GET ...` | ## 过程中发现并修复的问题 1. `response` 对象中存在 `400: undefined` 键导致 Fastify 遍历报错 → 改为过滤后仅保留已定义状态码。 2. 无 body 的 POST 路由(如 finish)曾被占位空对象 Schema 误拒空请求体 → 无 body Schema 时直接省略 `body` 键。 3. Zod union 字面量(duration 5|10|15)在 AJV anyOf 校验下报 4 条冗余英文错误 → 错误处理器按路径聚合并翻译为单条中文提示。 4. `practice/start` 未持久化会话导致后续接口 404 → 补充 `updateData` 写入会话(组卷题量、作答记录、结果计算仍为任务08 范围)。 ## 浏览器检查 任务 03 仅涉及后端接口层,未新增前端页面。Swagger UI(`http://127.0.0.1:3000/api/docs`)HTTP 200 可访问,未做前端回归(前端对接在任务04+)。 ## 当前结论 接口层完成:31 个路径全部注册并带完整 Schema;统一错误格式(400/404/500 均验证);OpenAPI 文档由运行时端点 `GET /api/openapi.json` 提供(不再导出静态文件);核心业务链路(start → question → answer → finish)跑通且会话已持久化。剩余 TODO 已标注在 handlers 内,归属任务08(刷题业务规则)。 > 微调(2026-08-31):改为「不落盘、运行时提供」——删除 `server/scripts/export-openapi.ts`、`server/openapi.json` 与 `openapi:export` 脚本,前端直接消费 `http://localhost:3000/api/openapi.json`。