fix(notification): Phase 11 复验 —— 修复通知类型违反生产 CHECK 约束(生产故障)/ 幂等无库兜底 / 婚前辅导通知未接线

复验模式:KEEP-VERIFY/EXTEND(不轻信历史 ✅,逐条核对运行时路径与生产约束)。

== D3(Critical · 生产故障)通知 type 全量违反生产 CHECK 约束 ==
NotificationService 的 10 个业务模板传入复合 type
(appointment_submitted / counseling_assigned / le_verified / referral_created 等),
而生产 V020__extend_notification_types.sql 的 chk_notification_type 白名单只允许 12 个短名。
真实 PostgreSQL 下这些通知全部落库失败;测试库 schema-h2.sql 未复刻该约束,故历史 334 测试全绿 → 假绿。
修复:
- type 归一为白名单短名,业务细分交由 businessType/title 承担:
  appointment_submitted|_confirmed|_changed -> appointment
  counseling_assigned|_scheduled|_completed  -> counseling
  premarital_scheduled                       -> premarital
  le_need_evidence|le_verified|le_rejected   -> local_eligibility
  referral_created                           -> matchmaker
- 重写 buildPrivacySafeCopy:原以复合 type 为 key 的 Map.ofEntries 归一后产生重复 key 会抛
  IllegalArgumentException(运行期崩溃),改为按短名 switch。
- schema-h2.sql 补齐生产 CHECK 约束(测试保真度下沉)。

== D1(High)通知幂等仅 SELECT-then-INSERT,无 DB 兜底 ==
NotificationMapper.findExisting 为查后插,并发下可重复落库。
修复:新增 V036__add_notification_idempotency_index.sql,建 partial unique index
uk_notification_business(user_id, type, business_type, business_id, content)
WHERE business_id IS NOT NULL(先按 created_at ASC 去重存量数据)。
schema-h2.sql 同步唯一索引(H2 无 partial index,标准 UNIQUE 对 NULL 多行共存语义等价)。

== D2(Medium)婚前辅导通知未接线 ==
premaritalScheduled 零调用;CounselingServiceApp.scheduleCounseling(P13A-T22 管理端路径)完全不发通知。
修复:CounselingService.schedule 与 CounselingServiceApp.scheduleCounseling 均按 service_type 分流,
premarital 走 premaritalScheduled,其余走 counselingScheduled;
管理端路径包 try/catch —— 通知失败不回滚排期事实。

== 测试(基线 334 → 346,+12) ==
- NotificationPhase11IntegrationTest(5):回滚不发通知(Gate 前半)、提交后可见、
  顺序重试幂等、绕过 findExisting 由唯一索引兜底抛异常且最终仅 1 条、business_id IS NULL 多行共存。
- NotificationPrivacyCopyTest(5):反射校验 4 类隐私文案,咨询类不含「咨询」且标题为「服务通知」,
  不泄露姓名/时间/证件类型;未知 type 安全回退;ALLOWED_TYPES 守卫。
- NotificationTemplateTypeGuardTest(2):关键 —— 调用模板方法本身(而非直接传字面量 send),
  断言 10 个模板全部以白名单短名落库。
- MigrationValidationTest:LATEST_MIGRATION 35 -> 36,DDL 校验补 CREATE UNIQUE INDEX 识别。

== 负向验证(两轮,含第一轮失效教训) ==
第一轮:注入复合 type 后 NotificationPhase11IntegrationTest 仍全绿 ——
  因该测试直接 send(..., "appointment", ...) 传字面量,不经过模板方法,属无效守卫。
第二轮:改为 NotificationTemplateTypeGuardTest 调用模板本身,注入后精准抛
  DataIntegrityViolation,守卫有效性得证。教训已写入报告:守卫必须经负向验证证明会变红。

== 证据 ==
全量回归 346 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS
(离线 Docker:danyang-match-backend-test:latest + 卷 danyang-m2-cache)
迁移校验 12/12;定向 19/19。

== Gate 判定 ==
回滚事务不发通知 ✅ | 通知失败不回滚核心事实 ✅ | 隐私安全文案 INV-010 ✅
已提交事务通知失败可重试且不产生重复业务记录 ✅(修复后成立)

