fix(activity-mutual): close Phase 16 KEEP-VERIFY/EXTEND — 7 defects + 1 hidden compile defect (reopenEpisode) + 1 test-fidelity; unify dual-impl to confirmActivityInterest; INV-006 fix; +7 tests (481 green)

This commit is contained in:
WorkBuddy
2026-09-17 16:47:10 +08:00
parent 29112eed6e
commit 556f95a562
16 changed files with 638 additions and 71 deletions
+24 -9
View File
@@ -10,7 +10,13 @@
> 测试库缺该唯一键故零覆盖。共修复 **16 处缺陷 + 3 处测试保真度缺陷**,新增 V040
> `activity_staff_assignments` 落实「服务站操作员只能操作指定活动」(INV-020)。
> 证据:`docs/Phase-15-V11-Recovery-Completion-Report-2026-09-17.md`。
> **Phase 16 需用户确认后启动。**
>
> **2026-09-17 Phase 16 复验关闭:** EPIC-P16-V11 已完成。复验发现活动互选**核心 Gate 缺陷**——双确认后**根本不建 Match/会话**(INV-014/015),
> 且 Phase 15 提交态引用未定义的 `matchMapper.reopenEpisode` → **整个模块从未编译通过**,"474 绿"是假象。已统一双实现到
> `RelationshipService.confirmActivityInterest`,修复 **7 处缺陷 + 1 处隐藏编译缺陷 + 1 处测试保真度缺陷**,新建
> `ActivityMutualConsentServiceTest`(7 用例),并经负向验证证明守卫有牙。新增 V041 `expires_at`+`match_id`。
> 证据:`docs/Phase-16-V11-Recovery-Completion-Report-2026-09-17.md`。
> **Phase 17 需用户确认后启动。**
> **2026-09-16 状态校正:** Phase 07 UI 已按 2026-09-13 规格重新实现,**首批 11 页于 2026-09-16 通过用户验收(结论:基本达标)**,本文件此前"视觉否决/REOPENED"口径随之关闭。
>
@@ -130,6 +136,7 @@
| Phase 13A 婚恋咨询与婚前辅导复验 | 已完成(2026-09-17) | 见 `Phase-13A-V11-Recovery-Completion-Report-2026-09-17.md`;发现 counseling **双实现**(权威类零测试 / 孤儿类被单测),已合并删除孤儿类;修复 **9 处缺陷**(**D1 咨询正文写日志=INV-013 泄露**、D2 申请 userId 取请求体、**D3 分配先读后写并发静默覆盖**、D4 manager 可读 followups=INV-013 绕过、D5 无状态机、**D6 `/services/intro` 后端不存在致小程序 intro 页 100% 无数据**、D7 Admin 5 端点 404、D8 小程序字段名/幂等/静默失败、D9 测试库 counseling 零 CHECK 约束)+ **4 处测试基建缺陷**(守卫找不到 DDL 恒真假绿、全限定 `@TableField` 漏解析、权限守卫假阳性、测试自身逻辑缺陷);迁移基线 V039;新增 `CounselingPhase13ATest`(25)+ `CounselingConstraintGuardTest`(5,解析真实 DDL),两轮负向验证证明守卫会红;后端 **420 tests 全绿**;**Phase 14 尚未获准** |
| Phase 14 多类型公益活动复验 | 已完成(2026-09-17) | 见 `Phase-14-V11-Recovery-Completion-Report-2026-09-17.md`;修复 **11 处后端缺陷**(**D1 `ActivityMapper.countRemainingCapacity` 无 SQL 空壳致详情/报名/审核必崩**、**D2 报名与审核先读后写并发超卖** → 改原子 SQL、D3 类型/角色未校验生产 CHECK 白名单、**D4 DTO 无 `activityType` 致小程序分类 Tabs 恒空**、D5 男女人数 `approvedCount/2` 编造、D6 年龄门槛空壳、D7 按类型资格策略为空、**D8 运营端建活动丢弃类型且把主办组织错设为操作者 userId**、D9 三个运营端点错挂用户端双实现、D10 软删活动/缺字段 NPE、D11 DTO 缺 4 字段)+ **3 处前端契约缺陷**(分类 code `classroom` 不在白名单、卡片读 7 个不存在字段、admin-web 无活动管理页)+ **2 处测试保真度缺陷**(2 条占位假绿、测试库缺 V023 列与三张表);新增 `ActivityPhase14Test`(23)+ `ActivityConstraintGuardTest`(9,解析真实 DDL **与前端源码**),两轮负向验证证明守卫会红;admin-web `vue-tsc` 0 error + `vite build` 通过;后端 **450 tests 全绿**;**Phase 15 尚未获准** |
| Phase 15 活动报名审核签到复验 | 已完成(2026-09-17) | 见 `Phase-15-V11-Recovery-Completion-Report-2026-09-17.md`;修复 **16 处缺陷**(**D1 签到码一生成即作废**:`generateCheckinToken` 把 token 写 `"used"` 而扫码端见 `"used"` 即拒 → 扫码 100% 失败;**D9 取消/拒绝后永远无法再报名**:撞生产 `UNIQUE(activity_id,user_id)`,测试库缺该约束故零覆盖 → 改复用记录;D2 重复签到竞态、D3 签到无数据范围、D4 手工签到无校验、D5 统计 NPE+语义错、D6 非法 token 500、D7 签名非恒定时间比较、D10 后台审核列表无 pending、D11 报名窗口不生效、D12 取消无审计、D13 审核/取消非原子、D14 容量分组不参与扣减)+ **2 处前端契约缺陷**(admin-web 无签到工作台、小程序无取消报名入口);新增 V040 `activity_staff_assignments` 落实 INV-020 活动数据范围;**3 处测试保真度缺陷**(测试库缺 UNIQUE+2 CHECK、3 条 `assertTrue(true)` 假绿、表未入必选集合);新增 `ActivityPhase15Test`(21),`CheckinServiceTest` 假绿改真实守卫(5),约束守卫 +2,迁移基线 39→40;一轮负向验证 3 处注入 → 8 条用例精准变红并还原;admin-web `vue-tsc` 0 error + `vite build` 通过;后端 **474 tests 全绿**;**Phase 16 尚未获准** |
| Phase 16 活动互选二阶段同意复验 | 已完成(2026-09-17) | 见 `Phase-16-V11-Recovery-Completion-Report-2026-09-17.md`;修复 **7 处缺陷 + 1 处隐藏编译缺陷 + 1 处测试保真度缺陷**(**D1 双确认后根本不建 Match/会话(Gate 缺陷·INV-014/015)**、**D3 Phase 15 提交态引用未定义 `reopenEpisode` 致整个模块不编译("474 绿"为假象)**、D4 INV-006 单向意向泄露、D5 复用/新建 Match 后缺会话);统一双实现到 `RelationshipService.confirmActivityInterest`;`MatchMapper` 补 `reopenEpisode`;测试库补 `activity_mutual_connection_consents` 表并移入必选集合,`MigrationValidationTest` 基线 40→41;新建 `ActivityMutualConsentServiceTest`(7 用例);`ChatPhase09IntegrationTest:344` 断言对齐 AGENTS.md #20 复用语义;新增 V041 `expires_at`+`match_id`;负向验证 2 用例精准变红并还原;后端 **481 tests 全绿**;**Phase 17 尚未获准** |
| 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 通过用户验收 |
@@ -445,24 +452,32 @@
---
## EPIC-P16-V11: 活动互选二阶段同意重构 ✅ 核心链路已完成(V1.1 MVP)
## EPIC-P16-V11: 活动互选二阶段同意重构 ✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验关闭)
### Data Tasks
- P16-V11-T01 activity_mutual_connection_consents table ✅ (V024)
- P16-V11-T02 pair unique active constraint ✅ (UNIQUE index with LEAST/GREATEST)
- P16-V11-T02b V041 expires_at + match_id ✅ 新增(与 consent 超时拒绝、互选溯源对齐)
### Business Tasks
- P16-V11-T03 detect mutual interest ✅ (detectMutualResults)
- P16-V11-T04 create mutual consent ✅ (createMutualConsent)
- P16-V11-T05 user A confirm ✅ (confirmConnection)
- P16-V11-T06 user B confirm ✅ (confirmConnection)
- P16-V11-T07 both-confirm transaction ✅ (@Transactional)
- P16-V11-T08 create Match Episode ✅ (existing RelationshipService.confirmActivityInterest)
- P16-V11-T09 create Conversation ✅ (not auto-created, per INV-015)
- P16-V11-T10 existing active Match reuse ✅ (hasActiveMatch check)
- P16-V11-T11 prior Unmatch new episode ✅ (findLatestMatch + priorUnmatchExists)
- P16-V11-T12 decline handling ✅ (declineConnection)
- P16-V11-T13 timeout handling ⏳ 待补充(后续Phase)
- P16-V11-T07 both-confirm transaction ✅ (@Transactional, exactly-once via `both_confirmed_at` 原子 UPDATE)
- P16-V11-T08 create Match Episode ✅ (统一到 `RelationshipService.confirmActivityInterest`,赢家方建 Match)
- P16-V11-T09 create Conversation ✅ **修复**:双确认后**显式开启 Conversation**(`getOrCreateConversation`);旧实现"不自动建"违反 Gate,已纠正
- P16-V11-T10 existing active Match reuse ✅ (统一经 `confirmActivityInterest`:active 复用、unmatched 经 `reopenEpisode` 复用同一行)
- P16-V11-T11 prior Unmatch reuse same row + episode increment ✅ (`reopenEpisode` 递增 episode,状态回 active)
- P16-V11-T12 decline handling ✅ (declineConnection + `declineSideIfPending`)
- P16-V11-T13 timeout handling ⏳ 待补充(后续 Phase;V041 已加 `expires_at` 列,拒绝逻辑待接线)
**复验备注(2026-09-17 KEEP-VERIFY/EXTEND)**:
- 修复 **7 处缺陷 + 1 处隐藏编译缺陷(Phase 15 提交态引用未定义 `reopenEpisode` 致模块不编译,"474 绿"为假象)+ 1 处测试保真度缺陷**。
- 双实现统一到 `RelationshipService.confirmActivityInterest`;`MatchMapper` 补 `reopenEpisode`;INV-006 单向意向泄露修复。
- 测试库补 `activity_mutual_connection_consents` 表并移入 `REQUIRED_IN_TEST_SCHEMA`;`MigrationValidationTest` 基线 40→41;新建 `ActivityMutualConsentServiceTest`(7 用例)。
- 负向验证 2 用例精准变红并还原;全量回归 **481 tests / 0 failures** 全绿。
- 证据:`docs/Phase-16-V11-Recovery-Completion-Report-2026-09-17.md`。
### Miniapp Tasks
- P16-V11-T14 Mutual Result page ✅ (activity-mutual-result)
@@ -0,0 +1,102 @@
# Phase 16(活动互选与双方开启沟通确认)V1.1 增量复验完成报告
- **日期**:2026-09-17
- **模式**:KEEP-VERIFY / EXTEND
- **范围**:activity_mutual、relationship、messaging 模块;activity-mutual 双端页面
- **基线**:Phase 15 收尾 `29112ee`(全量回归 474 tests)
- **结论**:**关闭**。三项计划事项全部落地,Gate 三项均达成
---
## 一、计划项达成情况
| 计划项 | 结论 | 证据 |
|---|---|---|
| 以 `activity_mutual_connection_consents` 为唯一逻辑,废弃 mutual 直接建 Match/Chat 的入口 | ✅ | 双实现(旧 `ActivityInterest.confirmInterest` / 新 `ActivityMutualConsent.confirmConnection`)统一到 `RelationshipService.confirmActivityInterest` 单实现 |
| 双方确认事务中:已有 active Match 则复用会话;历史 Unmatch 则复用同一行(episode 递增 + reopen);无历史则创建 Match 与 Conversation | ✅ | `ActivityMutualConsentService.createMatchAndConversation` 走 `confirmActivityInterest`;active 复用、unmatched 经 `MatchMapper.reopenEpisode` 复用同一行 |
| 补 decline、expires_at 超时、重复确认幂等、并发确认(exactly-once)与单向意向保密(INV-006)测试 | ✅ | 新增 `ActivityMutualConsentServiceTest`(7 例),覆盖双确认建 Match+会话+溯源、INV-006 单向不暴露、decline 阻止、幂等重确认不重复建、expires_at 超时拒绝、软删过滤、复用 active Match |
**Gate**
| Gate | 结论 |
|---|---|
| A 单选 B 时 B 无感知(INV-006 单向意向保密) | ✅ `getMyConsentStatus` 改为 `partnerAccepted = myAccepted && partnerSideAccepted`;本侧未确认则对方确认态不回显 |
| Mutual 但未双确认时无新 Chat | ✅ `confirmConnection` 单方确认不建 Match/会话,仅持久化本侧确认态 |
| 双确认后 exactly-once 形成正确 Match/Conversation | ✅ 原子 `confirmSideIfPending`(`both_confirmed_at` NULL→非NULL 仅一方赢家 `affected==1 && bothConfirmedAt!=null` 建 Match+会话并 `bindMatch` 溯源) |
---
## 二、复验发现与修复缺陷清单(7 处 + 1 处隐藏编译缺陷 + 1 处测试保真度)
### A. 核心 Gate 缺陷(活动互选二阶段同意)
- **D1 CRITICAL — 双确认后根本不建 Match/会话(Gate 缺陷 · 违反 INV-014/015)**:新路径 `ActivityMutualConsentController.confirmConnection` 在双方确认后**只置 `both_confirmed_at`,不调用任何 Match/会话创建**;旧路径 `ActivityInterest.confirmInterest`→`confirmActivityInterest` 建 Match 却**缺会话**。两条路径分裂,导致"活动互选成功后无法开启沟通"。重写为 `confirmConnection`:以原子 `confirmSideIfPending` 保证 exactly-once,由赢家方调用 `confirmActivityInterest` 建 Match+会话并 `bindMatch` 溯源;单方确认不建 Match。
- **D2 HIGH — 双实现分裂**:旧/新两条确认路径各自为政、语义不一致。统一到 `RelationshipService.confirmActivityInterest`(返回 `matchId` + `priorUnmatch`,显式开启会话)。
- **D3 HIGH — 隐藏编译缺陷(必崩级 · 最严重发现)**:Phase 15 提交态在 `RelationshipService.confirmActivityInterest` 中调用 `matchMapper.reopenEpisode`,但 `MatchMapper` **从未定义该方法** → 整个 activity_mutual / relationship 模块**根本不编译**。因此 **"474 绿"基线从未真实存在**,所谓通过是假象。修复:在 `MatchMapper` 补 `@Update reopenEpisode`(`WHERE id=#{id} AND status='unmatched' AND deleted_at IS NULL`)。**结论必须如实记录:Phase 15 的提交态从未通过编译。**
- **D4 HIGH — INV-006 单向意向泄露**:`getMyConsentStatus` 在本侧未确认时即回显对方确认状态。改为 `partnerAccepted = myAccepted && partnerSideAccepted`,本侧未确认则对方确认态不回显。
### B. 缺会话与测试保真度(4 处)
- **D5 MEDIUM — 复用/新建 Match 后缺会话**:`confirmActivityInterest` 此前不显式开启会话。现**显式**调用 `chatService.getOrCreateConversation(match.getId(), ...)` 并返回 `conversationAutoCreated=true`(新建与复用两条分支均覆盖)。
- **D6 MEDIUM — 测试库零覆盖(同族根因)**:`schema-h2.sql` 缺 `activity_mutual_connection_consents` 表 → 互选逻辑在测试环境无表可跑,长期静默。已补表(含 `deleted_at`/`expires_at`/`match_id` 与 `chk_consent_status` CHECK、UNIQUE(activity_id,user_a_id,user_b_id)),与生产 V024+V037+V041 对齐;`EntityMigrationParityTest` 将其从 `TEST_SCHEMA_OPTIONAL_TABLES` 移入 `REQUIRED_IN_TEST_SCHEMA`。
- **D7 MEDIUM — 无互选测试**:此前零互选单测。新建 `ActivityMutualConsentServiceTest`(7 例,`@SpringBootTest` 走真实方法路径)。
- **D8 MEDIUM — 断言与 AGENTS.md #20 冲突**:`ChatPhase09IntegrationTest:344` 假设"每次活动互选新建独立会话",与 AGENTS.md #20 "同对用户 Unmatch 后重连复用同一 Match 行、会话随之复用"冲突 → 改为 `assertEquals(conv.getId(), activityConv.getId(), ...)`(认可复用),保留 `sendText`/WebSocket/未读主断言以验证"复用同一消息管线"。
---
## 三、测试保真度修复(3 处)
- **T1 约束漂移盲区**:测试库缺 `activity_mutual_connection_consents` 表(正是 D6 长期不可见的根因)。已补齐并移入 `REQUIRED_IN_TEST_SCHEMA`。
- **T2 迁移基线推进**:`MigrationValidationTest` 基线 40 → 41(新增 V041)。该守卫会拒绝任何超出基线的新迁移文件,是设计内的收敛机制。
- **T3 断言与域语义对齐**:`ChatPhase09IntegrationTest:344` 修正为复用语义(见 D8)。
> 附带教训:**提交态不编译 ≠ 测试通过。** 本轮证明"474 绿"是假象(D3)。每次复验必须先在离线容器跑一次真实 `mvn -o test` 编译+执行,不能以文档声明或历史 ✅ 替代实证。
---
## 四、证据
**定向测试**:`ActivityMutualConsentServiceTest` 7 tests / 0 failures(覆盖双确认建 Match+会话+溯源、INV-006 单向不暴露、decline 阻止、幂等重确认不重复建、expires_at 超时拒绝、软删过滤、复用 active Match)
**负向验证**(注入真实缺陷 → 守卫必须变红,随后全部还原):
| 注入 | 变红的用例 |
|---|---|
| 临时注释 `createMatchAndConversation(consent);` 调用(还原 D1 一半:双确认不建 Match) | `doubleConfirmCreatesMatchAndConversation` / `idempotentReconfirmDoesNotDuplicateMatch`(2 条用例精准变红,归因与注入一一对应) |
还原后确认工作区无 `NEG-VAL` 残留(`grep -c "NEG-VAL"` = 0)。
**全量回归**:`Tests run: 481, Failures: 0, Errors: 0, Skipped: 3` / `BUILD SUCCESS`(离线容器 `danyang-match-backend-test` `mvn -B -o test`,约 5 分;基线 474 → 481,净 +7)
**新增迁移**:`V041__extend_mutual_consent_expiry.sql`(`expires_at` TIMESTAMPTZ + `match_id` UUID + 索引),与 consent 超时拒绝、互选溯源对齐。
---
## 五、变更清单
**新增文件**
- `backend/src/main/resources/db/migration/V041__extend_mutual_consent_expiry.sql`
- `backend/src/test/java/com/danyang/match/platform/activity_mutual/ActivityMutualConsentServiceTest.java`
**修改文件**
- `activity_mutual/service/ActivityMutualConsentService.java`(重写 `confirmConnection` 走 `confirmActivityInterest`;exactly-once 赢家建 Match+会话并 `bindMatch`;删除死代码 `hasActiveMatch`;修复 INV-006 `getMyConsentStatus`)
- `match/mapper/MatchMapper.java`(**新增 `reopenEpisode`**,消除 D3 隐藏编译缺陷)
- `relationship/service/RelationshipService.java`(`confirmActivityInterest` 返回 `matchId`/复用标记并**显式开启会话**)
- `activity_mutual/entity/ActivityMutualConsent.java`(extends BaseEntity + `expiresAt`/`matchId`)
- `activity_mutual/mapper/ActivityMutualConsentMapper.java`(软删过滤 `deleted_at IS NULL` + 原子 `confirmSideIfPending`/`declineSideIfPending`/`bindMatch`)
- `src/test/resources/schema-h2.sql`(新增 `activity_mutual_connection_consents` 表,对齐 V024+V037+V041)
- `deployment/EntityMigrationParityTest.java`(consent 表从可选集合移入 `REQUIRED_IN_TEST_SCHEMA`)
- `deployment/MigrationValidationTest.java`(基线 40 → 41)
- `chat/ChatPhase09IntegrationTest.java`(第 344 行断言对齐 AGENTS.md #20 复用语义)
---
## 六、遗留(供后续阶段)
1. **P16-V11-T13 超时处理未接线**:V041 已加 `expires_at` 列与拒绝逻辑所需字段,但"超时后自动置 expire"的定时/惰性触发尚未实现,留待后续 Phase。
2. **互选超时维度测试**:`ActivityMutualConsentServiceTest.expiredConsentRejectsConfirm` 已就"带 `expires_at` 且已过期的记录拒绝确认"给出守卫,但无"到期自动推进状态"的端到端验证。
3. **双向活动意向入口一致性**:小程序 `activity-mutual-result` 双端状态机已就绪,但双确认成功后的"跳转到既有会话"仍需真机联调确认(后端契约已通过 `conversations/from-match/{id}` 验证)。
---
**Phase 16 已关闭。Phase 17(举报与风控)未自动进入,等待用户确认。**
+22 -1
View File
@@ -2,7 +2,7 @@
> **2026-09-17 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 14 COMPLETE / GIT CLEAN`。**
>
> 阶段 0(在途收口)、阶段 1(小程序 C 组 + discover 筛选重设计)、阶段 2(管理端 Vue 新框架适配)、阶段 3(CapabilityService 独立化)、阶段 4(双端总验收)全部完成。三端构建通过、**13/13 契约全绿**、核心 11 页面**全部 TS-only(11/11)**。**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)、Phase 13(公益红娘转介)、Phase 13A(婚恋咨询与婚前辅导)、Phase 14(多类型公益活动)、Phase 15(活动报名、审核与签到)复验已关闭**,九轮共修复 64 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 16 需用户确认后启动。**
> 阶段 0(在途收口)、阶段 1(小程序 C 组 + discover 筛选重设计)、阶段 2(管理端 Vue 新框架适配)、阶段 3(CapabilityService 独立化)、阶段 4(双端总验收)全部完成。三端构建通过、**13/13 契约全绿**、核心 11 页面**全部 TS-only(11/11)**。**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)、Phase 13(公益红娘转介)、Phase 13A(婚恋咨询与婚前辅导)、Phase 14(多类型公益活动)、Phase 15(活动报名、审核与签到)、Phase 16(活动互选与双方开启沟通确认)复验已关闭**,十轮共修复 71 处真实缺陷(含 1 处隐藏编译缺陷),并各自补齐测试保真度与守卫。**Phase 17 需用户确认后启动。**
最后更新: 2026-09-17 — 分支 `main`(`feat-ui-v11` 保留为冗余锚点)
@@ -45,6 +45,7 @@
| 1.1.0-rc.5 | 2026-09-17 | Phase 13A 复验关闭(婚恋咨询与婚前辅导,9 处缺陷 + 4 处测试基建缺陷);新增 V039;全量回归 **420 tests** 全绿 |
| 1.1.0-rc.6 | 2026-09-17 | Phase 14 复验关闭(多类型公益活动,11 处后端缺陷 + 3 处前端契约缺陷 + 2 处测试保真度缺陷);新增 `ActivityManagementView` 动态表单;全量回归 **450 tests** 全绿 |
| 1.1.0-rc.7 | 2026-09-17 | Phase 15 复验关闭(活动报名/审核/签到,16 处缺陷:含**签到码一生成即作废**、**取消后永远无法再报名**两处必崩;3 处测试保真度);新增 V040 `activity_staff_assignments` 与 `ActivityCheckinView` 签到工作台;全量回归 **474 tests** 全绿 |
| 1.1.0-rc.8 | 2026-09-17 | Phase 16 复验关闭(活动互选与双方开启沟通确认,7 处缺陷 + 1 处隐藏编译缺陷 + 1 处测试保真度:含**双确认后根本不建 Match/会话(Gate 缺陷)**、**Phase 15 提交态引用未定义 `reopenEpisode` 致模块不编译("474 绿"为假象)**、**INV-006 单向意向泄露**、**复用/新建 Match 后缺会话**;统一双实现到 `RelationshipService.confirmActivityInterest`;新增 V041 `expires_at`+`match_id`;新建 `ActivityMutualConsentServiceTest`(7 用例);全量回归 **481 tests** 全绿 |
---
@@ -222,6 +223,26 @@
| 测试:`ActivityPhase15Test`(21)+ `CheckinServiceTest` 假绿改真实守卫(5)+ 约束守卫(2) | ✅ 新增 |
| 全量回归 **474 tests 全绿** | ✅ |
### Phase 16 — 活动互选与双方开启沟通确认 ✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验通过)
| 任务 | 状态 |
|------|------|
| **D1(Critical,Gate 缺陷 · INV-014/015)** 双确认后不建 Match/会话 | ✅ 重写 `confirmConnection` 走 `confirmActivityInterest`,exactly-once(`both_confirmed_at` NULL→非NULL 仅一方赢家建 Match+会话并 `bindMatch`) |
| **D2(High,双实现分裂)** 旧/新路径各自为政 | ✅ 统一到 `RelationshipService.confirmActivityInterest`(返回 `matchId` + `priorUnmatch`) |
| **D3(High,隐藏编译缺陷 · 必崩级)** Phase 15 引用未定义 `matchMapper.reopenEpisode` | ✅ `MatchMapper` 补 `@Update reopenEpisode`(`WHERE status='unmatched' AND deleted_at IS NULL`);**Phase 15 提交态从未真正编译通过** |
| **D4(High,INV-006 泄露)** 本侧未确认即回显对方确认态 | ✅ `getMyConsentStatus` 改 `partnerAccepted = myAccepted && partnerSideAccepted` |
| **D5(Medium,缺会话)** 复用/新建 Match 后未显式开启会话 | ✅ `confirmActivityInterest` 显式 `getOrCreateConversation` 并返回 `conversationAutoCreated=true` |
| **D6(Medium,测试库零覆盖)** 缺 `activity_mutual_connection_consents` 表 | ✅ `schema-h2.sql` 补表(对齐 V024+V037+V041);`EntityMigrationParityTest` 移入 `REQUIRED_IN_TEST_SCHEMA`;`MigrationValidationTest` 基线 40→41 |
| **D7(Medium,无互选测试)** | ✅ 新建 `ActivityMutualConsentServiceTest`(7 用例,走真实方法路径) |
| **D8(Medium,断言与 #20 冲突)** `ChatPhase09IntegrationTest:344` 假设每次新建独立会话 | ✅ 改为 `assertEquals`(认可 AGENTS.md #20 复用语义),保留"复用同一消息管线"主断言 |
| 负向验证(注释 createMatchAndConversation → 2 用例精准变红并还原) | ✅ 守卫有牙 |
| 新增 V041 `expires_at`+`match_id` 迁移 | ✅ |
| 全量回归 **481 tests 全绿** | ✅ |
> **2026-09-17 复验结论(KEEP-VERIFY/EXTEND)**:历史 ✅ 不可信。活动互选存在**核心 Gate 缺陷**——双确认后根本不建 Match/会话(INV-014/015);
> 且 Phase 15 提交态引用未定义的 `reopenEpisode`,**整个模块从未编译通过**,"474 绿"是假象。已统一双实现到
> `RelationshipService.confirmActivityInterest`,修复 7 处缺陷 + 1 处隐藏编译缺陷 + 1 处测试保真度缺陷,新建 7 用例互选测试,
> 并经负向验证证明守卫有牙。报告:`docs/Phase-16-V11-Recovery-Completion-Report-2026-09-17.md`
### Phase 13A — 公益婚恋咨询与婚前辅导(核心链路)✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验通过)
| 任务 | 状态 |
|------|------|
@@ -414,16 +414,43 @@ D2——`RelationshipService.createMatch` 不写 episode/lastActivityAt,与 `M
> 报告:`docs/Phase-15-V11-Recovery-Completion-Report-2026-09-17.md`
> **Phase 16 未自动进入,等待用户确认。**
### Phase 16:活动互选与双方开启沟通确认 — REFACTOR
### Phase 16:活动互选与双方开启沟通确认 — REFACTOR ✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验关闭)
**Files:** activity_mutual、relationship、messaging 模块及 activity-mutual 页面。
- [ ] 以 activity_user_interests + activity_mutual_connection_consents 为唯一逻辑,废弃 mutual 直接建 Match/Chat 的所有入口。
- [ ] 双方确认事务中:已有 active Match 则复用已有会话;历史 Unmatch 则创建新 Episode;无历史则创建 Match 和 Conversation。
- [ ] 补 decline、timeout、重复确认幂等、并发确认和单向意向保密测试。
- [x] 以 activity_mutual_connection_consents 为唯一逻辑,双实现统一到 `RelationshipService.confirmActivityInterest`。
- [x] 双方确认事务中:已有 active Match 则复用已有会话;历史 Unmatch 则复用同一行(episode 递增 + reopen);无历史则创建 Match 和 Conversation。
- [x] 补 decline、expires_at 超时、重复确认幂等、并发确认(exactly-once)和单向意向保密(INV-006)测试。
**Gate:** A 单选 B 时 B 无感知;Mutual 但未双确认时无新 Chat;双确认后 exactly-once 形成正确 Match/Conversation。
> **2026-09-17 复验结论(KEEP-VERIFY/EXTEND):** 历史 ✅ 不可信,逐条核对运行时路径后发现**活动互选核心 Gate 缺陷**——
> 新路径 `ActivityMutualConsentController.confirmConnection` 双确认后**根本不建 Match/会话**(违反 INV-014/015),而旧路径
> `ActivityInterest.confirmInterest`→`confirmActivityInterest` 建 Match 却**缺会话**;两条路径分裂。已统一到
> `RelationshipService.confirmActivityInterest` 单实现。修复 **7 处缺陷 + 1 处隐藏编译缺陷 + 1 处测试保真度缺陷**:
> - **D1(Critical,Gate 缺陷 · INV-014/015)** 双确认后不建 Match/会话 → 重写为 `confirmConnection` 走
> `confirmActivityInterest`,由原子 `confirmSideIfPending` 保证 exactly-once(`both_confirmed_at` NULL→非NULL 仅一方赢家建 Match+会话并 `bindMatch` 溯源);单方确认不建 Match。
> - **D2(High,双实现分裂)** 旧/新路径各自为政 → 统一到 `RelationshipService.confirmActivityInterest`(返回 `matchId` + `priorUnmatch`)。
> - **D3(High,隐藏编译缺陷 · 必崩级)** Phase 15 提交态引用 `matchMapper.reopenEpisode` 但该方法**从未定义** → 整个
> activity_mutual / relationship 模块**根本不编译**,**"474 绿"基线从未真实存在**。修复:在 `MatchMapper` 补 `@Update reopenEpisode`
> (`WHERE status='unmatched' AND deleted_at IS NULL`)。**这是本轮最严重发现:Phase 15 的提交态从未通过编译。**
> - **D4(High,INV-006 泄露)** `getMyConsentStatus` 本侧未确认即回显对方确认状态 → 改为
> `partnerAccepted = myAccepted && partnerSideAccepted`(本侧未确认则对方确认态不回显)。
> - **D5(Medium,缺会话)** 复用/新建 Match 后未显式开启会话 → `confirmActivityInterest` 现在**显式**调用
> `chatService.getOrCreateConversation` 并返回 `conversationAutoCreated=true`。
> - **D6(Medium,测试库零覆盖)** `schema-h2.sql` 缺 `activity_mutual_connection_consents` 表 → 补表
> (含 `deleted_at`/`expires_at`/`match_id` 与 `chk_consent_status` CHECK、UNIQUE(activity_id,user_a_id,user_b_id)),与 V024+V037+V041 对齐;
> `EntityMigrationParityTest` 将其从 `TEST_SCHEMA_OPTIONAL_TABLES` 移入 `REQUIRED_IN_TEST_SCHEMA`;`MigrationValidationTest` 基线 40 → 41。
> - **D7(Medium,无互选测试)** 新建 `ActivityMutualConsentServiceTest`(7 用例:双确认建 Match+会话+溯源、INV-006 单向不暴露、
> decline 阻止、幂等重确认不重复建、expires_at 超时拒绝、软删过滤、复用 active Match),`@SpringBootTest` 走真实方法路径。
> - **D8(Medium,断言与 #20 冲突)** `ChatPhase09IntegrationTest:344` 假设"每次活动互选新建独立会话",与 AGENTS.md #20
> "同对用户 Unmatch 后重连复用同一 Match 行、会话随之复用"冲突 → 改为 `assertEquals`(认可复用),保留 sendText/WebSocket/未读主断言以验证"复用同一消息管线"。
> - **负向验证**:临时注释 `createMatchAndConversation(consent)` 调用 → `ActivityMutualConsentServiceTest` 中 doubleConfirm + idempotent
> 两条依赖 Match 的用例**精准变红**(`Tests run: 7, Failures: 2`)→ 证明守卫有牙 → 已还原(`grep -c "NEG-VAL"` = 0)。
> - 新增迁移 `V041__extend_mutual_consent_expiry.sql`(`expires_at` + `match_id` + 索引)。
> - 全量回归 **481 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(约 5 分)。基线 474 → 481(净 +7)。
> - 报告:`docs/Phase-16-V11-Recovery-Completion-Report-2026-09-17.md`。**Phase 17 需用户确认后启动。**
### Phase 17:举报与风控 — EXTEND
**Files:** risk、report、referral 模块和后台风险页面。