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
@@ -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 模块和后台风险页面。