Files
lingxi/docs/工程规范/04-API与路由规范.md
Stev_Wang a1e28daec5
ci / 质量门禁 (push) Canceled after 0s
docs(routing): 新增前端路由与目录规范并收敛计划路由
背景:
- 前端路由缺少统一规范:导航模块 key 与 URL 首段不一致,页面目录未镜像
  路由,04 规范中的历史路由示例与实际实现不符;
- 用户确认「平台」更名为「管理中心」,路由统一为 /<模块>/<功能>。

改动:
- 新增 11-前端路由与目录规范:模块与基础路径表、路径语法(复数 kebab-case、
  /new、:id、层级与 query 状态)、pages/<模块>/<功能>/ 目录映射、路由注册、
  旧链重定向、新增页面检查清单;
- 04 §8 的 API-27 改为模块化路由并链接 11;工程规范索引与 AGENTS §9 登记 11;
- 开发计划:平台更名管理中心,附录 C 页面清单收敛为模块化路由,§7.1 / §8.3
  同步,版本记录补「路由规范回写」;
- A1 实施报告路由说明更新为 /system/users(旧 /users* 重定向)。

影响面:
- 仅文档与规范,不改变功能范围、权限点与后端接口;后续批次按 11 执行。

回滚方式:
- 回滚本提交即恢复原附录 C 与 04 §8 描述;代码不受影响。

验证证据:
- pnpm format:check 通过;路由与代码行为见相邻的 web 重构提交。
2026-11-03 10:30:55 +08:00

8.6 KiB
Raw Permalink Blame History

04 · API 与路由规范

项 内容
文档版本 V1.0(2026-11-02)
依据 技术架构 §6(接口设计)、§3.2(模块划分)
适用范围 apps/api 全部 HTTP / SSE 接口、apps/web 全部前端路由
强制级别 强制

1 URL 与版本

  • API-01 服务端统一前缀 /api/v1;版本在 URL 中,不使用 header 版本。
  • API-02 资源名用复数 kebab-case:/brief-cards、/media-tasks、/industry-packs。
  • API-03 嵌套 ≤ 2 层:/clients/:clientId/scripts;更深的归属用 query 过滤。
  • API-04 动作优先用子资源表达(POST /scripts/:id/approve → 可接受),禁止在 URL 用动词拼接业务流(/doGenerate)。
  • API-05 破坏性变更(删字段 / 改语义 / 改状态码)必须升 /api/v2;新增可选字段不升版本。
  • API-06 对外契约由 OpenAPI 生成,pnpm openapi:diff 检出破坏性变更即阻断(见 06)。

2 请求约定

项 约定
认证 Authorization: Bearer <access_token>;refresh 走独立端点
幂等 写接口支持 Idempotency-Key(24h 内同 key 返回首次结果);任务提交必带
内容类型 application/json; charset=utf-8;上传走预签名直传,不经 API 中转
时间 ISO 8601 带时区(2026-11-02T14:30:00+08:00),存储统一 UTC
ID ULID 字符串,26 字符;请求体里的 ID 一律字符串
字段命名 JSON body 用 camelCase;query 用 snake_case(cursor / limit / sort / next_cursor,与架构文档示例一致)
未知字段 严格模式拒绝(Zod .strict()),避免静默丢弃
  • API-07 所有入参用 Zod schema 校验,Controller 不写手写校验;动态 schema(要素卡)用 Ajv + 白名单。
  • API-08 分页:?cursor=<opaque>&limit=50,返回 next_cursor;limit 默认 50、上限 200。
  • API-09 排序:?sort=-created_at(- 为降序);仅允许白名单字段,非法字段 400。
  • API-10 过滤:显式白名单参数;禁止把任意 query 透传给 Prisma。
  • API-11 列表禁止返回大字段(JSONB 富文本、base64、完整分镜);产物只返回签名 URL。

3 响应约定

成功

  • 单个资源:直接返回对象(不套 data 信封)。
  • 列表:{ "items": [...], "next_cursor": "..." };无下一页时 next_cursor 为 null。
  • 创建:201 + 资源;异步任务提交:202 + { taskId, estimatedItems, estimatedCost, estimatedSeconds }。
  • 删除:204 无 body。

失败(统一结构,禁止其他形状)

{ "code": 40302, "message": "无权访问该客户数据", "details": {}, "trace_id": "01JA..." }
  • API-12 message 面向用户、中文、不含内部实现细节;details 放字段级错误;trace_id 必填。
  • API-13 禁止把堆栈、SQL、密钥、供应商原始报文写进响应。

4 错误码表

段 含义 常用码
400xx 参数校验 40001 参数缺失 / 格式错误
401xx 认证 40101 未登录、40102 token 过期、40103 签名失败
403xx 授权 40301 无权限点、40302 数据范围外、40303 缺特殊权限、40304 违反职责互斥
404xx 不存在 40401 资源不存在 / 已删除
409xx 冲突 40901 版本冲突、40902 重复提交、40903 状态不允许
422xx 业务规则 42201 业务校验失败、42202 合规拦截(禁忌词 / 极限词)、42203 成本超预算
429xx 限流 42901 请求过频、42902 并发任务超限
500xx 系统 50001 内部错误、50002 依赖不可用、50003 超时
  • API-14 新增错误码必须集中登记在 packages/shared 的错误码常量,禁止在业务代码里写裸数字。
  • API-15 授权类错误一律 403 且不区分「不存在」与「无权限」的原因(避免探测他人数据;日志里保留真实原因)。

