# 契约对齐执行报告

> 范围：`REVIEW-REPORT.md` 中标注为 P0/P1 的文档级差异 + 决策点 1/2 落地
> 性质：**纯文档对齐**，未改任何 `.ts` / `.js` / `.prisma` / schema 文件
> 时间：2026-04-30

---

## 修改的文件清单

| 报告项 | 文件 | 改动摘要 |
|--------|------|----------|
| **B1 + 决策 1** | `docs/modules/form-management/07-api.md` | 删除"前端永远只调用 /form-management 和 /approval-center"过时声明；改为说明 v2.x 现状（审批中心前端直调 `/api/v1/approval/*`，不存在 `/approval-center/*` 后端聚合层）|
| **B1 + 决策 1** | `docs/modules/approval-center/07-api.md` | 顶部新增"实际调用前缀（v2.x 现状）"段，明确审批中心前端实际调用的是 `approval-engine` 路由，并交叉引用 `approval-engine/07-api.md` |
| **D1 / F1 + 决策 2** | `docs/modules/form-management/07-api.md` | 整段重写"多区域支持"章节：删除 `X-Region-Id` 强制头、`regionId` / `REGION_REQUIRED` 等过时契约；改为说明 v2.0 架构（"组织决定范围，快照管理版本，实例记录环境"）；交叉引用 `prisma/schema/FORM_ORGANIZATION_ARCHITECTURE.md` |
| **D1 / F1 + 决策 2** | `docs/modules/form-management/07-api.md` | 全文删除独立 `X-Region-Id: CN` 请求头行（11 处）；改写错误码表（删除 `REGION_REQUIRED` / `INVALID_REGION` / `REGION_MISMATCH` / `CROSS_REGION_FORBIDDEN`，新增 `CROSS_ORGANIZATION_FORBIDDEN`）；FormDefinition 列表/创建响应示例的 `regionId` → `organizationId` |
| **D1 / F1 + 决策 2** | `docs/modules/form-management/07-api.md` | FormWebhook 创建/响应示例的 `regionId` → `organizationId`（与 v2.0 架构一致）|
| **D1 / F1 + 决策 2** | `docs/modules/form-management/06-data-model.md` | "关系与约束"段加入 organizationId / regionId 字段所有权说明；顶部加入 v2.0 架构通告 + 交叉引用 |
| **F3 / D2** | `docs/modules/form-management/07-api.md` | FormDefinition.status 枚举从 `DRAFT/ACTIVE/DISABLED/ARCHIVED` 改为 `DRAFT/PUBLISHED/DISABLED/ARCHIVED`（实际 schema 是 `FormStatus` enum）；发布响应中 `formDefinitionStatus` 由 ACTIVE 改 PUBLISHED；"内部逻辑"段标注 ACTIVE 仅是 ReleaseSnapshotStatus 不是 FormDefinition.status |
| **F4 / C1** | `docs/modules/form-engine/07-api.md` | set-default 路径从 `/forms/:formIdentifier/versions/:version/set-default` 改为实际的 `/forms/:formIdentifier/versions/_actions/set-default`（version 从 body 传入）|
| **A1 / F2** | `docs/modules/approval-engine/README.md` | 接口总数 40 → **54**（实测 `backend/src/engines/approval/` 全部 controller 的 `@Get/@Post/@Put/@Patch/@Delete` 共 54 条；管理员接口组从 6 个修正为 20 个）|
| **A1 / F2** | `docs/modules/approval-engine/07-api.md` | 顶部接口数量 47 → **54** |
| **A2** | `docs/modules/approval-engine/04-state-machine.md` | **完全重写**：补任务状态机（10 个 ApprovalTaskStatus）+ 动作矩阵（24 个 ApprovalTaskAction）+ 退回/加签/委托/撤回对实例状态的影响 + 超时与失败恢复策略；删除尾部"待确认项"自检列表 |
| **可选 7** | `docs/modules/approval-center/06-data-model.md` | `FormInstance.approvalStatus` 字段说明加注"schema 上是 String 而非 enum"，列出合法值 |
| **可选 7** | `docs/modules/form-management/07-api.md` | 提交实例（8.5）"内部逻辑"段补 `approvalProcessKey` 为空时的行为说明（needsApproval 计算逻辑 + 跳过审批直接 SUBMITTED）|

**共 7 个文件被修改**，全部为 docs/ 下文档；零代码 / 零 schema 改动。

---

## 决策记录

### 决策 1（approval-center 聚合层）：选 (b) 文档收编现状

