# Phase 16 完成报告:活动互选二阶段同意重构 > 版本基线:v1.0.0-RC.2 → v1.1.0-dev | 日期:2026-09-08 > 对应 EPIC:EPIC-P16-V11 --- ## 完成摘要 | 项目 | 内容 | |------|------| | Phase | 16 | | 模块 | 活动互选二阶段同意重构 | | 状态 | ✅ 核心链路已完成(V1.1 MVP) | | 不变量 | INV-014/INV-015 强制执行 | --- ## 核心变更 ### 数据模型(V024) **新建 `activity_mutual_connection_consents` 表:** - 每对用户在同一个活动中有一条独立记录 - 分别追踪 `user_a_status` 和 `user_b_status`(pending/accepted/declined) - 双方都 accepted 时设置 `both_confirmed_at` - UNIQUE 约束使用 `LEAST/GREATEST` 保证 (A,B) 和 (B,A) 是同一对 ### 不变量强制实现 | 不变量 | 实现方式 | |--------|----------| | INV-014: Activity Mutual 不能直接创建 Match | `confirmConnection()` 只更新 consent 状态,不自动创建 Match | | INV-015: 双方确认后才创建 Activity Match | `confirmActivityInterest()` 在 RelationshipService 中调用,仅在 bothConfirmed 时触发 | | INV-010: 复用已有 active Match | `hasActiveMatch()` 检查优先于新建 | | INV-010: prior Unmatch 新建 episode | `findLatestMatch()` 检查是否曾 unmatch | ### API 端点 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/activities/{id}/mutual/results` | 检测双向互选结果 | | POST | `/activities/{id}/mutual/consent` | 创建互选同意记录 | | POST | `/activities/{id}/mutual/confirm` | 用户确认开启沟通 | | POST | `/activities/{id}/mutual/decline` | 用户拒绝 | | GET | `/activities/{id}/mutual/status/{partnerId}` | 查询个人同意状态 | | GET | `/mutual/my-results` | 获取我的互选结果列表 | ### Miniapp 页面 | 页面 | 编号 | 功能 | |------|------|------| | activity-mutual-result | U60 | 展示所有互选结果,已确认可开聊 | | activity-mutual-waiting | U61 | 单对互选详情页,支持确认/拒绝/等待状态 | --- ## 前端状态流转 ``` [未表达] → [双方互选产生] → [等待确认] → [用户A确认] → [用户B确认] → [Match创建,可聊天] ↓ ↓ [用户A拒绝] [用户B拒绝] → [单向关闭] ``` --- ## 待补充 | 任务 | 阶段 | 说明 | |------|------|------| | T13 timeout handling | 后续 | 互选超时自动过期逻辑 | | Admin Web 互选管理 | Phase 19 | 运营后台查看互选统计 | | E2E 测试补全 | Phase 23 | 完整两阶段流程测试 | --- *Phase 16 核心链路完成。下一阶段:Phase 17(举报与风控扩展)*