5 鉴权与权限校验链

顺序固定,不得跳步:

JwtGuard → PermissionGuard(权限点)→ SpecialPermissionGuard(特殊权限)
        → ScopeGuard(数据范围)→ 业务校验 → Repository(ScopeContext) → 审计埋点
  • API-16 权限点由代码注册(常量),角色是权限点组合(数据化),见技术架构 D-07。
  • API-17 特殊权限(导出 / AI 配置 / 审计查看 / 行业包发布 / 跨客户复用授权)必须单独授予,不得打包进普通角色。
  • API-18 数据范围解析失败一律 403,禁止降级为「全量可见」。
  • API-19 /public/review/*、/public/share/* 是唯一免 JWT 路由组,必须:HMAC 签名 + 有效期 + 范围(只读 / 可改稿)+ 限流 + 访问留痕;不得在其中暴露任何其他资源。
  • API-20 每个端点必须有 3 类权限测试用例(有权限 / 无权限 / 范围外),见 06。

6 异步任务协议(所有耗时操作统一)

步骤 约定
提交 POST /xxx-tasks,带 Idempotency-Key,返回 202 + { taskId, estimatedItems, estimatedCost, estimatedSeconds }
订阅 GET /xxx-tasks/{id}/events(SSE),事件 item / progress / done / error,支持 Last-Event-ID 续推
兜底 GET /xxx-tasks/{id} 轮询(前端 SSE 失败 2 次后自动降级)
取消 POST /xxx-tasks/{id}/cancel;仅未开始项取消,已开始项等待结束
重试 POST /xxx-tasks/{id}/retry;只重跑失败项,成功项不得重算成本
产物 一律签名 URL(5–15 分钟),随 item / done 事件下发
  • API-21 长耗时操作禁止放在同步请求里(> 2s 一律异步)。
  • API-22 estimatedCost 必填;为 0 也要返回(前端展示"预估成本"是 PRD R-06 硬要求)。
  • API-23 任务终态写入后事件必须可重放(存事件表或 Redis Stream),断线可恢复。

7 上传与下载

场景 约定
上传 预签名 PUT 直传对象存储;前端先算 sha256 走秒传;> 100 MB 用分片(listMultipart)
校验 上传完成后服务端 headObject 校验大小 / 类型;不一致则任务失败并清理
下载 302 到签名 URL(5–15 分钟),支持 Range(视频预览断点续播)
导出包 服务端打包 → exports 桶 → 签名 URL,产物保留 3 天
限制 单文件上限读环境变量 MAX_UPLOAD_MB,禁止代码里写死
  • API-24 禁止 API 进程中转文件流(内存与带宽不可控)。
  • API-25 签名 URL 不得记录到日志(含 query 签名参数)。

8 前端路由

前端路由的模块表、路径语法与目录映射以 11 为准;本节保留通用约束。

  • API-26 路由常量集中 apps/web/src/routes.ts,禁止散落字符串路径。
  • API-27 前端路由统一为 /<模块>/<功能>:/system/users、/scripts/library;模块表、目录映射与旧路径重定向见 11 · 前端路由与目录规范。
  • API-28 统一布局路由:登录后进入 AppLayout(侧边导航 + 面包屑),未登录访问自动跳 /login 并带回跳地址。
  • API-29 权限守卫组件 <RequirePermission code="SCRIPT_APPROVE">;无权限渲染 403 页,不隐藏路由(避免"菜单有、点进去白屏")。
  • API-30 页面级懒加载(React.lazy + Suspense),首屏包体积纳入性能预算。
  • API-31 URL 是唯一状态源:筛选 / 分页 / 选中态可分享的必须进 query(useSearchParams),禁止只存 Zustand。
  • API-32 404 / 403 / 500 三个页面必须存在,且 500 页展示 trace_id 供排查。

9 契约与兼容

  • API-33 OpenAPI 由 NestJS 装饰器生成,提交到仓库,CI 用 oasdiff 检测破坏性变更。
  • API-34 新增字段必须可选且前端有默认值;删除字段先标记 deprecated 一个版本。
  • API-35 前端不得依赖未在 OpenAPI 声明的字段。

10 检查清单

  • URL 复数 kebab-case,版本 /api/v1,嵌套 ≤ 2 层
  • 入参 Zod 校验,未知字段拒绝
  • 列表走游标分页,不返回大字段
  • 错误体 { code, message, details, trace_id },错误码取自常量
  • 守卫链完整,特殊权限单独校验
  • 任务提交带 Idempotency-Key 与预估成本
  • 产物只给签名 URL,不打日志
  • 前端路由常量集中,权限守卫 + 三张错误页齐全
  • OpenAPI 已更新,openapi:diff 通过