- **不创建** `/api/v1/approval-center/*` controller
- 文档明确：审批中心是**前端模块名**，后端实现复用 `approval-engine` 全部路由
- 前端"我发起/待办/已办"调 `/api/v1/approval/my/*`、"管理员数据中心"调 `/api/v1/approval/admin/*`
- 落地位置：
  - `docs/modules/form-management/07-api.md` 顶部"前端调用边界"段
  - `docs/modules/approval-center/07-api.md` 顶部"实际调用前缀"段

### 决策 2（FormDefinition.regionId）：选 (b) 文档收编现状

- **不给 schema 加 regionId 列**；事实源是 `prisma/schema/FORM_ORGANIZATION_ARCHITECTURE.md`（v2.0 架构清理决策）
- 区域可见性：通过 `Organization → OrganizationRegion → Region` 多对多间接表达
- 文档去除：
  - `X-Region-Id` 必填头声明
  - `REGION_REQUIRED` / `INVALID_REGION` / `REGION_MISMATCH` 错误码
  - "同一个 formDefinitionId 在不同区域可以有不同的 ACTIVE 快照"等矛盾描述
- 落地位置：
  - `docs/modules/form-management/07-api.md` "🌐 多区域支持（v2.0 架构）" 段
  - `docs/modules/form-management/06-data-model.md` "关系与约束"段
  - 两处都加交叉引用指向 `FORM_ORGANIZATION_ARCHITECTURE.md`

---

## 调研中纠正了 REVIEW-REPORT 的两处误判

### 1. F5 / C2「创建版本接口疑似缺失」→ 实际**存在**

- 报告原文："`form-versions.controller.ts` **无对应 POST 根路径**"
- 实测：`backend/src/engines/form/form-engine/controllers/form-versions.controller.ts:48` 有 `@Post()` 装饰器，对应 `POST /forms/:formIdentifier/versions`
- 文档侧（`form-engine/07-api.md:61`）原文已正确列出该接口，**无需任何改动**
- 误判原因：调研 fork 没扫到正确的 controller 路径（实际在 `controllers/` 子目录下）

### 2. F2 / A1 报告中说"实测 ~52 条"→ 实际是 **54 条**

- 报告把 `approval.controller.ts` + `process-admin.controller.ts` 数错成 52
- 实测：approval.controller.ts 49 条 + process-admin.controller.ts 5 条 = **54 条**
- 文档已按 54 更新

---

## 跳过项 + 原因

| 项 | 跳过原因 |
|----|----------|
| **A3 (`X-Idempotency-Key` 未做去重)** | 现状描述已经在 `approval-engine/07-api.md:40-44` 用括号注明"当前仅透传到 DTO，未做统一去重处理"；属于代码侧 P1 工作（M4），文档侧无契约 drift |
| **A4 (ApprovalMode 单人/或签映射)** | 需要用户决策枚举设计（拆 SINGLE 还是文档锁口径），不在本批次"决策已拍板的文档对齐"范围 |
| **A5 (ADMIN targetUser 持久化)** | 这是后端代码 P2 改动；文档侧 `approval-center/07-api.md:331-334` 已有自承说明，保持现状 |
| **A6 (前端 COMPLETED 残留)** | 这是前端 grep + 改 i18n 的代码工作，不是文档对齐 |
| **审计：ReleaseSnapshot.status enum** | `ACTIVE` 是合法的 `ReleaseSnapshotStatus` 枚举值，文档中保留正确无需改 |

---

## 后续待办（不在本批次）

- **批次 B（P0 撤回 bug）** — 已并行进行
- **批次 C（功能补齐第一波 M5/M6/M7/M9）** — 等本批次 + B 完成后启动
- **A3 代码侧实现** — `approval-engine` 启动流程时落幂等去重表（Redis 或 DB unique key）
- **A4 用户决策** — `ApprovalMode` 是否拆 `SINGLE`，需用户拍板
- **A5/A6 代码侧** — 后端写 ADMIN_APPROVE/REJECT 的 targetUser；前端 grep `COMPLETED`
- **可选：把 v2.0 架构通告同步进 `docs/standards/04-database-architecture.md`**，让 schema 目录的 .md 不再"孤立"（参考 `.learnings/2026-04-30-schema-side-decision-docs-as-truth-source.md`）

---

## 验证建议

本批次纯文档改动，建议下一步用以下方式验证：

1. `grep -r "approval-center/[a-z]" docs/modules/` 应该没有把 approval-center 当作 API 前缀的残留
2. `grep -rn "X-Region-Id\|REGION_REQUIRED" docs/modules/form-management/` 应该只剩兼容性说明，不再作为强制契约出现
3. `grep -n '"status": "ACTIVE"' docs/modules/form-management/07-api.md` 检查剩余的 ACTIVE 是否都是 ReleaseSnapshot 上下文（合法）
4. 跑 `testing/scripts/contract-check.ts`（如存在）看 L0a/L0b 真实差异
