fix(matchmaker,referral): close Phase 13 KEEP-VERIFY/EXTEND — 7 real defects

KEEP-VERIFY/EXTEND 复验(历史 ✅ 不可信;测试覆盖函数 ≠ 覆盖运行时路径)。
逐条核对运行时路径与生产约束后修复 7 处缺陷,其中 3 处「只在真实 PostgreSQL 上炸」。

D1 (Critical, 生产故障)
  matchmaker_recommendations.status 代码写 liked/passed/confirmed_mutual,
  而 V009 chk_rec_status 只允许 pending_view/viewed/responded 且从未扩展
  → 真实 PG 上用户接受/跳过推荐必崩。测试库未复刻该 CHECK,故长期静默。

D2 (Critical, 生产故障)
  控制器缺省 serviceType="recommendation" 不在 chk_matchmaker_service_type
  白名单 → 提交红娘服务必崩。
  D1+D2 同修于新增 V038__align_matchmaker_status_constraints.sql
  (沿用 V018/V020/V025 的 DROP CONSTRAINT IF EXISTS + 重加白名单惯例)。

D3 (High, 越权写)
  MatchmakerServiceService.recommendPartner 无 assignedOperatorId 归属校验,
  且可把用户推荐给自己 → 补校验 + 禁止自我推荐。

D4 (High, 越权读)
  getServiceDetail 无归属校验且返回完整用户资料(userId/description/
  userProfile/followups)→ 签名加 requesterId,按调用者返回完整/脱敏视图。

D5 (High, 越权写)
  ReferralService 的 userId 取自请求体且零授权校验 → 新增
  assertMatchmakerServesUser,在 referToServiceStation/referToActivity 入口
  强制校验存在进行中的服务关系。

D6 (High, 生产故障)
  MatchmakerFollowupMapper.findByServiceId 只有声明无 @Select
  → Invalid bound statement (not found),getServiceDetail 必崩。
  该缺陷由本轮新增测试首次暴露(历史测试从未调用该路径)→ 补 @Select。

D7 (Medium, 契约缺口)
  admin-web 5 处 API 缺口(4 条 404 + C 端端点被运营端误用)+ 小程序缺转介服务层
  → 后端补 GET /operator/matchmaker/recommendations 与
  GET /referrals/operator/history 两个运营端端点;前端改路径、
  assign 改 POST + body operatorId;新增 miniapp/services/referralService.js。

测试保真度
  - 测试库补 matchmaker_services/recommendations/followups 三表(此前该模块零真实覆盖),
    约束与 V038 对齐;EntityMigrationParityTest 三表移入 REQUIRED_IN_TEST_SCHEMA;
    MigrationValidationTest LATEST_MIGRATION 37 → 38。
  - 新增 MatchmakerReferralPhase13Test(17 用例,@SpringBootTest 走真实方法路径 + 真实落库)。
  - 重写 MatchmakerServiceTest(6 用例):直接解析 db/migration/V*.sql,按文件名顺序应用
    DROP CONSTRAINT IF EXISTS + ADD CONSTRAINT 取"最终有效"白名单,并内置解析器自检。
    【关键】首版守卫硬编码白名单副本,注入 D1 后仍全绿(对漂移零反应)→ 必须解析真实 DDL。
  - 删除 3 处假测试占位(MatchmakerServiceTest.testMatchmakerCannotLike 的 assertTrue(true);
    CoreInvariantsTest INV-007 断言的是状态常量、INV-010 亦为 assertTrue(true)),
    已如实改名并注明行为验证位置。

负向验证(两轮)
  注入 D1(回退 chk_rec_status 白名单)→ 行为守卫 DataIntegrityViolation
  + 常量守卫 Multiple Failures (3 failures) → Tests run: 23, Failures: 1, Errors: 2
  / BUILD FAILURE,守卫有效性得证。缺陷已还原。

全量回归(离线容器,实测)
  [WARNING] Tests run: 391, Failures: 0, Errors: 0, Skipped: 3
  [INFO] BUILD SUCCESS / Total time: 04:59 min
  Phase 12 基线 371 → 391(净 +20)。

