jwb-api-review · v26.11.0 · 2026-11-01 · sha256 6d1fd2e552e63c4e

jwb-api-review v26.11.0A

Immutable. This exact content never changes and is served at /api/v1/blob/6d1fd2e552e63c4e.

---
name: jwb-api-review
description: Use when 审查或新增求职工作台后端 API 时,需要按本仓库的错误码契约、状态码语义、两段式写入与安全边界逐条核对。English triggers:API review, REST design, error codes, AIP-193, Zalando API guidelines, two-phase write.
compatibility: 面向「开发 / 扩展本仓库后端」的场景;仓库在侧(jobws 指仓库内的 python tools/jobws.py);Python 3.12+。
license: MIT
metadata:
  version: 26.11.0
---

# 后端 API 审查清单

外部参考:Google AIP-193(错误)、AIP-158(分页)、Zalando RESTful API Guidelines。
本文件是本仓库的**落地口径**——与既有代码 / 测试冲突时以测试为准。

## 错误:加法而非替换

- 每个错误必须同时带 `detail`(人读中文原文)与 `error_code`(稳定语义标识),
  用户可见的动态值进 `error_params`;
- 命名 `<域>.<语义>`,域与路由对齐(ws / app / status / approval / sys / …);
  **同一语义跨路由必须复用同一个 code**;
- 前端语言包按 code 出一份文案——改 code 等于破坏前端文案,要当契约对待;
- 新增 code 必须同步:两个语言包 + 自动校验(判定脚本会扫全部 ApiError 调用,
  校验 code ↔ 文案 ↔ 参数三者的对齐)。

## 状态码语义(本仓库口径)

| 码 | 用于 |
|---|---|
| 400 | 值层面拒绝:未知键、越界的目标、非法路径 |
| 404 | 记录 / 工作区不存在 |
| 409 | 冲突:预览后数据变了、乐观并发失败(重试前必须重新读取) |
| 422 | 语义校验失败(格式、枚举、终态不回退);令牌不可用 |
| 200 + ok:false | MCP 工具层的「可读拒绝」形态(宿主不当异常处理) |

## 写入:两段式与安全边界

- 写端点先预览(不落盘)→ 一次性令牌 → apply;「要写什么、写到哪里」在令牌里,
  **不在请求里**——apply 不接收路径 / 载荷参数;
- apply 的落盘段必须持资源锁(「读最新 → 重校验 → 写」整段互斥;锁在领域层的
  落盘函数里,不在 web 层单点加——CLI / MCP 走同一条路);
- 工作区参数只收相对名,`realpath` 后校验在允许根之内;越界拒绝而非警告;
- 路径经安全拼接;错误响应不得泄露未授权位置的信息。

## 形状与分页

- 资源名用复数、动作由 HTTP 方法承载;响应字段 camelCase;与 CSV 列名对齐的
  数据字段保持原样(中文列名不翻,展示层再映射);
- 列表响应:`items` + `total`;大集合设上限并提示「另有 N 条」(返回体不随
  数据量膨胀);
- 时间用 ISO 8601 字符串;「未提供」用 null,不用空字符串表达。

## 审查动作(逐条走)

1. 列出本 PR 的全部写端点:每个都有预览 + 令牌吗?落盘锁在哪一层?
2. 扫 ApiError 调用:code 是否与既有同义 code 重复定义?参数是否齐全?
3. 扫前端 `err.<code>`:新增 code 两侧语言包都补齐了吗?
4. 路径与工作区边界:有没有绕过既有校验的新入口?
5. 兼容面:默认行为是否不变?破坏性变更是否走了版本流程?

## 常见错误

| 错误 | 后果 | 正确做法 |
|---|---|---|
| 新错误只给 detail 不给 code | 英文界面露出中文原文 | 同步双语言包 |
| 同义错误用了两个 code | 前端文案漂移 | 复用同一个 code |
| 写端点直接落盘 | 用户失去确认权 | 两段式 + 令牌 |
| 锁只加在 web 层 | CLI / MCP 路径并发丢写 | 落盘函数自持锁 |
| 列表接口不设上限 | 大数据撑爆响应 | items 上限 + total |