# 审计功能模块接入度报告

- 日期：2026-05-08
- 范围：所有挂 `@Auditable()` 的后端控制器
- 数据源：
  - 静态扫描：`testing/scripts/audit-coverage-scan.ts`
  - 历史落盘：`platform_audit.audit_log` 表
  - 机制证据：`testing/backend/integration/audit/interceptor.integration.test.ts`

---

## 1. 接入总量

| 维度 | 数值 |
|---|---|
| 扫描控制器文件 | 96 |
| 含 `@Auditable()` 控制器 | 57 |
| `@Auditable()` 端点总数 | **284** |
| 涉及顶层模块 | 29 |

按动词分布：基本全部为 mutation（POST/PUT/PATCH/DELETE），**没有任何 GET 类 `@Auditable`** —— 与"读不审、写必审"的合规取向一致。

## 2. 模块矩阵（@Auditable 端点数）

详细矩阵见 `audit-coverage-2026-05-08T07-10-07-727Z.md`，下面是高亮：

| 高密集模块 | 端点数 |
|---|---|
| `engines/form` | 57 |
| `robot-manager` | 32 |
| `parts` + `parts/controllers` | 45 |
| `tickets` | 20 |
| `organization/users` | 17 |
| `organization/iam-governance` | 14 |
| `engines/approval` | 12 |

## 3. 真实流量落盘证据

`audit_log` 表当前 72 行。按 `module` 字段（= `controller.name.replace('Controller','')`）拆分：

**真实拦截器写入**（PascalCase，匹配控制器类名）：
| module | SUCCESS | FAILED | 备注 |
|---|---|---|---|
| Auth | 8 | 6 | 登录/密码相关，最常触发 |
| Users | 4 (UPDATE) | 1 (CREATE) | 真实使用 |
| Regions | 2 | 0 | 真实使用 |
| TicketAdmin | 1 | 0 | 真实使用 |

**Seed/Fixture 数据**（小写域名，非控制器类名）：`iam`、`approval`、`finance`、`hr`、`document`、`deploy`、`performance`、`sync`、`system` —— 这批是合规/告警用例的种子，不能算作"真实拦截器写入证据"。

> 可见：到目前 dev 数据上，拦截器**实证触发只有 4 个控制器**。剩余 92 个控制器虽然装饰器已声明，但 dev 流量从未走到。这不代表它们"未接入"——见下一节。

## 4. 机制层接入证据

`testing/backend/integration/audit/interceptor.integration.test.ts` 有 5 条用例（全部跑通），覆盖了拦截器机制全部关键路径：

| 用例 | 验证点 |
|---|---|
| AUDIT-INT-001 | `@Auditable` 端点调用后 `audit_log` 至少 +1 行（写库链路） |
| AUDIT-INT-002 | `region` 字段写入后小写归一（多租户字段） |
| AUDIT-INT-003 | 5W1H 字段全部非空，`currentHash` 非空（合规字段） |
| AUDIT-INT-004 | `currentHash` 全局唯一（哈希链） |
| AUDIT-INT-005 | 错误密码登录 `status=FAILED` 且 `errorMessage` 非空（异常路径） |

**机制传递性论证**：
- `AuditLogInterceptor` 通过 `APP_INTERCEPTOR` 在 `AppModule` 全局注册
- 每个请求走同一处 `intercept()` 实现，用 `Reflector.getAllAndOverride(AUDITABLE_KEY, [handler, controller])` 取元数据
- `@Auditable()` 仅做 `SetMetadata(AUDITABLE_KEY, true)`，所有调用点元数据等价
- 拦截器对 SUCCESS/FAILED 两路径都写库（`tap` + `catchError`）

→ **任意 1 个 `@Auditable` 端点的端到端集成测试通过**，即可推及全部 284 个端点的拦截器装载、元数据识别、写库链路、异常路径都正常。`/auth/login` 即承担这个代表点。

## 5. 真正的接入风险点

机制层覆盖 ≠ 业务层正确性。下面这些点无法靠机制测试兜底，需要在每个模块的集成测试中单独覆盖：

| 风险类别 | 说明 | 建议 |
|---|---|---|
| `module` 字段值 | 拦截器用类名做 module，如果同一业务多个 Controller（如 form-engine 8 个 controller），audit 报表里会被拆碎 | 用 module 装饰器或在每个 controller 显式声明业务模块名（待规范确认） |
| `entityType` / `entityId` 提取 | 当前从 URL 路径推断（`extractEntityType/Id`），动词路径或自定义路径会失败 | 检查每个控制器路径是否符合 RESTful 约定 |
| `@Sensitive` / `@Financial` 标记 | 静态扫描显示有部分控制器加了 `@Sensitive` 但很多敏感操作没加（密码、删除、批量） | 走一轮 sensitive/financial coverage review |
| 业务字段在 `oldValue` / `newValue` 落盘 | 拦截器只写 `newValue = sanitizeBody(req.body)`，没有 oldValue（资源加载前快照） | 需要时通过 service 层手动调 `auditService.log()`，不能完全依赖装饰器 |

## 6. 结论与建议

**接入面**：284 个 mutation 端点，覆盖 29 个模块，装饰器声明完整。

**机制有效性**：通过现有 5 条 interceptor 集成测试代表性证明，无需对每个端点重复实测。

**遗留疑问**：
1. 跨多 controller 的同一业务（form-engine 8 个 controller）在 audit 报表里 module 字段会被拆碎 —— 是否需要业务统一标识（建议看 `docs/modules/audit-system/01-prd.md` 是否有规约）
2. `@Sensitive` / `@Financial` 标记覆盖率应单独审一轮（密码、批量、删除应几乎全 sensitive）
3. dev 数据 72 行里只有 4 个真实控制器 → UAT/生产是否有更广覆盖证据，建议在生产 read-only 视图上跑一次同款 module 分布查询，验证生产真实流量的 audit 覆盖面

**不建议**：随机调 mutation 端点做"真实探测"。一是大概率被 ValidationPipe 拦下、即使跑通也是 4xx FAILED 路径，与 SUCCESS 链路并不等价；二是潜在副作用（删数据、触发通知、SAP 同步）超出测试值。

## 7. 复用资产

- 静态扫描脚本：[testing/scripts/audit-coverage-scan.ts](../../testing/scripts/audit-coverage-scan.ts)（独立可重跑）
- 详细端点清单：[testing/reports/audit-coverage-2026-05-08T07-10-07-727Z.md](audit-coverage-2026-05-08T07-10-07-727Z.md)
- 拦截器机制测试：[testing/backend/integration/audit/interceptor.integration.test.ts](../backend/integration/audit/interceptor.integration.test.ts)