报告:docs/Phase-13-V11-Recovery-Completion-Report-2026-09-16.md
Phase 13A 未自动进入,等待用户确认。
This commit is contained in:
WorkBuddy
2026-09-17 00:01:53 +08:00
parent c607e87b45
commit 5b3bad326e
21 changed files with 1336 additions and 71 deletions
+22 -3
View File
@@ -5,6 +5,24 @@
> **2026-09-16 状态校正:** Phase 07 UI 已按 2026-09-13 规格重新实现,**首批 11 页于 2026-09-16 通过用户验收(结论:基本达标)**,本文件此前"视觉否决/REOPENED"口径随之关闭。
>
> **2026-09-16 Phase 13 复验关闭:** EPIC-P13-V11 已完成。复验中发现并修复 7 处缺陷——
> **D1(Critical,生产故障)** `matchmaker_recommendations.status` 代码写入 `liked`/`passed`/`confirmed_mutual`,
> 而生产 `V009 chk_rec_status` 只允许 `pending_view/viewed/responded` → **真实 PG 上用户接受/跳过推荐必崩**;
> **D2(Critical,生产故障)** 控制器缺省 `serviceType="recommendation"` 不在 `chk_matchmaker_service_type` 白名单
> → **提交红娘服务必崩**。两者同修于新增 `V038__align_matchmaker_status_constraints.sql`。
> **D3(High,越权写)** `recommendPartner` 无归属校验 → 任意红娘可替非本人负责的服务发起推荐、可推荐给自己。
> **D4(High,越权读)** `getServiceDetail` 无归属校验且返回完整用户资料 → 任意红娘可读任意用户敏感详情。
> **D5(High,越权写)** `ReferralService` 的 `userId` 取自请求体且**零授权校验** → 可替任意用户发起转介。
> **D6(High,生产故障)** `MatchmakerFollowupMapper.findByServiceId` 无 `@Select` → `Invalid bound statement`,
> `getServiceDetail` 必崩(**本轮新增测试首次暴露**)。**D7(Medium)** admin-web 5 处 API 契约缺口(404 / C 端端点误用)
> + 小程序缺转介服务层 → 后端补 2 个运营端端点、前端改路径、小程序新增 `referralService.js`。
> 测试库补 3 张 matchmaker 表(`EntityMigrationParityTest` 移入必需集),迁移基线 37 → 38。
> **关键教训**:首版常量守卫**硬编码白名单副本**,注入 D1 缺陷后**仍全绿** → 重写为**直接解析 `db/migration/V*.sql`**
> (按文件名顺序取"最终有效"白名单 + 解析器自检);修正后经**第二轮负向验证**证明会红
> (`Tests run: 23, Failures: 1, Errors: 2 / BUILD FAILURE`)。删除 3 处假测试占位。
> 证据:`docs/Phase-13-V11-Recovery-Completion-Report-2026-09-16.md`。**Phase 13A 需用户确认后启动。**
> 后端全量 **391 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(4 分 59 秒)。
>
> **2026-09-16 Phase 12 复验关闭:** EPIC-P12-V11 已完成。复验中发现并修复 4 处缺陷——
> **D1(Critical,生产故障 · 同一族系统性缺陷)** 实体与生产迁移**系统性漂移**:23 张表、50 个列在 `db/migration` 中
> 根本不存在,而测试库 `schema-h2.sql` 手写了它们 → 全量测试绿 = **假绿**。其中 11 张表属"INSERT 必崩"级
@@ -19,7 +37,7 @@
> 新增永久守卫 `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 需用户确认后启动。**
> 证据:`docs/Phase-12-V11-Recovery-Completion-Report-2026-09-16.md`。
>
> **2026-09-16 Phase 11 复验关闭:** EPIC-P11-V11 已完成。复验中发现并修复 3 处缺陷——
> **D3(Critical,生产故障)** `NotificationService` 全部 10 种通知使用复合 type(`appointment_submitted` /
@@ -60,7 +78,8 @@
| 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 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 尚未获准 |
| 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 公益红娘转介复验 | 已完成(2026-09-16) | 见 `Phase-13-V11-Recovery-Completion-Report-2026-09-16.md`;修复 7 处缺陷(**2 处生产故障级 CHECK 约束缺口致推荐接受/服务提交必崩**、推荐与转介三处越权、`getServiceDetail` 越权读、Mapper 缺 `@Select` 致必崩、前端 5 处契约缺口),测试库补 3 张 matchmaker 表,迁移基线 V038;新增 `MatchmakerReferralPhase13Test`(17 用例)+ 重写 `MatchmakerServiceTest`(6 用例,解析真实 DDL),删除 3 处假测试,后端 **391 tests 全绿**;**Phase 13A 尚未获准** |
| 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 通过用户验收 |
@@ -94,7 +113,7 @@
| EPIC-P10-V11 | P10 | 聊天安全扩展 | P0 | ✅
| EPIC-P11-V11 | P11 | 公益服务通知扩展 | P0 | ✅
| EPIC-P12-V11 | P12 | 关系状态轻量扩展 | P1 | ✅
| EPIC-P13-V11 | P13 | 红娘服务转介扩展 | P1 |
| EPIC-P13-V11 | P13 | 红娘服务转介扩展 | P1 | ✅
| EPIC-P13A | P13A | 公益婚恋咨询与婚前辅导 | P1 |
| EPIC-P14-V11 | P14 | 多类型公益活动 | P1 |
| EPIC-P15-V11 | P15 | 活动签到回归测试 | P1 |
@@ -0,0 +1,269 @@
# Phase 13 V1.1 Recovery Completion Report — 公益红娘转介
> 日期:2026-09-16
> 阶段:Phase 13(Matchmaker Referral — KEEP-VERIFY/EXTEND)
> 分支:`main` | Tag:`phase13-recovery-2026-09-16`
> 结论:**CLOSED(Gate 全项通过)**
---
## 一、计划目标与 Gate
计划出处:`docs/善缘丹阳 V1.1 客户需求基线增量开发与修复计划.md` 第 256-264 行。
| 计划项 | 要求 | 结论 |
|---|---|---|
| 计划项 1 | 保留红娘服务和人工推荐;验证任何工作人员不能替用户 Like | ✅ 通过(替换假测试,6 用例行为验证) |
| 计划项 2 | 补齐转介咨询、活动、服务站的用户同意、来源追踪和状态记录 | ✅ 通过(6 用例,真实落库校验) |
| 计划项 3 | 红娘只能看转介状态和必要摘要,不能读取 counseling followup 正文 | ✅ 通过(3 用例 + 越权防护) |
**Gate:** 转介形成可追踪记录但不越权;用户决定权测试通过。
| Gate 断言 | 证据 | 结果 |
|---|---|---|
| 转介形成可追踪记录 | `referToActivityCreatesTrackableRecord` / `referToServiceStationCreatesTrackableRecord`:`source=matchmaker_referral`、`requestSource` 同名、`createdByOperatorId` 落库 | ✅ |
| 转介不越权(用户决定权) | `otherMatchmakerCannotReferUser` / `otherUserCannotConfirmReferral` / `nonReferralCannotBeConfirmedAsReferral` | ✅ |
| 红娘不可替用户 Like | `matchmakerRecommendationDoesNotCreateLike` / `matchmakerCannotAcceptOnBehalfOfUser` / `onlyUserCanLikeViaRecommendation` | ✅ |
| 只读摘要不读正文 | `nonOwnerSeesSanitizedDetail` / `matchmakerDetailNeverExposesCounselingFollowupContent` | ✅ |
---
## 二、复验发现:历史证据全为假测试
Phase 13 历史文档声称「**INV-005 红娘不可代用户操作验证 ✅**」。复验发现该结论**没有任何真实证据支撑**:
| 声称的验证 | 实际情况 |
|---|---|
| `MatchmakerServiceTest#testMatchmakerCannotLike` | `assertTrue(true); // 占位测试` —— 零覆盖 |
| `CoreInvariantsTest#testMatchmakerCannotLike` | 断言 `MATCHMAKER_STATUS_SUBMITTED/ASSIGNED/IN_SERVICE/COMPLETED` 四个**状态常量字符串**,与「红娘不能替用户 Like」**毫无关系**(名不副实的假测试) |
| 测试库覆盖 | `schema-h2.sql` **完全缺失** `matchmaker_services` / `matchmaker_recommendations` / `matchmaker_followups` 三表 → 该模块**零真实行为覆盖** |
另发现同类:`CoreInvariantsTest#testCrossOrgNoAccess`(INV-010)也是 `assertTrue(true)`。
---
## 三、缺陷清单(D1~D7)
### D1 — Critical(生产故障):推荐状态 CHECK 约束与代码写入值漂移
- **DDL**:`V009__add_matchmaker_tables.sql:55-57` 的 `chk_rec_status` 只允许
`pending_view` / `viewed` / `responded`,且**全迁移目录无任何 ALTER/DROP 扩展它**。
- **代码**:`MatchmakerService.java:225/250` 写入 `liked` / `passed` / `confirmed_mutual`。
- **后果**:真实 PostgreSQL 上用户点击「接受/跳过」红娘推荐 → 违反 CHECK → **必然 500**。Phase 13 主流程不可用。
- **修复**:`V038` 用 `DROP CONSTRAINT IF EXISTS` + 重加白名单(含 `liked`/`passed`/`confirmed_mutual`)。
### D2 — Critical(生产故障):service_type 缺省兜底值越界
- **DDL**:`V009:26-29` 的 `chk_matchmaker_service_type` 只允许
`recommend_partner` / `review_preference` / `contact_intention` / `join_activity` /
`no_match_help` / `chat_help` / `other`。
- **代码**:`MatchmakerController.java:47-48` 在请求未带 `serviceType` 时兜底写 `"recommendation"`;
`MatchmakerService.java:57-61` 注释声明 `referral_counseling` / `referral_activity` / `referral_station`。
- **后果**:真实 PG 上「提交红娘服务」(缺省路径)与三种转介子类型**必然 500**。
- **修复**:`V038` 扩展白名单纳入这 4 个值。
### D3 — High:`recommendPartner` 缺归属校验(越权推荐)
`MatchmakerServiceService.recommendPartner` 只校验 `service != null`,**无** `assigned_operator_id` 校验
→ 任何持 `matchmaker.service` 权限的 operator 可对**任意** serviceId 创建推荐。
(对比 `MatchmakerService.createRecommendation:118-121` 有校验 —— 同项目双路径口径不一致。)
**修复**:补归属校验 + 「不得推荐用户给自己」。
### D4 — High:`getServiceDetail` 无归属校验(越权读)
任何持 `matchmaker.view` 的 operator 可读**任意**服务详情,含完整用户资料
(displayName / gender / age / regionName / education / occupation / maritalStatus)。
**修复**:改为按调用者授权范围返回 —— 负责人得完整详情;非负责人得脱敏视图
(无 `userProfile`、无 `userId`、无 followup 正文,仅 `followupCount` 摘要)。
### D5 — High:转介无授权校验(越权代操作)
`ReferralService.referToActivity` / `referToServiceStation` 的 `userId` **直接取自请求体**,
`ReferralService` 甚至**未注入** `MatchmakerServiceMapper` —— 零授权校验。
任何红娘可把**任意用户**转介到任意活动/服务站,并顺带为该用户创建
`activity_registrations` / `service_appointments` 记录。
**修复**:新增 `assertMatchmakerServesUser`,断言存在进行中的
`matchmaker_service(assigned_operator_id=红娘, user_id=目标用户)`,否则拒绝。
### D6 — High(生产故障):`MatchmakerFollowupMapper.findByServiceId` 缺 `@Select`
```java
// 修复前 —— 只有声明,MyBatis 无法绑定
List<MatchmakerFollowup> findByServiceId(String serviceId);
```
运行时抛 `Invalid bound statement (not found):
...MatchmakerFollowupMapper.findByServiceId`。调用方
`MatchmakerServiceService.getServiceDetail:311` 因此**必然 500** ——
红娘工作台「查看服务详情」完全不可用。
此前不可见:测试库缺 `matchmaker_followups` 表,无任何测试能走到这条路径。
**修复**:补 `@Select` 注解(含 `deleted_at IS NULL` 过滤 + `@Param`)。
### D7 — Medium:Admin referral 前端契约缺口
| admin-web 调用 | 后端实际 | 结果 |
|---|---|---|
| `GET /referrals/my-history`(ReferralManagementView) | 该端点用 `UserPrincipal`(C 端) | 运营端无"我的用户身份" |
| `GET /matchmaker/services/operator/queue` | `GET /operator/matchmaker/pending` | 404 |
| `GET /matchmaker/recommendations/operator` | **不存在** | 404 |
| `PUT /matchmaker/services/{id}/assign` | `POST /operator/matchmaker/services/{id}/assign` | 404(方法+前缀均不符) |
| `POST /matchmaker/services/{id}/recommendation` | `POST /operator/matchmaker/services/{id}/recommend` | 404(路径+前缀不符) |
**修复**:后端新增 `GET /referrals/operator/history`(按 `created_by_operator_id` 过滤)
与 `GET /operator/matchmaker/recommendations`(限本人名下服务);前端 5 处调用改为真实路径。
---
## 四、测试保真度
### 假测试清理
| 文件 | 问题 | 处理 |
|---|---|---|
| `MatchmakerServiceTest` | 含 `assertTrue(true); // 占位测试` | 重写为**直接解析迁移 SQL** 的口径守卫(6 用例) |
| `CoreInvariantsTest#testMatchmakerCannotLike` | DisplayName 称"红娘不能替用户 Like",实际断言 `MATCHMAKER_STATUS_*` 常量 | DisplayName 如实改为"常量口径 INV-007: 红娘服务状态取值固定",并指向真实验证位置 |
| `CoreInvariantsTest#testCrossOrgNoAccess` | `assertTrue(true)` | 改为有意义的常量断言 + 注明行为验证待 Phase 19 |
| `CoreInvariantsTest` 类头 | —— | 新增注释说明本类**只做常量口径守卫,不验证行为**,并列出各 INV 的真实验证位置 |
### 新增真实行为测试 `MatchmakerReferralPhase13Test`(17 用例)
走**真实服务方法 + 真实落库**(`@SpringBootTest` + `@Transactional`):
| 分组 | 用例 |
|---|---|
| 计划项 1(红娘不代 Like) | `matchmakerRecommendationDoesNotCreateLike`、`onlyUserCanLikeViaRecommendation`、`matchmakerCannotAcceptOnBehalfOfUser`、`userPassViaRecommendation` |
| 计划项 2(同意/来源/状态) | `referToActivityCreatesTrackableRecord`、`referToServiceStationCreatesTrackableRecord`、`userConfirmsActivityReferral`、`userConfirmsServiceStationReferral`、`otherUserCannotConfirmReferral`、`nonReferralCannotBeConfirmedAsReferral` |
| 越权防护(D3/D5) | `otherMatchmakerCannotReferUser`、`otherMatchmakerCannotRecommend`、`ownerMatchmakerCanRecommend`、`cannotRecommendUserToSelf` |
| 计划项 3(只读摘要) | `nonOwnerSeesSanitizedDetail`、`ownerSeesFullDetail`、`matchmakerDetailNeverExposesCounselingFollowupContent` |
> 注:`createUser` 会补齐 `user_profiles`(approved)+ `user_identities`(verified),
> 使 `capabilityService.isBaseEligible` 通过,从而让「用户接受推荐 → 产生 liked」这条
> 真实路径可以走通(第一版测试因缺这两张表而假红)。
### 测试库补齐 `matchmaker_*` 三表
`schema-h2.sql` 新增三表,**约束与 V038 对齐**(含扩展后的 `chk_rec_status` /
`chk_matchmaker_service_type`)。同步从 `EntityMigrationParityTest.TEST_SCHEMA_OPTIONAL_TABLES`
白名单移除这三张表,并加入 `REQUIRED_IN_TEST_SCHEMA`——守卫从此**强制**它们必须存在。
### 两轮负向验证(守卫有效性证明)
**注入缺陷**:把 `chk_rec_status` 回退到 V009 原白名单(DDL + 测试库同步收窄)。
**第一轮 —— 暴露守卫缺口:**
| 守卫 | 结果 |
|---|---|
| `MatchmakerReferralPhase13Test.onlyUserCanLikeViaRecommendation` | ✅ `DataIntegrityViolation`(精准变红) |
| `MatchmakerReferralPhase13Test.userPassViaRecommendation` | ✅ `DataIntegrityViolation` |
| `MatchmakerServiceTest`(**抄副本版**常量守卫) | ❌ **仍然全绿** —— 它比对的是一份与 DDL 脱钩的常量副本,对漂移无反应 |
**修正**:把常量守卫改为**直接解析 `db/migration/V*.sql`**,按文件名顺序处理
`DROP CONSTRAINT IF EXISTS` + `ADD CONSTRAINT`,取"最终有效"白名单(解析器另有自检用例
`parserMustActuallyResolveConstraint` 防止守卫因解析失效而永真)。
**第二轮 —— 守卫全部精准变红:**
```
[ERROR] MatchmakerServiceTest.recommendationStatusConstantsAreWithinProductionWhitelist:112
Multiple Failures (3 failures)
[ERROR] MatchmakerReferralPhase13Test.onlyUserCanLikeViaRecommendation:149 » DataIntegrityViolation
[ERROR] MatchmakerReferralPhase13Test.userPassViaRecommendation:178 » DataIntegrityViolation
Tests run: 23, Failures: 1, Errors: 2
```
还原缺陷后 6/6 + 17/17 恢复全绿。
---
## 五、测试结果
### 定向测试(还原缺陷后)
| 测试类 | 结果 |
|---|---|
| `MatchmakerReferralPhase13Test` | 17/17 ✅ |
| `MatchmakerServiceTest` | 6/6 ✅ |
| `CoreInvariantsTest` | 12/12 ✅ |
| `MigrationValidationTest` | 12/12 ✅ |
| `EntityMigrationParityTest` | 5/5 ✅ |
| **合计** | **51 tests / 0 failures / 0 errors** |
### 全量回归
离线容器(`danyang-match-backend-test:latest`)实测,`mvn -o test` 汇总行:
```
[WARNING] Tests run: 391, Failures: 0, Errors: 0, Skipped: 3
[INFO] BUILD SUCCESS
[INFO] Total time: 04:59 min
```
| 项 | 值 |
|---|---|
| 用例总数 | **391**(Phase 12 基线 371 → +20) |
| Failures / Errors | **0 / 0** |
| Skipped | 3 |
| 结果 | **BUILD SUCCESS**(4 分 59 秒) |
> **计数说明(沿用 AGENTS.md 纪律:以实测为准,不预估)**:本轮新增 23 例(`MatchmakerReferralPhase13Test` 17 +
> `MatchmakerServiceTest` 重写后 6),同时删除 3 处占位断言,净增 **+20**(371 → 391)。
> 汇总行是 `[WARNING]` 而非 `[INFO]`(因含 skipped 用例),grep 时不可只匹配 `^\[INFO\]`。
---
## 六、变更文件清单
| 文件 | 类型 |
|---|---|
| `backend/src/main/resources/db/migration/V038__align_matchmaker_status_constraints.sql` | 新增 |
| `backend/src/test/java/.../matchmaker/MatchmakerReferralPhase13Test.java` | 新增(17 用例) |
| `backend/src/test/java/.../matchmaker/MatchmakerServiceTest.java` | **重写**(删假测试,改为解析 DDL 的守卫) |
| `backend/src/test/java/.../invariants/CoreInvariantsTest.java` | 修改(假测试如实改名) |
| `backend/src/test/resources/schema-h2.sql` | 修改(补 `matchmaker_*` 三表) |
| `backend/src/main/java/.../matchmaker/mapper/MatchmakerFollowupMapper.java` | 修改(D6:补 `@Select`) |
| `backend/src/main/java/.../matchmaker/mapper/MatchmakerRecommendationMapper.java` | 修改(新增 `findByOperatorId`) |
| `backend/src/main/java/.../matchmaker/mapper/MatchmakerServiceMapper.java` | 修改(新增 `findActiveByOperatorAndUser`) |
| `backend/src/main/java/.../matchmaker/service/MatchmakerServiceService.java` | 修改(D3/D4) |
| `backend/src/main/java/.../matchmaker/controller/MatchmakerOperatorController.java` | 修改(D4/D7) |
| `backend/src/main/java/.../referral/service/ReferralService.java` | 修改(D5/D7) |
| `backend/src/main/java/.../referral/controller/ReferralController.java` | 修改(D7 新增运营端端点) |
| `admin-web/src/modules/matchmaker/MatchmakerManagementView.vue` | 修改(D7 契约修正) |
| `admin-web/src/modules/referral/ReferralManagementView.vue` | 修改(D7 契约修正) |
| `miniapp/services/referralService.js` | 新增(转介确认客户端) |
| `backend/src/test/java/.../deployment/EntityMigrationParityTest.java` | 修改(白名单迁移) |
| `backend/src/test/java/.../deployment/MigrationValidationTest.java` | 修改(`LATEST_MIGRATION` 37→38) |
| `docs/STATUS.md`、`docs/JIRA_BACKLOG.md`、本报告 | 文档 |
---
## 七、遗留与 gap(登记,不自动实现)
| 项 | 说明 |
|---|---|
| **miniapp 转介确认 UI 缺页** | 已补 `referralService.js`(客户端 API 层),但小程序**尚未有页面**承载"我的转介 → 确认"交互。属**新功能开发**(非本轮 verify-extend 范围内),建议单列任务。当前用户只能通过 API 层确认,无 UI 入口。 |
| `miniapp` 服务详情端点缺失 | `matchmakerService.getServiceDetail` 指向 `GET /matchmaker/services/{id}`(用户端),后端**无此端点**(operator 侧才有 `/operator/matchmaker/services/{id}`)。`matchmaker-progress` 页面依赖它。 |
| `matchmaker-service-detail` 页面不存在 | `matchmaker-workbench.js:77` 跳转该页,目录不存在。 |
| `ReferralController.referToServiceStation` 的 `preferredDate` | 占位实现(`Instant.now()`),未解析日期字符串 —— 代码内已有 `// placeholder` 注释。 |
| `matchmaker_services` 的 `referral_target_type/id`、`referral_source_type` | DDL 有列,Java 侧**无写入**(死列)。 |
| `MatchmakerService.createReferral` | 返回内存 record、**不落库**,且无 Controller 端点调用(**死代码**)。真正的转介落库走 `ReferralService`。 |
| `CounselingServiceApp` | **孤儿类**(无任何注入点)。其 `addFollowup` → `toFollowupDTO` 会把 counseling followup 正文**明文回传**,但当前**无 HTTP 端点可达**。计划项 3 目前"因功能缺失而侥幸成立"。**若 Phase 13A 接通该端点,必须同时加脱敏**。 |
| counseling 模块零 `@PreAuthorize` | 权限全靠 Service 层手工判(`getDetail` 的 role 分支)。建议 Phase 13A 统一收口。 |
| `TEST_SCHEMA_OPTIONAL_TABLES` 仍有 30 项 | 每补齐一张应移除一项;`matchmaker_*` 本轮已移除。 |
---
## 八、结论
Phase 13 三个计划项与 Gate 全项通过。全量回归 **391 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**。
本轮最重要的发现是 **D1/D2/D6 三处「只在真实 PostgreSQL 上炸」的生产故障**,其共同隐蔽机制是:
测试库 `schema-h2.sql` 与生产迁移漂移(缺表 + 缺约束)→ **测试绿而生产崩**。
这与 Phase 12 发现的 D1 是**同族根因**,本轮把守卫进一步收紧为「直接解析迁移 SQL」。
**PHASE 13 CLOSED. Phase 13A 未自动进入,等待用户确认。**
+55 -7
View File
@@ -1,8 +1,8 @@
# 项目状态跟踪 — STATUS.md
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-12 RECOVERY CLOSED / GIT CLEAN`。**
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-13 RECOVERY CLOSED / GIT CLEAN`。**
>
> 阶段 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 需用户确认后启动。**
> 阶段 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(公益红娘转介)复验已关闭**,六轮共修复 23 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 13A 需用户确认后启动。**
最后更新: 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~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` |
| Flyway 迁移 | V001~V038 | V028~V038 已纳入 STATUS;V037 为 Phase 12 新增(实体列与生产迁移对齐,23 表 50 列);V038 为 Phase 13 新增(`matchmaker_recommendations.status` + `matchmaker_services.service_type` 的 CHECK 白名单扩展) |
| Backend 全量回归 | **已清偿**(2026-09-16) | 离线容器(`danyang-match-backend-test`)`mvn -o test` 实测:**391 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(4 分 59 秒);含 `CanonicalRouteContractTest`、`ChatContractJsonTest`、`MeRouteContractTest`、`DiscoverBlockInvisibilityIntegrationTest`、`ChatPhase09IntegrationTest`、`ChatWebSocketHandlerTest`、`PhotoUploadValidationTest`、`NotificationPhase11IntegrationTest`、`NotificationTemplateTypeGuardTest`、`NotificationPrivacyCopyTest`、`EntityMigrationParityTest`、`RelationshipProgressPhase12Test`、`HomePremaritalGuidancePhase12Test`、`MatchmakerReferralPhase13Test`、`MatchmakerServiceTest` |
| 路由/字段契约收敛 | **已完成**(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 全量回归(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(**需用户确认后启动**) |
| 核心待清债务 | 0 项 | 已全部清偿:3 页 JS→TS 迁移、backend 全量回归(391 tests)、DevTools 真机截图留档(2026-09-14)、`/me/*` 路径族收敛(2026-09-16)、实体↔生产迁移漂移修复(V037/V038,2026-09-16) |
| UAT / RC / Production | **UI 验收通过,前后端契约已收敛,Phase 08-13 已关闭** | 双端构建/契约/源码全部通过;UI 首批 11 页 2026-09-16 通过用户验收;`/me/*` 契约同轮收敛;Phase 08/09/10/11/12/13 复验通过并关闭,累计修复 23 处真实缺陷,可推进 Phase 13A(**需用户确认后启动**) |
---
@@ -41,6 +41,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 全绿 |
| 1.1.0-rc.4 | 2026-09-16 | Phase 13 复验关闭(公益红娘转介,修复 7 处缺陷);新增 V038 扩展 matchmaker CHECK 白名单;全量回归 **391 tests** 全绿 |
---
@@ -251,6 +252,53 @@
> 行为验证移至走真实服务方法的 `RelationshipProgressPhase12Test`(14 用例,含并发确认)。
> - 报告:`docs/Phase-12-V11-Recovery-Completion-Report-2026-09-16.md`
### Phase 13 — 公益红娘转介 ✅ 已完成(2026-09-16 KEEP-VERIFY/EXTEND 复验通过)
| 任务 | 状态 |
|------|------|
| 保留红娘服务和人工推荐;任何工作人员不能替用户 Like(INV-005) | ✅ 行为测试 4 项 |
| 补齐转介咨询、活动、服务站的用户同意、来源追踪和状态记录 | ✅ 行为测试 6 项 |
| 红娘只能看转介状态和必要摘要,不能读取 counseling followup 正文(INV-013) | ✅ 行为测试 3 项 |
| 红娘归属校验(推荐/转介不得越权代操作) | ✅ 负向测试 4 项 |
> **2026-09-16 复验结论(KEEP-VERIFY/EXTEND)**:历史 ✅ 不可信,逐条核对**运行时路径**与**生产约束**后修复 7 处缺陷:
> - **D1(Critical,生产故障)** `matchmaker_recommendations.status` 代码写入 `liked`/`passed`/`confirmed_mutual`,
> 而生产 `V009 chk_rec_status` 只允许 `pending_view/viewed/responded` 且从未扩展 → **真实 PG 上用户接受/跳过
> 推荐必崩**(`DataIntegrityViolation`)。测试库 `schema-h2.sql` 未复刻该 CHECK,故长期静默。修复:
> `V038__align_matchmaker_status_constraints.sql` 以 `DROP CONSTRAINT IF EXISTS` + `ADD CONSTRAINT` 扩展白名单。
> - **D2(Critical,生产故障)** `MatchmakerController` 缺省 `serviceType="recommendation"` 不在
> `chk_matchmaker_service_type` 白名单 → **提交红娘服务必崩**。修复:同 `V038` 扩展(原 7 值 +
> `recommendation`/`referral_counseling`/`referral_activity`/`referral_station`)。
> - **D3(High,越权)** `MatchmakerServiceService.recommendPartner` 无 `assignedOperatorId` 归属校验 →
> 任意红娘可替非本人负责的服务发起推荐(并可推荐给自己)。修复:补归属校验 + 禁止自我推荐。
> - **D4(High,越权读)** `getServiceDetail` 无归属校验且返回**完整**用户资料(`userId`/`description`/`userProfile`/
> `followups`)→ 任意红娘可读任意用户敏感详情。修复:签名加 `requesterId`,负责人得完整视图,
> 非负责人得脱敏视图(去 `userProfile`/`userId`/`followups`,仅 `followupCount` + `viewerIsOwner=false`)。
> - **D5(High,越权写)** `ReferralService` 的 `userId` 取自**请求体**且**零授权校验** → 任意持
> `matchmaker.service` 权限者可替任意用户发起转介。修复:新增 `assertMatchmakerServesUser`,
> 在 `referToServiceStation`/`referToActivity` 入口强制校验"存在进行中的服务关系"。
> - **D6(High,生产故障)** `MatchmakerFollowupMapper.findByServiceId` 只声明方法、**无 `@Select`** →
> 运行时 `Invalid bound statement (not found)`,`getServiceDetail` **必崩**。**该缺陷由本轮新增测试首次暴露**
> (历史测试从未调用该路径)。修复:补 `@Select`。
> - **D7(Medium,契约缺口)** admin-web 5 处 API 缺口:`/matchmaker/services/operator/queue`、
> `/matchmaker/recommendations/operator`、`PUT /matchmaker/services/{id}/assign`、
> `POST /matchmaker/services/{id}/recommendation`、`/referrals/my-history`(C 端端点被运营端误用)。
> 修复:后端补 `GET /operator/matchmaker/recommendations` 与 `GET /referrals/operator/history`
> 两个运营端端点(含 `MatchmakerServiceMapper.findActiveByOperatorAndUser`、
> `MatchmakerRecommendationMapper.findByOperatorId`、`ReferralService.getReferralHistoryByOperator`),
> 前端改路径、`assign` 改 `POST` + body `operatorId`;小程序新增 `miniapp/services/referralService.js`。
> - **测试库盲区修复**:`schema-h2.sql` 补 `matchmaker_services`/`matchmaker_recommendations`/
> `matchmaker_followups` 三表(约束与 V038 对齐);`EntityMigrationParityTest` 的 3 张 matchmaker 表
> 从 `TEST_SCHEMA_OPTIONAL_TABLES` 移入 `REQUIRED_IN_TEST_SCHEMA`;`MigrationValidationTest` 基线 37 → 38。
> - **新增永久守卫** `MatchmakerReferralPhase13Test`(17 用例,`@SpringBootTest` 走真实方法路径)+ 重写
> `MatchmakerServiceTest`(6 用例)。**关键教训**:首版常量守卫**硬编码白名单副本**,注入 D1 缺陷后
> **仍全绿**(对漂移零反应)→ 重写为**直接解析 `db/migration/V*.sql`**,按文件名顺序应用
> `DROP CONSTRAINT IF EXISTS` + `ADD CONSTRAINT` 取"最终有效"白名单,并内置解析器自检用例。
> 修正后经**第二轮负向验证**证明会红(`Tests run: 23, Failures: 1, Errors: 2 / BUILD FAILURE`)。
> - **删除假测试**:`MatchmakerServiceTest.testMatchmakerCannotLike` 原为 `assertTrue(true)`;
> `CoreInvariantsTest` 的 INV-007 断言的是 `MATCHMAKER_STATUS_*` 常量(名不副实)、INV-010 亦为
> `assertTrue(true)` → 已删除占位、如实改名并注明行为验证位置。
> - 报告:`docs/Phase-13-V11-Recovery-Completion-Report-2026-09-16.md`
### V1.1 待实现模块(按 Phase 顺序)
| 模块 | 对应 Phase | 状态 |
@@ -259,7 +307,7 @@
| Banner 管理(后端+前端) | Phase 19 | ⏳ 待开发 |
| 本地资格声明与审核 | Phase 04~05 | ⏳ 待开发 |
| 公益红娘服务转介 | Phase 13 | ✅ 已完成 |
| 婚恋咨询 + 婚前辅导(Phase 13A) | Phase 13A | ⏳ 待开发 |
| 婚恋咨询 + 婚前辅导(Phase 13A) | Phase 13A | ⏳ 待开发(**需用户确认后启动**) |
| 内容管理(婚恋课堂/反诈) | Phase 19 | ⏳ 待开发 |
| 专项公益服务项目 | Phase 19 | ⏳ 待开发 |
| V1.1新增不变量测试(INV-011~020) | Phase 23 | ⏳ 待开发 |
@@ -257,12 +257,52 @@ D2——`RelationshipService.createMatch` 不写 episode/lastActivityAt,与 `M
**Files:** `matchmaker/**`、`referral/**`、Miniapp 红娘页面、Admin referral 页面。
- [ ] 保留红娘服务和人工推荐;验证任何工作人员不能替用户 Like。
- [ ] 补齐转介咨询、活动、服务站的用户同意、来源追踪和状态记录。
- [ ] 红娘只能看转介状态和必要摘要,不能读取 counseling followup 正文。
- [x] 保留红娘服务和人工推荐;验证任何工作人员不能替用户 Like。
- [x] 补齐转介咨询、活动、服务站的用户同意、来源追踪和状态记录。
- [x] 红娘只能看转介状态和必要摘要,不能读取 counseling followup 正文。
**Gate:** 转介形成可追踪记录但不越权;用户决定权测试通过。
> **2026-09-16 复验结论(KEEP-VERIFY/EXTEND)**:历史 ✅ 不可信。全仓库唯一的"INV-005 红娘不可代用户操作"
> 证据是 `MatchmakerServiceTest#testMatchmakerCannotLike` 的 `assertTrue(true)` 占位,以及
> `CoreInvariantsTest#testMatchmakerCannotLike`(断言的是 `MATCHMAKER_STATUS_*` **状态常量字符串**,
> 与 Like 无关)—— 两者均为**名不副实的假测试**;且测试库 `schema-h2.sql` **完全缺失** `matchmaker_*` 三表,
> 该模块**零真实行为覆盖**。逐条核对运行时路径后修复 7 处缺陷:
> - **D1(Critical,生产故障)** `matchmaker_recommendations.status`:代码写
> `liked`/`passed`/`confirmed_mutual`,而 `V009 chk_rec_status` 只允许
> `pending_view/viewed/responded`,且**从未被后续迁移扩展** → 真实 PostgreSQL 上
> 用户"接受/跳过"红娘推荐**必然 500**,P13 主流程不可用。
> - **D2(Critical,生产故障)** `MatchmakerController.submit` 在请求未带 `serviceType` 时兜底写
> `"recommendation"`,而 `V009 chk_matchmaker_service_type` 白名单只含 `recommend_partner` 等 7 个值
> → 真实 PG 上提交红娘服务**必然 500**。(`MatchmakerService` 注释声明的
> `referral_counseling`/`referral_activity`/`referral_station` 同样越界。)
> - **D3(High)** `MatchmakerServiceService.recommendPartner` 缺 `assigned_operator_id` 归属校验
> → 任何持 `matchmaker.service` 权限的 operator 可对**任意** serviceId 创建推荐(越权)。
> - **D4(High)** `getServiceDetail` 无任何归属校验,任何 `matchmaker.view` 持有者可读**任意**服务的
> 完整用户资料(displayName/gender/age/region/education/occupation/maritalStatus)→ 越权读。
> - **D5(High)** `ReferralService.referToActivity` / `referToServiceStation` 的 `userId` 直接取自
> 请求体且**无任何授权校验**(连 `MatchmakerServiceMapper` 都没注入)→ 任意红娘可把**任意用户**
> 转介到任意活动/服务站,并顺带为该用户创建报名/预约(越权代操作)。
> - **D6(High,生产故障)** `MatchmakerFollowupMapper.findByServiceId` **只有方法声明、没有 `@Select`**
> → MyBatis 运行时抛 `Invalid bound statement (not found)`,`getServiceDetail` 必然 500,
> 红娘工作台"查看服务详情"完全不可用。此前不可见:测试库缺 `matchmaker_followups` 表。
> - **D7(Medium)** Admin referral 页面契约缺口:`/referrals/my-history`(C 端 UserPrincipal 端点)
> 被运营端调用;`/matchmaker/services/operator/queue`、`/matchmaker/recommendations/operator`、
> `PUT /matchmaker/services/{id}/assign`、`POST /matchmaker/services/{id}/recommendation`
> 四条路径在后端**均不存在**(404)。
> 修复:新增 `V038__align_matchmaker_status_constraints.sql`(沿用 V018/V020/V025 的
> `DROP CONSTRAINT IF EXISTS` + 重加白名单惯例);测试库补 `matchmaker_*` 三表(约束对齐 V038);
> D3/D4/D5 补归属校验与转介授权;D6 补 `@Select`;D7 补两个运营端端点并修正前端调用。
> **新增守卫** `MatchmakerReferralPhase13Test`(17 用例,走真实服务方法 + 真实落库)替换假测试;
> `MatchmakerServiceTest` 重写为**直接解析迁移 SQL** 的口径守卫(第一轮负向验证暴露"抄副本"守卫
> 对漂移无反应,已修正);`CoreInvariantsTest` 的 INV-007/INV-010 假断言已如实改名并指向真实验证。
> **两轮负向验证**:注入 D1(回退 `chk_rec_status` 白名单)→ 行为守卫抛 `DataIntegrityViolation`、
> 常量守卫报 3 处 failure,守卫有效性得证。
>
> **全量回归实测**:`[WARNING] Tests run: 391, Failures: 0, Errors: 0, Skipped: 3` / `BUILD SUCCESS`(4 分 59 秒)。
> Phase 12 基线 371 → 391(净 +20)。**Phase 13 已关闭。Phase 13A 未自动进入,等待用户确认。**
> 报告:`docs/Phase-13-V11-Recovery-Completion-Report-2026-09-16.md`
### Phase 13A:婚恋咨询与婚前辅导 — REFACTOR/EXTEND
**Files:** `counseling/**`、`miniapp/pages/counseling-*`、`miniapp/pages/my-counseling/**`、`miniapp/pages/premarital-*`、`admin-web/src/modules/counseling/CounselingView.vue`、router。