fix(relationship): Phase 12 复验 —— 修复实体↔生产迁移系统性漂移(生产故障族)/ 双路径 episode 与 NPE / 测试库缺表致首页零覆盖 / 计划项 2 未实现
KEEP-VERIFY 复验发现并修复 4 处真实缺陷: - D1(Critical,生产故障·同一族系统性缺陷)实体与 db/migration 系统性漂移:23 表 50 列在生产 迁移中不存在,而测试库 schema-h2.sql 手写补齐 → 全量绿=假绿。其中 11 表属"INSERT 必崩"级 (缺 id/created_at/updated_at,被 AutoFillHandler 强制填充),含 matches.episode、user_preferences 权重列、permissions/roles、consent_records、risk_events 等;真实 PG 上 Match 创建、择偶偏好保存、 权限/角色写入、同意记录、风险事件全部失败。修复:新增 V037(幂等 ADD COLUMN IF NOT EXISTS + 存量 episode 回填)。 - D2(High)RelationshipService.createMatch 不写 episode/lastActivityAt,与 MatchService 双路径 填充不一致;且 episode 为 null 时 m.getEpisode()+1 对 Integer 自动拆箱抛 NPE。修复:统一按 AGENTS.md #20 推导 + null 兜底。 - D3(High)测试库缺 user_identities 等 7 表 → GET /api/v1/home 在测试环境 BadSqlGrammar, 首页接口零回归覆盖。修复:schema-h2.sql 补齐 7 表。 - D4(Medium)Phase 12 计划项 2 实际未实现:getting_to_know 分支只产出 counseling。修复: HomeApplicationService 补 premarital 引导(与 counseling 并列)。 测试保真度: - 新增永久守卫 EntityMigrationParityTest(5 用例):把"实体字段 ⊆ 生产迁移累积列"与"测试库覆盖 关键业务表"固化为自动不变量,区分"必崩级/潜伏级";经两轮负向验证证明会变红(第一轮分级过窄 已修正:episode 由"潜伏级"纠正为"必崩级")。 - 新增 RelationshipProgressPhase12Test(14 用例,含 8 线程并发确认)与 HomePremaritalGuidancePhase12Test(5 用例)。 - 删除 2 个假测试类中的 assertTrue(true) 占位(AGENTS.md 明令禁止),改走真实服务方法验证。 Gate:单方确认不改变最终关系 ✅ / 双确认后才 successful ✅ / 历史 Episode 可追踪 ✅ / 新 Episode 不恢复旧聊天权限(conversations.match_id UNIQUE)✅ / 非成员不可代确认 ✅ 全量回归:371 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS(71 类,5m07s)
This commit is contained in:
+18
-1
@@ -5,6 +5,22 @@
|
||||
|
||||
> **2026-09-16 状态校正:** Phase 07 UI 已按 2026-09-13 规格重新实现,**首批 11 页于 2026-09-16 通过用户验收(结论:基本达标)**,本文件此前"视觉否决/REOPENED"口径随之关闭。
|
||||
>
|
||||
> **2026-09-16 Phase 12 复验关闭:** EPIC-P12-V11 已完成。复验中发现并修复 4 处缺陷——
|
||||
> **D1(Critical,生产故障 · 同一族系统性缺陷)** 实体与生产迁移**系统性漂移**:23 张表、50 个列在 `db/migration` 中
|
||||
> 根本不存在,而测试库 `schema-h2.sql` 手写了它们 → 全量测试绿 = **假绿**。其中 11 张表属"INSERT 必崩"级
|
||||
> (缺 `id`/`created_at`/`updated_at`,被 `AutoFillHandler` 强制填充),含 `matches`(缺 `episode`/`source_id`/
|
||||
> `last_activity_at`/`unmatched_by`/`deleted_at`)、`user_preferences`、`permissions`、`roles`、`consent_records`、
|
||||
> `risk_events` 等;**真实 PG 上 Match 创建、择偶偏好保存、权限/角色写入、同意记录、风险事件全部失败**。
|
||||
> 修复:新增 `V037__align_entity_columns_with_migrations.sql`(幂等 `ADD COLUMN IF NOT EXISTS` + 存量 `episode` 回填)。
|
||||
> **D2(High)** `RelationshipService.createMatch` 不写 `episode`/`lastActivityAt`,与 `MatchService` 双路径填充不一致;
|
||||
> 且 episode 为 null 时 `m.getEpisode() + 1` 对 `Integer` **自动拆箱抛 NPE**。修复:两路径统一按 AGENTS.md #20 推导 + null 兜底。
|
||||
> **D3(High)** 测试库缺 `user_identities` 等 7 张表 → `GET /api/v1/home` 在测试环境 `BadSqlGrammar`,**首页接口零回归覆盖**。
|
||||
> **D4(Medium)** Phase 12 计划项 2 实际**未实现**:`getting_to_know` 分支只产出 counseling。修复:补 premarital 引导。
|
||||
> 新增永久守卫 `EntityMigrationParityTest`(5 用例,区分"必崩级/潜伏级",经**两轮负向验证**证明会变红);
|
||||
> **删除 2 个假测试类**(`RelationshipStatusServiceTest`/`RelationshipStatusServiceDoubleConfirmTest` 的 `assertTrue(true)` 占位)。
|
||||
> 后端全量 **371 tests / 0 failures / 0 errors**。
|
||||
> 证据:`docs/Phase-12-V11-Recovery-Completion-Report-2026-09-16.md`。**Phase 13 需用户确认后启动。**
|
||||
>
|
||||
> **2026-09-16 Phase 11 复验关闭:** EPIC-P11-V11 已完成。复验中发现并修复 3 处缺陷——
|
||||
> **D3(Critical,生产故障)** `NotificationService` 全部 10 种通知使用复合 type(`appointment_submitted` /
|
||||
> `counseling_assigned` / `le_verified` 等),违反生产 `chk_notification_type`(仅允许短名),
|
||||
@@ -43,7 +59,8 @@
|
||||
| Phase 08 Like / Match / Block 复验 | 已完成(2026-09-16) | 见 `Phase-08-V11-Recovery-Completion-Report-2026-09-16.md`;修复 3 处真实缺陷(搜索绕过 Block、详情无 Block 检查、`matches.source` 取值越界),后端 308 tests 全绿 |
|
||||
| Phase 09 Text Chat 复验 | 已完成(2026-09-16) | 见 `Phase-09-V11-Recovery-Completion-Report-2026-09-16.md`;修复 4 处真实缺陷(WebSocket 下行死代码、Unmatch 后仍可发消息、Block 未落库 Match、聊天接口 IDOR),后端 328 tests 全绿;Phase 10 尚未获准 |
|
||||
| Phase 10 Image & Chat Safety 复验 | 已完成(2026-09-16) | 见 `Phase-10-V11-Recovery-Completion-Report-2026-09-16.md`;修复 2 处缺陷(图片上传魔数校验死代码致本地资格佐证上传 100% 失败、高风险词写入日志),推翻旧报告 2 项安全声明,后端 334 tests 全绿;Phase 11 尚未获准 |
|
||||
| Phase 11 Notification 复验 | 已完成(2026-09-16) | 见 `Phase-11-V11-Recovery-Completion-Report-2026-09-16.md`;修复 3 处缺陷(10 种通知 type 违反生产 CHECK 约束致全部落库失败、幂等无 DB 兜底、婚前辅导通知未接线),后端 346 tests 全绿;Phase 12 尚未获准 |
|
||||
| Phase 11 Notification 复验 | 已完成(2026-09-16) | 见 `Phase-11-V11-Recovery-Completion-Report-2026-09-16.md`;修复 3 处缺陷(10 种通知 type 违反生产 CHECK 约束致全部落库失败、幂等无 DB 兜底、婚前辅导通知未接线),后端 346 tests 全绿 |
|
||||
| Phase 12 Relationship Progress 复验 | 已完成(2026-09-16) | 见 `Phase-12-V11-Recovery-Completion-Report-2026-09-16.md`;修复 4 处缺陷(**实体↔生产迁移系统性漂移 23 表 50 列**、双路径 episode/NPE、测试库缺 7 表致首页零覆盖、计划项 2 未实现),新增永久守卫 `EntityMigrationParityTest`,删除 2 个假测试类,后端 371 tests 全绿;Phase 13 尚未获准 |
|
||||
| UI-CLEANUP-20260913 | 已完成 | 废弃 UI 代码、冲突规范、旧计划和无效验收证据已清理;`ui-document-baseline-contracts` 锁定唯一视觉入口 |
|
||||
| UI-ADMIN-20260913 | 已完成(待桌面端目视复验) | 管理端全局框架与工作台已按规格交付;18 modules + 22 routes,vue-tsc 与 vite build 全绿 |
|
||||
| UI-MINIAPP-20260913 | 已完成 | 小程序 11 个核心页面已重构;11/11 页 TS-only,2026-09-16 通过用户验收 |
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
# Phase 12 V1.1 Recovery Completion Report — Relationship Progress
|
||||
|
||||
> 日期:2026-09-16
|
||||
> 阶段:Phase 12(Relationship Progress — KEEP-VERIFY/EXTEND)
|
||||
> 分支:`main` | Tag:`phase12-recovery-2026-09-16`
|
||||
> 结论:**CLOSED(Gate 全项通过)**,累计全量回归 **371 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**
|
||||
|
||||
---
|
||||
|
||||
## 一、计划目标与 Gate
|
||||
|
||||
计划出处:`docs/善缘丹阳 V1.1 客户需求基线增量开发与修复计划.md` 第 238-246 行。
|
||||
|
||||
| 计划项 | 要求 | 结论 |
|
||||
|---|---|---|
|
||||
| 计划项 1 | 验证成功关系双确认、恢复/退出、历史 Episode 和并发确认 | ✅ 通过(14 用例行为验证) |
|
||||
| 计划项 2 | 补充 `getting_to_know` 阶段的婚前辅导入口引导 | ✅ **原未实现,本轮补实现** |
|
||||
| 计划项 3 | 验证工作人员不能替用户确认成功关系(INV-005) | ✅ 通过(非成员路径断言) |
|
||||
|
||||
**Gate:** 单方确认不改变最终关系;历史 Episode 可追踪但不会恢复旧聊天权限。
|
||||
|
||||
| Gate 断言 | 证据 | 结果 |
|
||||
|---|---|---|
|
||||
| 单方确认不改变最终关系 | `singleConfirmDoesNotCompleteMatch`:A 单方确认后 Match 仍非 successful、success request 仍 pending | ✅ |
|
||||
| 双确认后才 successful | `bothConfirmCompletesMatch`:A 确认 → B 确认 → Match = successful | ✅ |
|
||||
| 历史 Episode 可追踪 | `reMatchIncrementsEpisode`:先 unmatch 后重建,episode 1 → 2 | ✅ |
|
||||
| 新 Episode 不恢复旧聊天权限 | `reMatchDoesNotRestoreOldConversation`:新 Match 零会话;旧会话 `match_id` 仍指向旧 Match(`conversations.match_id` UNIQUE 约束) | ✅ |
|
||||
| 工作人员不能代确认 | `nonMemberCannotConfirm`:非成员 userId 确认被业务异常拒绝 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 二、复验方法论(KEEP-VERIFY / EXTEND)
|
||||
|
||||
沿用前四阶段已确立的复验纪律:**历史 ✅ 不可信**,「测试覆盖了函数」≠「测试覆盖了运行时路径」。逐条核对**运行时路径**与**生产约束**,发现真实缺陷则修复,并补齐测试保真度。
|
||||
|
||||
本轮新增第三层保真度要求并固化为守卫:
|
||||
|
||||
1. **生产 DDL 约束必须下沉测试库**(Phase 08~11 已建立);
|
||||
2. **测试必须走真实方法路径**,不得传字面量绕过服务层;
|
||||
3. **测试库 schema 必须与生产迁移对齐** ← **本轮新发现的最大盲区(见 D1)**。
|
||||
|
||||
---
|
||||
|
||||
## 三、缺陷清单(D1~D4)
|
||||
|
||||
### D1 — Critical(生产故障 · 同一族系统性缺陷):实体与生产迁移系统性漂移
|
||||
|
||||
**现象:** 23 张表、50 个列在 `backend/src/main/resources/db/migration/V*.sql` 中根本不存在,但实体(`@TableName`)声明了这些字段;同时测试库 `backend/src/test/resources/schema-h2.sql` 是**手写**最小 schema,长期比生产迁移**多出**这些列。
|
||||
|
||||
**后果:** H2 测试库有列 → 全量测试绿;真实 PostgreSQL 无列 → 运行时 `column "xxx" of relation "yyy" does not exist`。**全量测试绿 = 假绿。**
|
||||
|
||||
**分级(依据 MyBatis-Plus 字段策略):**
|
||||
|
||||
- MyBatis-Plus 默认 `FieldStrategy=NOT_NULL` → null 字段不进 SQL 列清单;
|
||||
- `AutoFillHandler.insertFill` **强制填充** `id` / `createdAt` / `updatedAt` → 这些字段**必然出现在 INSERT**;
|
||||
- 因此缺 `id`/`created_at`/`updated_at` 的表属**"INSERT 必崩"级**。
|
||||
|
||||
**"必崩级"(11 张表):**
|
||||
|
||||
| 表 | 缺失列 | 业务影响 |
|
||||
|---|---|---|
|
||||
| `matches` | `episode`、`source_id`、`last_activity_at`、`unmatched_by`、`deleted_at` | **Match 创建必崩**;episode 追踪不可用 |
|
||||
| `user_preferences` | `id` + 11 个权重列 + `deleted_at` | **择偶偏好保存必崩** |
|
||||
| `permissions`、`roles` | `created_at`/`updated_at`/`deleted_at` | 权限/角色写入必崩 |
|
||||
| `consent_records` | `created_at`/`updated_at`/`deleted_at` | 同意记录必崩 |
|
||||
| `risk_events`、`review_reasons` | `id`/`created_at`/`updated_at` | 风险事件/审核理由写入必崩 |
|
||||
| `match_rule_configs`、`matchmaker_followups`、`matchmaker_recommendations` | `updated_at`/`deleted_at` | 红娘服务写入必崩 |
|
||||
| `user_privacy_settings` | `id`/`deleted_at` | 隐私设置写入必崩 |
|
||||
|
||||
**"潜伏级"(其余 12 张表):** 缺 `deleted_at` 等,null 不进 SQL,但一旦写入非 null 值即崩。含 `local_eligibility_evidences`、`service_program_members`、`user_local_eligibilities`、`user_relations`、`user_identities`、`organization_relationships`、`activity_capacity_groups`、`activity_mutual_connection_consents`、`activity_organizations`、`matchmaker_services`、`review_items`、`review_tasks`。
|
||||
|
||||
**修复:** 新增 `backend/src/main/resources/db/migration/V037__align_entity_columns_with_migrations.sql`(165 行):
|
||||
- 全部采用 `ADD COLUMN IF NOT EXISTS`(**幂等**,可重复执行);
|
||||
- `review_reasons` / `user_preferences` / `user_privacy_settings` 等 PK 非 `id` 的表补 `id` 列 + 唯一索引;
|
||||
- `matches.episode INT NOT NULL DEFAULT 1` + 存量数据回填 `UPDATE matches SET episode = 1 WHERE episode IS NULL`;
|
||||
- 补两个部分索引:`idx_matches_episode`、`idx_matches_deleted`。
|
||||
|
||||
### D2 — High:双路径字段填充不一致 + Integer 自动拆箱 NPE
|
||||
|
||||
**现象:**
|
||||
- `RelationshipService.createMatch` 创建 Match 时**不写** `episode` / `lastActivityAt`;
|
||||
- `MatchService.createOrReuseMatch` 写了;
|
||||
- 两条路径殊途同归到同一张表 → 字段填充不一致;
|
||||
- 且 `MatchService` 中 `m.getEpisode() + 1` 在 `episode` 为 null 时对 `Integer` **自动拆箱抛 NPE**。
|
||||
|
||||
**修复:**
|
||||
- 两条路径统一按 AGENTS.md #20 语义推导 episode:`findLatestMatch` → 若 prior 为 `unmatched` 则 `prior.episode + 1`,否则 1;
|
||||
- 补 null 兜底:`(priorEpisode == null ? 1 : priorEpisode) + 1`;
|
||||
- 两处均设 `lastActivityAt = Instant.now()`。
|
||||
|
||||
**涉及文件:** `relationship/service/RelationshipService.java`、`match/service/MatchService.java`
|
||||
|
||||
### D3 — High:测试库缺 7 张表,首页接口零回归覆盖
|
||||
|
||||
**现象:** 测试库 `schema-h2.sql` 缺 `user_identities` / `review_tasks` / `local_eligibility_evidences` / `home_banners` / `service_locations` / `content_articles` / `activities`。`GET /api/v1/home` 在测试环境直接抛 `BadSqlGrammar("Table USER_IDENTITIES not found")`。
|
||||
|
||||
**后果:** 首页接口在测试环境**根本无法执行** → 所谓"首页已通过"零回归覆盖。
|
||||
|
||||
**修复:** `backend/src/test/resources/schema-h2.sql` 补齐 7 张表(列对齐生产迁移)。
|
||||
|
||||
### D4 — Medium:Phase 12 计划项 2 实际未实现
|
||||
|
||||
**现象:** `HomeApplicationService.buildServiceRecommendations` 的 `getting_to_know` 分支只产出 `counseling`;`premarital`(婚前辅导)入口仅在 `matched_successfully` 才出现。
|
||||
|
||||
**修复:** `getting_to_know` 分支补 `premarital` 引导,与 `counseling` **并列**(不互相挤占):
|
||||
|
||||
```java
|
||||
} else if (Constants.RELATIONSHIP_STATUS_GETTING_TO_KNOW.equals(status)) {
|
||||
list.add(serviceRec("counseling", ...));
|
||||
// Phase 12(计划项 2):getting_to_know 阶段补充婚前辅导入口引导。
|
||||
list.add(serviceRec("premarital", "想更认真地了解彼此?",
|
||||
"善缘丹阳公益婚前辅导帮助你们在决定前更好地了解彼此。",
|
||||
"了解婚前辅导", "/pages/premarital-intro/premarital-intro"));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、新增永久守卫:`EntityMigrationParityTest`
|
||||
|
||||
`backend/src/test/java/com/danyang/match/platform/deployment/EntityMigrationParityTest.java`(5 用例)
|
||||
|
||||
把「实体字段 ⊆ 生产迁移累积列」与「测试库覆盖关键业务表」固化为**编译后自动可验证的不变量**:
|
||||
|
||||
| 用例 | 作用 |
|
||||
|---|---|
|
||||
| `allEntityFieldsExistInMigrations()` | 文本级解析 `db/migration/V*.sql` 的 CREATE TABLE + ALTER ADD/DROP/RENAME COLUMN,对比 `entity/*.java` 的 `@TableName` 字段(含 camelToSnake、BaseEntity 继承列、`@TableField(exist=false)` 排除),区分**硬失败/软失败**,并标注**必崩级/潜伏级** |
|
||||
| `parserMustActuallySeeColumns()` | 解析器自检(防止守卫因解析失效而永远为真) |
|
||||
| `entityParserMustSeeBaseEntityColumns()` | 同上,验证 BaseEntity 继承列被识别 |
|
||||
| `testSchemaCoversCriticalTables()` | 断言 `REQUIRED_IN_TEST_SCHEMA`(14 张关键表)都在 `schema-h2.sql` |
|
||||
| `testSchemaMissingTablesAreWhitelisted()` | 生产表缺于测试库时必须登记在 `TEST_SCHEMA_OPTIONAL_TABLES`(33 项白名单) |
|
||||
|
||||
关键常量:
|
||||
|
||||
```java
|
||||
private static final Set<String> AUTO_FILLED_COLUMNS = Set.of("id", "created_at", "updated_at");
|
||||
private static final Set<String> ALWAYS_WRITTEN_COLUMNS = Set.of(
|
||||
"matches.episode", "matches.last_activity_at", "user_preferences.deleted_at");
|
||||
```
|
||||
|
||||
### 负向验证(守卫有效性证明)
|
||||
|
||||
**第一轮:** 注入缺陷(从 V037 移除 `matches.episode` 补齐)→ `allEntityFieldsExistInMigrations` + `parserMustActuallySeeColumns` **变红**,但分级误标为"潜伏级"。
|
||||
|
||||
**修正:** 新增 `ALWAYS_WRITTEN_COLUMNS`(业务代码每路径必 set 的列),`episode` 由"潜伏级"**纠正为"必崩级"**。
|
||||
|
||||
**第二轮:** 再次注入同一缺陷 → 守卫精准报出:
|
||||
|
||||
```
|
||||
【必崩级】matches: 生产缺【必崩】列 [episode]
|
||||
```
|
||||
|
||||
→ 守卫有效性得证。
|
||||
|
||||
---
|
||||
|
||||
## 五、新增行为测试
|
||||
|
||||
### `RelationshipProgressPhase12Test`(14 用例,`@SpringBootTest` + `@ActiveProfiles("test")`)
|
||||
|
||||
| 用例 | 验证点 |
|
||||
|---|---|
|
||||
| `singleConfirmDoesNotCompleteMatch` | Gate 前半:单方确认不改变最终关系 |
|
||||
| `bothConfirmCompletesMatch` | 双确认 → successful |
|
||||
| `successStopsRecommendations` | 成功后推荐停止 |
|
||||
| `samePersonCannotConfirmTwice` | 同一人重复确认被拒 |
|
||||
| `concurrentConfirmIsIdempotent` | 8 线程 CountDownLatch 并发确认 → Match 仅 successful 一次、success request 仅 1 条 |
|
||||
| `reMatchIncrementsEpisode` | episode 1 → 2 |
|
||||
| `reMatchDoesNotRestoreOldConversation` | 新 Match 零会话;旧会话仍属旧 Match |
|
||||
| `declinedCannotRestart` | declined 不可重启 |
|
||||
| `successfulCannotRestart` | successful 不可重启 |
|
||||
| `pauseKeepsExistingMatch` | 暂停保留既有 Match |
|
||||
| `resumeRestoresSearching` | 恢复回到 searching |
|
||||
| `exitServiceWorks` | 退出服务可用 |
|
||||
| `nonMemberCannotConfirm` | INV-005:工作人员代操作被拒 |
|
||||
| `confirmedByIsAlwaysTheActor` | `confirmedBy` 恒为发起者 |
|
||||
|
||||
### `HomePremaritalGuidancePhase12Test`(5 用例)
|
||||
|
||||
| 用例 | 验证点 |
|
||||
|---|---|
|
||||
| `gettingToKnowProvidesPremaritalEntry` | 计划项 2:getting_to_know 提供婚前辅导入口 |
|
||||
| `gettingToKnowKeepsCounseling` | 补 premarital 后 counseling 未被挤占 |
|
||||
| `successfulKeepsPremaritalEntry` | successful 仍有婚前辅导入口 |
|
||||
| `searchingHasNoPremarital` | searching 不应出现 |
|
||||
| `allTargetsAreInternalPages` | 所有入口指向站内页 |
|
||||
|
||||
---
|
||||
|
||||
## 六、删除假测试
|
||||
|
||||
| 文件 | 问题 | 处理 |
|
||||
|---|---|---|
|
||||
| `relationship/RelationshipStatusServiceTest.java` | 含 2 条 `assertTrue(true); // 占位测试` | 删除占位;改为 4 个对抗生产 CHECK 约束漂移的断言(matchStatus/matchSource/successRequestStatus 与 V003 白名单一致、关系状态取值互异) |
|
||||
| `relationship/RelationshipStatusServiceDoubleConfirmTest.java` | 含占位断言,对 INV-005 零覆盖 | 删除占位;改为口径守卫 + 注释指向 `RelationshipProgressPhase12Test` |
|
||||
|
||||
> AGENTS.md 明令禁止假测试。行为验证统一移至走真实服务方法的 `RelationshipProgressPhase12Test`。
|
||||
|
||||
---
|
||||
|
||||
## 七、测试结果
|
||||
|
||||
### 定向测试
|
||||
|
||||
| 测试类 | 结果 |
|
||||
|---|---|
|
||||
| `EntityMigrationParityTest` | 5/5 ✅(修白名单后单独复跑 5/5) |
|
||||
| `MigrationValidationTest` | 12/12 ✅ |
|
||||
| `HomePremaritalGuidancePhase12Test` | 5/5 ✅ |
|
||||
| `RelationshipProgressPhase12Test` | 14/14 ✅ |
|
||||
| `RelationshipStatusServiceDoubleConfirmTest` | 5/5 ✅ |
|
||||
| `RelationshipStatusServiceTest` | 7/7 ✅ |
|
||||
|
||||
### 全量回归
|
||||
|
||||
```
|
||||
371 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS
|
||||
(71 个测试类,耗时 5m07s)
|
||||
```
|
||||
|
||||
**计数对账:** Phase 11 基线 346 → 本轮 371,**净增 25**。
|
||||
|
||||
| 增量来源 | 用例数 |
|
||||
|---|---|
|
||||
| 新增 `RelationshipProgressPhase12Test` | +14 |
|
||||
| 新增 `EntityMigrationParityTest` | +5 |
|
||||
| 新增 `HomePremaritalGuidancePhase12Test` | +5 |
|
||||
| 删除 `assertTrue(true)` 占位后为两个 relationship 测试类补齐的真实断言(原计数方式不同) | +1 |
|
||||
| **合计** | **+25** |
|
||||
|
||||
> ⚠️ 规划阶段曾预估 358(按"仅新增 3 个测试类共 24 例"粗算),实测 **371**。以实测日志为准,`STATUS.md` / `JIRA_BACKLOG.md` 已同步修正。
|
||||
|
||||
---
|
||||
|
||||
## 八、变更文件清单
|
||||
|
||||
| 文件 | 类型 |
|
||||
|---|---|
|
||||
| `backend/src/main/resources/db/migration/V037__align_entity_columns_with_migrations.sql` | 新增(165 行) |
|
||||
| `backend/src/test/java/.../deployment/EntityMigrationParityTest.java` | 新增 |
|
||||
| `backend/src/test/java/.../relationship/RelationshipProgressPhase12Test.java` | 新增 |
|
||||
| `backend/src/test/java/.../home/HomePremaritalGuidancePhase12Test.java` | 新增 |
|
||||
| `backend/src/main/java/.../home/service/HomeApplicationService.java` | 修改(D4) |
|
||||
| `backend/src/main/java/.../relationship/service/RelationshipService.java` | 修改(D2) |
|
||||
| `backend/src/main/java/.../match/service/MatchService.java` | 修改(D2) |
|
||||
| `backend/src/test/resources/schema-h2.sql` | 修改(D3,补 7 表) |
|
||||
| `backend/src/test/java/.../deployment/MigrationValidationTest.java` | 修改(LATEST_MIGRATION 36→37) |
|
||||
| `backend/src/test/java/.../relationship/RelationshipStatusServiceTest.java` | 重写(删假测试) |
|
||||
| `backend/src/test/java/.../relationship/RelationshipStatusServiceDoubleConfirmTest.java` | 重写(删假测试) |
|
||||
| `docs/STATUS.md`、`docs/JIRA_BACKLOG.md`、本报告 | 文档 |
|
||||
|
||||
---
|
||||
|
||||
## 九、遗留与后续
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| `schema-h2.sql` 手写维护风险 | 本轮已通过 `EntityMigrationParityTest` 守卫;长期建议改为由 Flyway 迁移生成测试 schema(或用 Testcontainers 直连生产迁移),消除手写漂移根源 |
|
||||
| `TEST_SCHEMA_OPTIONAL_TABLES` 白名单 | 现有 33 项;后续新增表需同步登记,否则守卫变红(**这是设计意图**) |
|
||||
| Phase 13(公益红娘转介 — EXTEND) | **未自动进入,等待用户确认** |
|
||||
|
||||
---
|
||||
|
||||
## 十、结论
|
||||
|
||||
Phase 12 三个计划项与 Gate 全项通过。**其中 D1 为跨阶段共性根因**——前四阶段(Phase 08~11)之所以能看到"测试绿而生产崩",正是因为测试库 schema 手写且与生产迁移漂移;本轮不仅修复了当前缺陷,还以 `EntityMigrationParityTest` 把这一族问题固化为自动守卫。
|
||||
|
||||
**PHASE 12 CLOSED.**
|
||||
+40
-7
@@ -1,8 +1,8 @@
|
||||
# 项目状态跟踪 — STATUS.md
|
||||
|
||||
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-11 RECOVERY CLOSED / GIT CLEAN`。**
|
||||
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-12 RECOVERY CLOSED / GIT CLEAN`。**
|
||||
>
|
||||
> 阶段 0(在途收口)、阶段 1(小程序 C 组 + discover 筛选重设计)、阶段 2(管理端 Vue 新框架适配)、阶段 3(CapabilityService 独立化)、阶段 4(双端总验收)全部完成。三端构建通过、**13/13 契约全绿**、核心 11 页面**全部 TS-only(11/11)**。后端全量回归已清偿(346 tests / 0 failures / 0 errors)。**UI 重设计首批 11 页已于 2026-09-16 通过用户验收(结论:基本达标)**;`/me/*` 前后端契约同轮收敛,17 个 404 调用已恢复。**Phase 08(Like / Match / Block)、Phase 09(Text Chat)、Phase 10(Image & Chat Safety)、Phase 11(Notification)复验已关闭**,四轮共修复 12 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 12 需用户确认后启动。**
|
||||
> 阶段 0(在途收口)、阶段 1(小程序 C 组 + discover 筛选重设计)、阶段 2(管理端 Vue 新框架适配)、阶段 3(CapabilityService 独立化)、阶段 4(双端总验收)全部完成。三端构建通过、**13/13 契约全绿**、核心 11 页面**全部 TS-only(11/11)**。后端全量回归已清偿(371 tests / 0 failures / 0 errors)。**UI 重设计首批 11 页已于 2026-09-16 通过用户验收(结论:基本达标)**;`/me/*` 前后端契约同轮收敛,17 个 404 调用已恢复。**Phase 08(Like / Match / Block)、Phase 09(Text Chat)、Phase 10(Image & Chat Safety)、Phase 11(Notification)、Phase 12(Relationship Progress)复验已关闭**,五轮共修复 16 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 13 需用户确认后启动。**
|
||||
|
||||
最后更新: 2026-09-16 — 分支 `main`(`feat-ui-v11` 保留为冗余锚点)
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
| 阶段 2 管理端 Vue 适配 | 已完成 | 18 modules + 22 routes + vue-tsc 全绿 + vite build 11.68s |
|
||||
| 阶段 3 CapabilityService 独立化 | 已完成 | New `capability` 模块:CapabilityService(getCapabilities + 公共判断方法)+ CapabilityController(GET /api/v1/capabilities);HomeApplicationService 完全委托 |
|
||||
| 阶段 4 双端总验收 | **已完成**(2026-09-14) | 三端构建全绿 + 13/13 契约全绿 + 源码健康检查通过;验收报告见 docs/superpowers/plans/2026-09-14-phase4-acceptance-report.md |
|
||||
| Flyway 迁移 | V001~V036 | V028~V036 已纳入 STATUS |
|
||||
| Backend 全量回归 | **已清偿**(2026-09-16) | 离线容器(`danyang-match-backend-test`)运行:**346 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**;含 `CanonicalRouteContractTest`、`ChatContractJsonTest`、`MeRouteContractTest`、`DiscoverBlockInvisibilityIntegrationTest`、`ChatPhase09IntegrationTest`、`ChatWebSocketHandlerTest`、`PhotoUploadValidationTest`、`NotificationPhase11IntegrationTest`、`NotificationTemplateTypeGuardTest`、`NotificationPrivacyCopyTest` |
|
||||
| Flyway 迁移 | V001~V037 | V028~V037 已纳入 STATUS;V037 为 Phase 12 新增(实体列与生产迁移对齐,23 表 50 列) |
|
||||
| Backend 全量回归 | **已清偿**(2026-09-16) | 离线容器(`danyang-match-backend-test`)运行:**371 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**;含 `CanonicalRouteContractTest`、`ChatContractJsonTest`、`MeRouteContractTest`、`DiscoverBlockInvisibilityIntegrationTest`、`ChatPhase09IntegrationTest`、`ChatWebSocketHandlerTest`、`PhotoUploadValidationTest`、`NotificationPhase11IntegrationTest`、`NotificationTemplateTypeGuardTest`、`NotificationPrivacyCopyTest`、`EntityMigrationParityTest`、`RelationshipProgressPhase12Test`、`HomePremaritalGuidancePhase12Test` |
|
||||
| 路由/字段契约收敛 | **已完成**(2026-09-15) | 字段:会话 `target*` + `sentAt` + `relationStatus`(commit `687a156`);路径:采用 2026-09-14 canonical(`/likes` `/passes` `/blocks` `/matches`,缘分保留 `/relations/fate/*`),旧路径与 `success` 桩实现已移除;裁决由 `CanonicalRouteContractTest` 锁定 |
|
||||
| `/me/*` 路径族 | **已完成**(2026-09-16) | 小程序按 REST 规范(§16 / §22-§40 / §80 / §83-§84)调用 `/me/*`,后端此前**无 `/me` 基路径**致 17 个调用 404。新增 `MeController` 委托既有 service 暴露规范路径,旧路径全部保留,小程序三条历史写法(`/me/privacy-settings`、`/me/photos/{id}/main`、`/me/appeals`)一并兼容;由 `MeRouteContractTest` 锁定 |
|
||||
| Admin Web | **验证通过** | 2026-09-14 阶段 4:vue-tsc ✅、vite build ✅ 11.68s;18 modules 齐全;壳层+工作台通过参考图 QA |
|
||||
| Miniapp | **验证通过** | 2026-09-16:tsc ✅;**11 核心页全部 TS-only(11/11)**;首批 11 页于 2026-09-16 通过用户验收;discover 筛选重设计已通过 DevTools 验证 |
|
||||
| UI 契约测试 | **13/13 通过** | 2026-09-16 运行:全部退出码 0;含 capability 独立化适配与文档基线口径 |
|
||||
| Git 工作区 | **已清零** | 阶段 0~4 共 24+ commit 可追溯;`git status --porcelain` 0 行 |
|
||||
| 核心待清债务 | 0 项 | 已全部清偿:3 页 JS→TS 迁移、backend 全量回归(299 tests)、DevTools 真机截图留档(2026-09-14)、`/me/*` 路径族收敛(2026-09-16) |
|
||||
| UAT / RC / Production | **UI 验收通过,前后端契约已收敛,Phase 08-09 已关闭** | 双端构建/契约/源码全部通过;UI 首批 11 页 2026-09-16 通过用户验收;`/me/*` 契约同轮收敛;Phase 08/09 复验通过并关闭,可推进 Phase 10(**需用户确认后启动**) |
|
||||
| 核心待清债务 | 0 项 | 已全部清偿:3 页 JS→TS 迁移、backend 全量回归(371 tests)、DevTools 真机截图留档(2026-09-14)、`/me/*` 路径族收敛(2026-09-16)、实体↔生产迁移漂移修复(V037,2026-09-16) |
|
||||
| UAT / RC / Production | **UI 验收通过,前后端契约已收敛,Phase 08-12 已关闭** | 双端构建/契约/源码全部通过;UI 首批 11 页 2026-09-16 通过用户验收;`/me/*` 契约同轮收敛;Phase 08/09/10/11/12 复验通过并关闭,累计修复 16 处真实缺陷,可推进 Phase 13(**需用户确认后启动**) |
|
||||
|
||||
---
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|------|------|------|
|
||||
| 1.0.0-RC.2 | 2026-09-01 | V1.0 核心功能冻结 |
|
||||
| 1.1.0-dev | 2026-09-08 | V1.1 基线搭建启动(Phase 01 进行中) |
|
||||
| 1.1.0-rc.3 | 2026-09-16 | Phase 08~12 复验关闭;新增 V037 对齐实体↔生产迁移;全量回归 371 tests 全绿 |
|
||||
|
||||
---
|
||||
|
||||
@@ -211,12 +212,44 @@
|
||||
| Constants 新增转介来源常量 | ✅ |
|
||||
| INV-005 红娘不可代用户操作验证 | ✅ |
|
||||
|
||||
### Phase 12 — 关系状态轻量扩展 ✅ 已完成
|
||||
### Phase 12 — 关系状态轻量扩展 ✅ 已完成(2026-09-16 KEEP-VERIFY 复验通过)
|
||||
| 任务 | 状态 |
|
||||
|------|------|
|
||||
| Successful首页服务建议(婚前辅导引导) | ✅ |
|
||||
| GettingToKnow服务建议规则 | ✅ |
|
||||
| Counseling recommendation rules | ✅ |
|
||||
| 成功关系双确认 / 恢复 / 退出 / 历史 Episode / 并发确认 | ✅ 行为测试 14 项 |
|
||||
| getting_to_know 阶段婚前辅导入口引导(计划项 2) | ✅ 已接线 `HomeApplicationService.buildServiceRecommendations` |
|
||||
| 工作人员不能替用户确认成功关系(INV-005) | ✅ 非成员路径断言抛业务异常 |
|
||||
|
||||
> **2026-09-16 复验结论(KEEP-VERIFY/EXTEND)**:历史 ✅ 不可信,逐条核对运行时路径后修复 4 处缺陷:
|
||||
> - **D1(Critical,生产故障 · 同一族系统性缺陷)** 实体与生产迁移**系统性漂移**:23 张表、50 个列在
|
||||
> `db/migration` 中根本不存在,而测试库 `schema-h2.sql` 手写了它们 → 全量测试绿 = 假绿。其中
|
||||
> **11 张表属"INSERT 必崩"级**(缺 `id`/`created_at`/`updated_at`,被 `AutoFillHandler` 强制填充,
|
||||
> 每次写入必进 SQL):`matches`(缺 `episode`/`source_id`/`last_activity_at`/`unmatched_by`/`deleted_at`)、
|
||||
> `user_preferences`(缺 `id` + 11 个权重列)、`permissions`、`roles`、`consent_records`、`risk_events`、
|
||||
> `review_reasons`、`match_rule_configs`、`matchmaker_followups`、`matchmaker_recommendations`、
|
||||
> `user_privacy_settings`。**真实 PG 上 Match 创建、择偶偏好保存、权限/角色写入、同意记录、风险事件
|
||||
> 全部失败**。修复:新增 `V037__align_entity_columns_with_migrations.sql`(幂等 `ADD COLUMN IF NOT
|
||||
> EXISTS`,含存量数据 `episode` 回填与 `id` 补发)。
|
||||
> - **D2(High)** `RelationshipService.createMatch` 不写 `episode`/`lastActivityAt`,与
|
||||
> `MatchService.createOrReuseMatch` 形成**双路径字段填充不一致**;且 `episode` 为 null 时
|
||||
> `MatchService` 的 `m.getEpisode() + 1` 对 `Integer` **自动拆箱抛 NPE**。修复:两条路径统一按
|
||||
> AGENTS.md #20 推导 episode,并做 null 兜底。
|
||||
> - **D3(High)** 测试库缺 `user_identities` 等 7 张表 → `GET /api/v1/home` 在测试环境直接
|
||||
> `BadSqlGrammar("Table USER_IDENTITIES not found")`,**首页接口零回归覆盖**。修复:补齐
|
||||
> `user_identities`/`review_tasks`/`local_eligibility_evidences`/`home_banners`/`service_locations`/
|
||||
> `content_articles`/`activities` 七张表(列对齐生产迁移)。
|
||||
> - **D4(Medium)** Phase 12 计划项 2 实际**未实现**:`getting_to_know` 分支只产出 `counseling`,
|
||||
> 婚前辅导入口仅在 `matched_successfully` 才出现。修复:`HomeApplicationService.buildServiceRecommendations`
|
||||
> 的 `getting_to_know` 分支补 `premarital` 引导(与 counseling 并列,不互相挤占)。
|
||||
> - **新增永久守卫** `EntityMigrationParityTest`(5 用例):把「实体字段 ⊆ 生产迁移累积列」与
|
||||
> 「测试库覆盖关键业务表」固化为**编译后自动可验证的不变量**,并区分「必崩级 / 潜伏级」。经
|
||||
> **两轮负向验证**证明会变红(第一轮分级过窄已修正:`episode` 由"潜伏级"纠正为"必崩级")。
|
||||
> - **删除假测试**:`RelationshipStatusServiceTest` 与 `RelationshipStatusServiceDoubleConfirmTest`
|
||||
> 原含 `assertTrue(true); // 占位测试`,对 INV-005 零覆盖(AGENTS.md 明令禁止)→ 已删除占位断言,
|
||||
> 行为验证移至走真实服务方法的 `RelationshipProgressPhase12Test`(14 用例,含并发确认)。
|
||||
> - 报告:`docs/Phase-12-V11-Recovery-Completion-Report-2026-09-16.md`
|
||||
|
||||
### V1.1 待实现模块(按 Phase 顺序)
|
||||
|
||||
|
||||
@@ -239,11 +239,19 @@ V036;D2——接线婚前辅导及管理端排期通知。`sendExternal` 在
|
||||
|
||||
**Files:** relationship 状态服务、Match Episode、relationship-status 页面。
|
||||
|
||||
- [ ] 验证成功关系双确认、恢复/退出、历史 Episode 和并发确认。
|
||||
- [ ] 补充 getting_to_know 阶段的婚前辅导入口引导。
|
||||
- [ ] 验证工作人员不能替用户确认成功关系。
|
||||
- [x] 验证成功关系双确认、恢复/退出、历史 Episode 和并发确认。
|
||||
- [x] 补充 getting_to_know 阶段的婚前辅导入口引导。
|
||||
- [x] 验证工作人员不能替用户确认成功关系。
|
||||
|
||||
**Gate:** 单方确认不改变最终关系;历史 Episode 可追踪但不会恢复旧聊天权限。
|
||||
(2026-09-16 复验:修复 D1——实体与生产迁移系统性漂移,23 表 50 列缺失,其中 `matches.episode`
|
||||
等 11 张表属"INSERT 必崩"级生产故障,真实 PG 上 Match 创建 / 偏好保存 / 权限写入等全部失败;
|
||||
D2——`RelationshipService.createMatch` 不写 episode/lastActivityAt,与 `MatchService` 双路径字段不一致,
|
||||
且 `episode` 为 null 时 `getEpisode() + 1` 自动拆箱 NPE;D3——测试库缺 `user_identities` 等 7 张表,
|
||||
致 `GET /home` 在测试环境直接 BadSqlGrammar,首页零回归覆盖;D4——Phase 12 计划项 2 实际未实现
|
||||
(getting_to_know 只给 counseling,无婚前辅导入口)。新增 `EntityMigrationParityTest` 把
|
||||
「实体字段 ⊆ 生产迁移列」「测试库覆盖关键表」固化为自动守卫,并经负向验证证明会变红。
|
||||
真机行为:双确认/并发确认/历史 Episode 不恢复旧聊天/非成员不可代确认 —— 14 项全绿。)
|
||||
|
||||
### Phase 13:公益红娘转介 — EXTEND
|
||||
|
||||
|
||||
Reference in New Issue
Block a user