Files
danyang-match/docs/Phase-17-V11-Recovery-Completion-Report-2026-09-17.md
T

95 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 17 — 举报与风控 KEEP-VERIFY/EXTEND 复验完成报告
**日期**:2026-09-17
**范围**:举报与风控(risk / report / referral 模块 + 后台风险页)
**纪律**:KEEP-VERIFY/EXTEND。历史 ✅ 不可信;测试覆盖 ≠ 运行时路径覆盖;逐条核对运行时路径与生产约束;发现真实缺陷即修;补测试保真度;守卫必须解析真实 DDL;离线容器真实编译+执行;每阶段出报告后停止等确认。
---
## 一、结论
**两项 Gate 全部达成,复验关闭。**
| 项 | 结果 |
|---|---|
| 风险来源可追踪 | ✅ counseling / service_station / offline_activity 三来源均接入统一 `RiskEvent`,且已确认运行时真实调用(非定义未调用) |
| 正文最小可见 | ✅ 后台风险画像只带必要来源上下文,不暴露无关咨询正文(INV-006 同族约束延续) |
| 低权限人员不能扩大读取范围 | ✅ 禁止公开黑名单;敏感访问全部审计(AGENTS.md #8 敏感数据默认拒绝 + 查看需审计) |
**本轮修复真实缺陷 6 处(含 1 处必崩级测试盲区、1 处隐藏编译缺陷)**,新增集成测试 7 例。
- 全量回归:**488 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(约 4 分)
- 基线 481 → 488,**净 +7**(Phase 17 集成测试 7 方法)
- 负向验证:临时注释 `getReportDetail` 敏感访问审计 → `operatorRiskEndpointsRecordSensitiveAccessAudit` 精准变红,已还原
---
## 二、本轮最关键发现(必记)
### F1:风控/举报真实 DB 路径「零覆盖」盲区(必崩级)
`EntityMigrationParityTest` 的白名单 `TEST_SCHEMA_OPTIONAL_TABLES` 中列有 `reports` / `risk_actions` / `risk_events` 三张表,但 **H2 测试库 `schema-h2.sql` 中根本没有这三张表的定义** → 整条 `ReportService` / `RiskReferralService` 真实 DB 路径**零测试覆盖**;而 `ReportServiceTest` 全是 `assertTrue(true)` 假绿。这与 STATUS.md 反复警示的"测试库缺表 → 零覆盖 → 必崩"模式完全一致,是本轮 KEEP-VERIFY 的最高风险点。
**修复**:在 `schema-h2.sql` 补齐三表(对齐 V012 生产 DDL,去外键、保留 CHECK);将三表从 `TEST_SCHEMA_OPTIONAL_TABLES` 移除,加入 `REQUIRED_IN_TEST_SCHEMA`(带 Phase 17 注释);新建真实 DB 集成测试。
### F2:隐藏编译缺陷(javax → jakarta)
为补审计在 `ReportService` / `RiskReferralService` / `AuditLogService` 中引入 `HttpServletRequest` 取 ip/userAgent 时,误用 `javax.servlet.http`(Spring Boot 3.x 已迁移到 **Jakarta EE** `jakarta.servlet.http`)→ `test-compile` 直接失败。修复为 `jakarta.servlet`。**印证 Phase 15 铁律:每次复验结束前必须真实编译+执行,不能以文档声明替代实证。**
### F3:补表时的列缺失
`schema-h2.sql` 的 `risk_events` 表起初漏了 `updated_at` 列(实体 `RiskEvent extends BaseEntity` 含 `updatedAt`,MyBatis-Plus 注入该列)→ 全量回归 6 个 `BadSqlGrammar` 错误。修复补齐 `updated_at`(与 `reports` / `risk_actions` 一致)。
---
## 三、真实缺陷清单(对应 Gate)
| 编号 | 类别 | 缺陷 | 修复 | Gate |
|---|---|---|---|---|
| D1 | 测试保真度/必崩级盲区 | `reports`/`risk_events`/`risk_actions` 在 H2 缺失 → 风控/举报真实 DB 路径零覆盖;`ReportServiceTest` 假绿 | 补三表 + 移出白名单 + 入必需集合 + 真实集成测试 `ReportAndRiskPhase17IntegrationTest`(7 例) | 风险来源可追踪 |
| D2 | 审计缺失 | `manualFlagRisk`(人工风险标记)不写审计 | 注入 `AuditLogMapper`,写 `MANUAL_FLAG_RISK` | 低权限不扩大读取 |
| D3 | 敏感访问无审计 | 查看他人风险画像/举报详情/风险事件列表不记录审计(AGENTS.md #8) | 新增 `AuditLogService.recordSensitiveAccess`;`getReportDetail`/`getUserRiskProfile`/`RiskController.getRiskEvents` 三端点接线 | 低权限不扩大读取 |
| D4 | 审计完整性 | `ReportService.saveAuditLog` 未填 `ip`/`userAgent` | 增强补全(与 `AuthorizationAuditListener` 同模式取 `RequestContextHolder`) | 低权限不扩大读取 |
| D5 | 数据一致性 | `RiskEventMapper.findByUserId` 未过滤 `deleted_at`,与 `sumScoreByUserId`(已过滤)不一致 | 补 `deleted_at IS NULL` | 风险来源可追踪 |
| D6 | 编译/列缺失 | 见 F2、F3 | jakarta 修正 + `risk_events` 补 `updated_at` | — |
> 来源接入代码层(`detectCounselingRisk` / `detectAppointmentRisk` / `detectActivityRisk`)**在运行时已被 `CounselingService`/`ActivityService`/`ServiceAppointmentService` 真实调用**,常量齐全,不是"假绿",本轮核心在修复覆盖盲区与审计缺口,而非重写来源逻辑。
---
## 四、负向验证(守卫有牙)
- 临时注释 `ReportOperatorController.getReportDetail` 中的 `auditLogService.recordSensitiveAccess(...)` → 定向运行 `ReportAndRiskPhase17IntegrationTest` → **BUILD FAILURE**(`operatorRiskEndpointsRecordSensitiveAccessAudit` 断言 `before+3 == after` 失败,实际只 +2)。已还原,代码回到 488 全绿状态。
---
## 五、证据(实测,非声明)
- **全量回归**:`488 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS`(离线容器 `danyang-match-backend-test` `mvn -B -o test`,约 4 分)。
- **定向编译验证**:`mvn -B -o test-compile` 通过(jakarta 修正后)。
- **新增测试**:`backend/src/test/java/com/danyang/match/platform/risk/ReportAndRiskPhase17IntegrationTest.java`(7 例:submitReport 写 reports+abuse_report 风险事件、三来源接入统一 RiskEvent、处置封禁写 risk_actions(ban)、manualFlagRisk 审计、敏感访问审计、findByUserId 软删过滤、最小可见)。
---
## 六、改动文件清单
**主代码(src/main)**
- `backend/src/test/resources/schema-h2.sql` — 补齐 `reports` / `risk_events` / `risk_actions` 三表(对齐 V012,去外键、保留 CHECK;`risk_events` 含 `updated_at`)。
- `backend/src/main/java/com/danyang/match/platform/risk/mapper/RiskEventMapper.java` — `findByUserId` 补 `deleted_at IS NULL`。
- `backend/src/main/java/com/danyang/match/platform/risk/service/ReportService.java` — `saveAuditLog` 补全 `ip`/`userAgent`(jakarta 修正)。
- `backend/src/main/java/com/danyang/match/platform/risk/service/RiskReferralService.java` — 注入 `AuditLogMapper`,`manualFlagRisk` 写审计(jakarta 修正)。
- `backend/src/main/java/com/danyang/match/platform/audit/service/AuditLogService.java` — 新增 `recordSensitiveAccess`(jakarta 修正)。
- `backend/src/main/java/com/danyang/match/platform/risk/controller/ReportOperatorController.java` — `getReportDetail`/`getUserRiskProfile` 接线敏感访问审计。
- `backend/src/main/java/com/danyang/match/platform/risk/controller/RiskController.java` — `getRiskEvents` 接线敏感访问审计。
**测试(src/test)**
- `backend/src/test/java/com/danyang/match/platform/risk/ReportAndRiskPhase17IntegrationTest.java` — 新增 7 例。
- `backend/src/test/java/com/danyang/match/platform/deployment/EntityMigrationParityTest.java` — 三表移出白名单、入 `REQUIRED_IN_TEST_SCHEMA`(Phase 17 注释)。
**文档**
- `docs/STATUS.md`、`docs/JIRA_BACKLOG.md`、`docs/善缘丹阳 V1.1 客户需求基线增量开发与修复计划.md` 的 Phase 17 段。
- `docs/Phase-17-V11-Recovery-Completion-Report-2026-09-17.md`(本报告)。
- 技能 `danyang-platform-verification` 同步更新。
---
## 七、下一步
**Phase 18(申诉 — KEEP-VERIFY)未自动进入,等待用户确认后启动。** 是否继续?