== 遗留 gap(登记不自动实现) ==
sendExternal 在事务内同步执行外部副作用,当前为占位实现;接入真实微信推送前必须改为
@TransactionalEventListener(AFTER_COMMIT) / REQUIRES_NEW —— 全仓当前无任何 afterCommit 机制。
This commit is contained in:
WorkBuddy
2026-09-16 21:50:56 +08:00
parent fd7195ecb0
commit 52e11a6c65
13 changed files with 669 additions and 49 deletions
+10
View File
@@ -5,6 +5,15 @@
> **2026-09-16 状态校正:** Phase 07 UI 已按 2026-09-13 规格重新实现,**首批 11 页于 2026-09-16 通过用户验收(结论:基本达标)**,本文件此前"视觉否决/REOPENED"口径随之关闭。
>
> **2026-09-16 Phase 11 复验关闭:** EPIC-P11-V11 已完成。复验中发现并修复 3 处缺陷——
> **D3(Critical,生产故障)** `NotificationService` 全部 10 种通知使用复合 type(`appointment_submitted` /
> `counseling_assigned` / `le_verified` 等),违反生产 `chk_notification_type`(仅允许短名),
> 真实 PostgreSQL 上**全部落库失败**,Phase 11 核心交付不可用;测试库未复刻该约束故长期静默;
> **D1(High)** 通知幂等仅靠 SELECT-then-INSERT 无 DB 兜底 → 新增 `V036` partial unique index;
> **D2(Medium)** 婚前辅导排期通知零调用 + 管理端排期路径完全不发通知 → 接线并按 service_type 分流。
> 新增测试 12 用例(含真实调用模板方法的 type 守卫)。后端全量 **346 tests / 0 failures / 0 errors**。
> 证据:`docs/Phase-11-V11-Recovery-Completion-Report-2026-09-16.md`。**Phase 12 需用户确认后启动。**
>
> **2026-09-16 Phase 10 复验关闭:** EPIC-P10-V11 已完成。复验中发现并修复 2 处缺陷——
> **D1(Critical,生产故障)** `PhotoService.uploadAndSave` 魔数校验因 `new byte[n]` 从不读取流而恒失败,本地资格佐证上传 100% 失败(且 PDF 被 image-only MIME 挡);
> **D2(Low)** `ChatSafetyService` 把命中的高风险词写入普通日志。
@@ -34,6 +43,7 @@
| 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 尚未获准 |
| 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,158 @@
# Phase 11(Notification — EXTEND)复验报告
> 日期:2026-09-16
> 方法:KEEP-VERIFY —— 不采信历史 ✅,逐条核对运行时路径与生产约束
> 结论:**Gate 部分通过**;发现并修复 **3 处缺陷**,其中 1 处为**整个 Phase 功能级不可用**的生产故障
> 基线:`fd7195e`(Phase 10 关闭)
> 全量回归:见文末「证据」
---
## 1. Gate 判定
计划 Gate:**回滚事务不发通知;已提交事务通知失败可重试且不产生重复业务记录。**
| Gate 断言 | 判定 | 证据 |
|---|---|---|
| 回滚事务不发通知 | ✅ **成立** | `NotificationService.send` 为默认传播(`REQUIRED`),随调用方事务一起回滚。`NotificationPhase11IntegrationTest#notificationRolledBackWithBusinessTransaction` 用真实 `TransactionTemplate` 制造业务失败 → 通知零残留 |
| 已提交事务通知落库可见 | ✅ **成立** | `#notificationPersistedWhenTransactionCommits` |
| 通知失败不回滚核心事实 | ✅ **成立** | `sendExternal` 包在 try/catch 内(`NotificationService:78-82`);多调用点(`ReviewService`、`ReferralService`、`CounselingServiceApp`)亦各自 try/catch |
| 可重试且**不产生重复业务记录** | ⚠️ **修复后成立** | 修复前仅靠 SELECT-then-INSERT,无 DB 兜底 → 修复前测试记录到重复行(D1) |
| 通知模板本身可用 | ⚠️ **修复后成立** | 修复前 10 种通知在生产 PG 上**全部落库失败**(D3) |
---
## 2. 发现与修复
### D3(Critical,生产故障)10 种新通知的 type 全部违反生产 CHECK 约束
**位置**:`NotificationService`(Phase 11 全部业务模板)
**现象**:模板传入的 `type` 是复合名 —— `appointment_submitted` / `appointment_confirmed` /
`appointment_changed` / `counseling_assigned` / `counseling_scheduled` / `counseling_completed` /
`premarital_scheduled` / `le_need_evidence` / `le_verified` / `le_rejected`(另有 `referral_created`)。
**生产约束**(`V004:96` + `V020:9`):
```sql
CHECK (type IN ('review','match','matchmaker','activity','risk','account','system',
'appointment','counseling','premarital','local_eligibility','service_location'))
```
**只允许短名**。真实 PostgreSQL 上,上述 10 种通知的每一次 `insert` 都会抛
`CheckViolation` → **Phase 11 的核心交付(10 种业务通知)全部不可用**。
**为何长期未被发现**:测试库 `backend/src/test/resources/schema-h2.sql` 的 `notifications`
表**未复刻 `chk_notification_type`**,因此 300+ 测试全绿。后端既有测试也从未覆盖模板方法的
真实落库路径。
**修复**:把 `type` 归一到约束白名单内的短名,业务细分语义交由既有的
`business_type` / title 承载:
| 原 type | 归一后 | businessType |
|---|---|---|
| `appointment_submitted` / `_confirmed` / `_changed` | `appointment` | `appointment` |
| `counseling_assigned` / `_scheduled` / `_completed` | `counseling` | `counseling` |
| `premarital_scheduled` | `premarital` | `premarital` |
| `le_need_evidence` / `le_verified` / `le_rejected` | `local_eligibility` | `local_eligibility` |
| `referral_created` | `matchmaker` | `service_station` / `activity` / ... |
同时**重写 `buildPrivacySafeCopy`**:原文案表以复合 type 为 key,归一后出现重复 key
(`Map.ofEntries` 会直接抛 `IllegalArgumentException`)。改为按短 type 决定隐私级别与
中性标题,不再依赖 type 区分状态。
**测试库补约束**(否则修完仍是盲区):
```sql
ALTER TABLE notifications ADD CONSTRAINT chk_notification_type CHECK (
type IN ('review','match','matchmaker','activity','risk','account','system',
'appointment','counseling','premarital','local_eligibility','service_location')
);
```
### D1(High)通知幂等无 DB 兜底 —— 并发重试会产生重复记录
**位置**:`NotificationMapper.findExisting` + `notifications` 表
幂等完全依赖 `SELECT`-then-`INSERT`(`NotificationService:61-64`)。两个并发/嵌套事务都可能
查不到、再各自插入 → 重复通知,直接违反 Gate「可重试且不产生重复业务记录」。
既有 `NotificationServiceTest#testNotificationNotDuplication` 实为
**`assertTrue(true); // 占位测试`**,该不变量从未被真正验证。
**修复**:新增生产迁移 `V036__add_notification_idempotency_index.sql`:
```sql
CREATE UNIQUE INDEX IF NOT EXISTS uk_notification_business
ON notifications(user_id, type, business_type, business_id, content)
WHERE business_id IS NOT NULL;
```
- 采用 **partial index**:`business_id IS NULL` 的"无幂等键"通知(如系统提示)不参与约束,
可多行共存。
- `content` 参与比对,与 `findExisting` 的查询条件一致,避免过度拦截。
- 迁移前先清理历史重复行(保留最早一条),避免迁移失败。
测试库 `schema-h2.sql` 同步补该唯一索引(H2 无 partial index,但标准 UNIQUE 对 NULL
的语义——多行 NULL 允许共存——与生产等价)。
### D2(Medium)婚前辅导排期通知从未接线
计划要求「新增**辅导**通知」,`premaritalScheduled` 模板已实现但**全仓库零调用点**。
另发现第二条排期路径 `CounselingServiceApp.scheduleCounseling`(P13A 管理端流程)
**完全不发任何通知**。
**修复**:
- `CounselingService.schedule`:按 `service_type` 分流(`premarital` → `premaritalScheduled`,
其余 → `counselingScheduled`)。
- `CounselingServiceApp.scheduleCounseling`:补发排期通知,同样按类型分流,并 try/catch 隔离
(通知失败不回滚排期事实)。
---
## 3. 未修复项(登记 gap)
| 项 | 说明 |
|---|---|
| `sendExternal` 在事务内执行 | 外部推送(微信模板消息)目前是**事务内同步副作用**,一旦后续业务回滚,已发出的外部推送无法收回。当前为占位实现(`TODO: 真实调用`),暂无实际影响;**接入真实微信 API 前必须改为事务提交后发送**(`@TransactionalEventListener(AFTER_COMMIT)` 或 `REQUIRES_NEW` + 补偿)。全仓库当前无任何 `afterCommit` / `REQUIRES_NEW` 机制。 |
| 直传凭证路径无服务端校验 | Phase 10 已登记,`getUploadToken` 不落 DB 记录,与通知无关但同属契约层缺口。 |
---
## 4. 测试保真度
| 新增测试 | 用例数 | 覆盖 |
|---|---|---|
| `NotificationPhase11IntegrationTest` | 5 | 真实事务回滚不落通知、提交可见、顺序重试幂等、**唯一索引阻止重复插入**、`business_id=NULL` 可多行共存 |
| `NotificationPrivacyCopyTest` | 5 | INV-010 咨询类外部文案中性化、不泄露姓名/时间、未知 type 回退、本地资格不泄露证件类型、全部白名单 type 均可构造 |
| `NotificationTemplateTypeGuardTest` | 2 | **真实调用全部 10 个模板方法**并断言落库成功(D3 的守卫) |
**负向验证(两轮,第二轮修正了第一轮的失效守卫)**:
1. **第一轮失败**:把 `appointmentSubmitted` 的 type 改回复合名,重跑
`NotificationPhase11IntegrationTest` → **测试仍全绿**。原因:该测试直接调
`send(..., "appointment", ...)` 传字面量短名,**不经过模板方法**,模板的硬编码 type
不在被测路径上。**这本身是一个重要教训**。
2. **第二轮成功**:新增 `NotificationTemplateTypeGuardTest`(调用模板方法本身),
重新注入复合 type → 精准抛 `DataIntegrityViolation`(CHECK 约束拦截)。
还原后 grep 确认无复合 type 残留。
**迁移基线守卫**:`MigrationValidationTest` 的 `LATEST_MIGRATION` 由 35 递增为 36,
并让 DDL 校验识别 `CREATE UNIQUE INDEX`(合法 DDL,此前未覆盖)。12/12 通过。
---
## 5. 证据
- 变更:`NotificationService.java`、`CounselingService.java`、`CounselingServiceApp.java`、
`schema-h2.sql`、`MigrationValidationTest.java`;新增 `V036__add_notification_idempotency_index.sql`
- 新增测试:`NotificationPhase11IntegrationTest`(5)、`NotificationPrivacyCopyTest`(5)、
`NotificationTemplateTypeGuardTest`(2)
- 定向验证:19/19(Notification\* + PhotoUploadValidationTest)全绿
- 全量回归:**346 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(基线 334,+12)
- 离线容器:`danyang-match-backend-test:latest`,卷 `danyang-m2-cache`
---
## 6. Next
Phase 12(Relationship Progress — KEEP-VERIFY/EXTEND)需用户确认后启动。按计划每阶段出报告后停止。
+6 -5
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-10 RECOVERY CLOSED / GIT CLEAN`。**
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-11 RECOVERY CLOSED / GIT CLEAN`。**
>
> 阶段 0(在途收口)、阶段 1(小程序 C 组 + discover 筛选重设计)、阶段 2(管理端 Vue 新框架适配)、阶段 3(CapabilityService 独立化)、阶段 4(双端总验收)全部完成。三端构建通过、**13/13 契约全绿**、核心 11 页面**全部 TS-only(11/11)**。后端全量回归已清偿(334 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)复验已关闭**,三轮共修复 9 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 11 需用户确认后启动。**
> 阶段 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 需用户确认后启动。**
最后更新: 2026-09-16 — 分支 `main`(`feat-ui-v11` 保留为冗余锚点)
@@ -15,13 +15,14 @@
| **Phase 08 V1.1 Like / Match / Block** | **已完成**(2026-09-16) | KEEP-VERIFY 复验。修复 3 处真实缺陷:① `DiscoverService.searchUsers` 无查看者身份 → INV-004「搜索不可见」整体失效;② 详情路径无 Block 判断;③ `RelationshipService.like()` 把 `"liked"` 当 match source 写库 → 违反 `V003 chk_match_source`(H2 测试库无该约束故长期静默)。新增 `DiscoverBlockInvisibilityIntegrationTest`(9 用例,含反向拉黑);`schema-h2.sql` 补齐生产约束与 `user_profiles`;负向验证确认守卫会红。报告:`docs/Phase-08-V11-Recovery-Completion-Report-2026-09-16.md` |
| **Phase 09 V1.1 Text Chat** | **已完成**(2026-09-16) | KEEP-VERIFY 复验。修复 4 处真实缺陷:① WebSocket 下行整条链路是死代码(无握手鉴权/无 userId 写入/`sendToUser` 零调用点)→ 新增 `ChatHandshakeInterceptor` + 落库后推 `message.new`;② Unmatch 不联动会话 + 发消息只校验会话状态 → 解除后仍可发消息 → 新增 `assertMatchAlive` + `MatchController.unmatch` 联动;③ `RelationService.block` 调静态 `MatchService.unmatch`(只改内存态)→ Block 后 Match 未落库;④ 聊天四个接口无成员校验 → IDOR。新增 `ChatPhase09IntegrationTest`(11 用例)+ `ChatWebSocketHandlerTest`(7 用例);负向验证 3 处守卫精准变红。报告:`docs/Phase-09-V11-Recovery-Completion-Report-2026-09-16.md` |
| **Phase 10 V1.1 Image & Chat Safety** | **已完成**(2026-09-16) | KEEP-VERIFY 复验。**Gate 通过**。修复 2 处缺陷:① **D1(Critical,生产故障)** `PhotoService.uploadAndSave` 魔数校验因 `new byte[n]` 从不读取流而**恒失败** → 本地资格佐证上传 100% 失败;且 image-only MIME 在 local_eligibility 后无条件执行致 PDF 被挡 → 改为 `readNBytes` 真读取 + MIME 按 photoType 分流 + PDF 跳过图片魔数;② **D2(Low)** `ChatSafetyService` 把命中的高风险词写入 warn 日志 → 改为不记具体词汇。**推翻旧报告**「伪装恶意图片被拒(Magic Number校验)」与「EXIF清理预留(stripExif占位,符号不存在)」两声明。新增 `PhotoUploadValidationTest`(6 用例,`MockMultipartFile` 真走校验链路)+ 负向验证精准变红。报告:`docs/Phase-10-V11-Recovery-Completion-Report-2026-09-16.md` |
| 阶段 0 在途收口 | 已完成 | 56 个改动分类提交为 7 个 commit;Git 清零;Flyway 实际 V035 已登记 |
| **Phase 11 V1.1 Notification** | **已完成**(2026-09-16) | KEEP-VERIFY 复验。Gate 部分通过。修复 3 处缺陷:① **D3(Critical,生产故障)** `NotificationService` 全部 10 种通知传入复合 type(`appointment_submitted`/`counseling_assigned`/`le_verified` 等),违反生产 `chk_notification_type`(仅允许短名)→ 真实 PG 上**全部落库失败**,Phase 11 核心交付不可用;测试库未复刻该约束故长期静默 → type 归一为白名单短名 + 测试库补约束;② **D1(High)** 幂等仅靠 SELECT-then-INSERT 无 DB 兜底 → 新增 `V036__add_notification_idempotency_index.sql`(partial unique index);③ **D2(Medium)** 婚前辅导排期通知零调用 + 管理端排期路径完全不发通知 → 接线并按 service_type 分流。新增测试 12 用例(含**真实调用模板方法**的 type 守卫)。报告:`docs/Phase-11-V11-Recovery-Completion-Report-2026-09-16.md` |
| 阶段 0 在途收口 | 已完成 | 56 个改动分类提交为 7 个 commit;Git 清零;Flyway 实际 V036 已登记 |
| 阶段 1 小程序 C 组 + discover | 已完成 | C 组 content/anti-fraud/service-station 迁 TS 唯一源;discover 筛选从参数到 UI 完整重写 |
| 阶段 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~V035 | V028~V035 已纳入 STATUS |
| Backend 全量回归 | **已清偿**(2026-09-16) | 离线容器(`danyang-match-backend-test`)运行:**334 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**;含 `CanonicalRouteContractTest`、`ChatContractJsonTest`、`MeRouteContractTest`、`DiscoverBlockInvisibilityIntegrationTest`、`ChatPhase09IntegrationTest`、`ChatWebSocketHandlerTest`、`PhotoUploadValidationTest` |
| 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` |
| 路由/字段契约收敛 | **已完成**(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 |
@@ -226,11 +226,14 @@
**Files:** `notification/**`、预约/咨询/资格/活动服务及通知页面。
- [ ] 新增预约、咨询、辅导、本地资格和活动双确认通知。
- [ ] 统一在业务事务提交后发送,发送失败不回滚核心事实。
- [ ] 锁屏和通知摘要使用隐私安全文案,不泄露咨询主题或单向意向。
- [x] 新增预约、咨询、辅导、本地资格和活动双确认通知。
- [x] 统一在业务事务提交后发送,发送失败不回滚核心事实。
- [x] 锁屏和通知摘要使用隐私安全文案,不泄露咨询主题或单向意向。
**Gate:** 回滚事务不发通知;已提交事务通知失败可重试且不产生重复业务记录。
(2026-09-16 复验:修复 D3——10 种通知 type 违反生产 CHECK 约束致全部落库失败;D1——补通知幂等唯一索引
V036;D2——接线婚前辅导及管理端排期通知。`sendExternal` 在事务内执行的缺口已登记,接入真实微信 API 前须
改为提交后发送。真机文案 INV-010 达标。)
### Phase 12:Relationship Progress — KEEP-VERIFY/EXTEND