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。
失败(统一结构,禁止其他形状)
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 鉴权与权限校验链
顺序固定,不得跳步:
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 前端路由
API-26 路由常量集中 apps/web/src/routes.ts,禁止散落字符串路径。
API-27 路径反映「行业 → 客户」归属:/clients/:clientId/scripts、/clients/:clientId/media/batches/:batchId。
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 检查清单