fix(counseling): close Phase 13A KEEP-VERIFY/EXTEND — 9 real defects + 4 test-infra defects
Phase 13A(婚恋咨询与婚前辅导)复验关闭。历史 ✅ 不可信:counseling 模块存在双实现,
线上权威 CounselingService(167 行)零测试,唯一被测的 CounselingServiceApp(280 行)
是孤儿类(无注入点、无 HTTP 端点可达,生产永不执行)→ 已合并并删除。
生产缺陷(D1~D9):
- D1 Critical 生产故障 · INV-013 泄露:咨询正文 userRequestSummary 原文被写入 WARN 日志
(同 Phase 10 D2 一族)→ 改为只记 serviceId + riskLevel,绝不记正文。
- D2 High 越权:申请 userId 取自请求体而非认证主体 → Controller 强制 principal.id()。
- D3 High 并发:分配用「先读后写」→ 两管理员并发分配静默覆盖;
改 assignIfPending 条件更新(WHERE status='pending' AND assigned_operator_id IS NULL),
affectedRows=0 明确报错。
- D4 High 越权读:counseling_manager 经 getDetail 可读 followups 正文 → INV-013 绕过通道;
manager 只加回 userRequestSummary,不装配 followups。
- D5 Medium:无状态机 → 「已完成」可被再取消;新增 ALLOWED_TRANSITIONS + assertTransition。
- D6 Medium 生产故障:GET /counseling/services/intro 后端不存在(且被 /services/{id} 吞掉)
→ 小程序 intro 页 100% 无数据;补端点并声明在变量路径之前。
- D7 Medium 契约:Admin 调用 5 条不存在的 /services/operator/*(404)+ 缺四态
→ 后端补 8 个运营端端点(@PreAuthorize),前端整体重写 + 注册 /counseling 路由。
- D8 Medium 契约:小程序字段名错(userRequestSummary/preferredServiceMode)+ 无幂等
+ my-counseling 未检查 success(403 静默空列表)+ 跳转不存在的页 + WXML 内调用方法。
- D9 Medium 测试保真度:测试库 counseling 两表零 CHECK 约束,
counseling_services 未列入 EntityMigrationParityTest 任何集合(= 零覆盖)。
测试基建缺陷(T1~T4,不修即恒真假绿):
- T1 守卫找不到 DDL:surefire workingDirectory = ${project.basedir}/.. 使 CWD 为仓库根,
且 backend/ 是子目录非祖先 → 改为候选后缀 + 逐级向上定位,失败大声抛异常。
- T2 全限定 @com.baomidou...TableField(exist=false) 漏解析(假漂移警报)→ 正则放宽 + 回归守卫。
- T3 OperatorEndpointPermissionCoverageTest 扫到私有辅助方法(假阳性)→ 只扫描真实端点 + 自检。
- T4 两处测试自身逻辑缺陷:陈旧内存快照比较、Instant 纳秒 vs DB 微秒精度。
迁移:新增 V039(counseling 幂等键 + 三个 CHECK 白名单显式固化),基线 38 → 39。
负向验证两轮(均已还原,grep -c NEG-VAL = 0):
- ① DDL 常量守卫:从 V039 移除 'scheduled' → 常量守卫精准变红
(证明解析的是真实 DDL 而非副本)。
- ② 行为守卫:注入「并发回退先读后写 / manager 读 followups / 状态机守卫失效」
→ 三个用例各自精准变红。
测试:新增 CounselingPhase13ATest(25)+ CounselingConstraintGuardTest(5,解析真实 DDL);
删除孤儿类及其 3 个 DTO、删除测死代码的 CounselingDataScopeTest。
全量回归:Tests run: 420, Failures: 0, Errors: 0, Skipped: 3 / BUILD SUCCESS(4:49)。
基线 391 → 420(净 +29)。admin-web vue-tsc EXIT=0;miniapp tsc EXIT=0。
Tag: phase13a-recovery-2026-09-17
Report: docs/Phase-13A-V11-Recovery-Completion-Report-2026-09-17.md
This commit is contained in:
+60
-25
@@ -5,6 +5,28 @@
|
||||
|
||||
> **2026-09-16 状态校正:** Phase 07 UI 已按 2026-09-13 规格重新实现,**首批 11 页于 2026-09-16 通过用户验收(结论:基本达标)**,本文件此前"视觉否决/REOPENED"口径随之关闭。
|
||||
>
|
||||
> **2026-09-17 Phase 13A 复验关闭:** EPIC-P13A 已完成。复验发现 counseling 模块存在**双实现**——
|
||||
> 线上权威 `CounselingService`(167 行)**零测试**,唯一被测的 `CounselingServiceApp`(280 行)是
|
||||
> **孤儿类**(无注入点、无 HTTP 端点可达,生产永不执行)→ 已合并并删除孤儿类。修复 **9 处缺陷**:
|
||||
> **D1(Critical,生产故障 · INV-013 泄露)** 咨询正文原文被写入 WARN 日志(同 Phase 10 D2 一族);
|
||||
> **D2(High,越权)** 申请 `userId` 取自请求体而非认证主体 → 可替他人申请;
|
||||
> **D3(High,并发)** 分配用「先读后写」→ 并发**静默覆盖**,改 `assignIfPending` 条件更新;
|
||||
> **D4(High,越权读)** manager 经 `getDetail` 可读 followups 正文(INV-013 绕过通道);
|
||||
> **D5(Medium)** 无状态机 → 「已完成」可被再取消;
|
||||
> **D6(Medium,生产故障)** `GET /counseling/services/intro` 后端不存在 → 小程序 intro 页 100% 无数据
|
||||
> (且被 `/services/{id}` 吞掉,需置于变量路径之前);
|
||||
> **D7(Medium,契约)** Admin 调用 5 条不存在的 `/services/operator/*`(404)+ 缺四态 → 后端补 8 端点;
|
||||
> **D8(Medium,契约)** 小程序字段名错 + 无幂等 + 未检查 `success`(403 静默空列表)+ 跳转不存在的页;
|
||||
> **D9(Medium,测试保真度)** 测试库 counseling 两表**零 CHECK 约束**,`counseling_services` 零覆盖。
|
||||
> **另修 4 处测试基建缺陷**:守卫因 surefire `workingDirectory` 指向仓库根而**找不到 DDL**(恒真假绿);
|
||||
> 全限定 `@TableField(exist=false)` 漏解析(假警报);权限守卫扫到私有辅助方法(假阳性);
|
||||
> 两处测试自身逻辑缺陷(陈旧快照 / Instant 精度)。
|
||||
> **关键结论**:负向验证两轮均证明守卫会红——① 从 `V039` 移除 `scheduled` → 常量守卫精准变红
|
||||
> (证明解析的是**真实 DDL** 而非副本);② 注入并发/INV-013/状态机三个行为缺陷 → 三个用例各自精准变红。
|
||||
> 迁移基线 38 → 39(新增 `V039`)。证据:`docs/Phase-13A-V11-Recovery-Completion-Report-2026-09-17.md`。
|
||||
> 后端全量 **420 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(4 分 49 秒)。
|
||||
> **Phase 14 需用户确认后启动。**
|
||||
>
|
||||
> **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 上用户接受/跳过推荐必崩**;
|
||||
@@ -80,6 +102,7 @@
|
||||
| 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 公益红娘转介复验 | 已完成(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 尚未获准** |
|
||||
| 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 尚未获准** |
|
||||
| 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 通过用户验收 |
|
||||
@@ -114,7 +137,7 @@
|
||||
| EPIC-P11-V11 | P11 | 公益服务通知扩展 | P0 | ✅
|
||||
| EPIC-P12-V11 | P12 | 关系状态轻量扩展 | P1 | ✅
|
||||
| EPIC-P13-V11 | P13 | 红娘服务转介扩展 | P1 | ✅
|
||||
| EPIC-P13A | P13A | 公益婚恋咨询与婚前辅导 | P1 |
|
||||
| EPIC-P13A | P13A | 公益婚恋咨询与婚前辅导 | P1 | ✅
|
||||
| EPIC-P14-V11 | P14 | 多类型公益活动 | P1 |
|
||||
| EPIC-P15-V11 | P15 | 活动签到回归测试 | P1 |
|
||||
| EPIC-P16-V11 | P16 | 活动互选二阶段同意重构 | P0 |
|
||||
@@ -305,56 +328,68 @@
|
||||
|
||||
---
|
||||
|
||||
## EPIC-P13A: 公益婚恋咨询与婚前辅导 ✅ 核心链路已完成(V1.1 MVP)
|
||||
## EPIC-P13A: 公益婚恋咨询与婚前辅导 ✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验关闭)
|
||||
|
||||
### Story: 数据模型
|
||||
- P13A-T01 counseling_services migration ✅ (V022)
|
||||
- P13A-T02 counseling_followups migration ✅ (V022)
|
||||
- P13A-T03 indexes ✅ (V022)
|
||||
- P13A-T04 migration tests ⏳ 待补全
|
||||
- P13A-T04 migration tests ✅ 已补全(`MigrationValidationTest` 基线 38 → 39;测试库补 3 个 CHECK 约束 + 新建 `counseling_followups` 表)
|
||||
- P13A-T04b **V039 幂等键 + CHECK 白名单显式固化** ✅ 新增(`idempotencyKey` 列 + `uk_counseling_idem` 唯一索引)
|
||||
|
||||
### Story: 用户咨询
|
||||
- P13A-T05 GET counseling service intro ✅
|
||||
- P13A-T06 POST counseling request ✅
|
||||
- P13A-T07 GET my counseling ✅
|
||||
- P13A-T08 GET counseling detail ✅
|
||||
- P13A-T09 cancel counseling ✅
|
||||
- P13A-T05 GET counseling service intro ✅ **修复:后端端点此前不存在**(被 `/services/{id}` 吞掉)→ 已补并置于变量路径之前
|
||||
- P13A-T06 POST counseling request ✅ **修复:userId 改从认证主体取;补幂等键;字段名对齐**
|
||||
- P13A-T07 GET my counseling ✅ **修复:检查 `success`(403 静默空列表);移除跳转不存在的页**
|
||||
- P13A-T08 GET counseling detail ✅ **修复:补 owner 校验 + 读侧分层(INV-013)**
|
||||
- P13A-T09 cancel counseling ✅ **修复:补 owner 校验 + 合法状态迁移**
|
||||
|
||||
### Story: 婚前辅导
|
||||
- P13A-T10 premarital service type ✅ (service_type=premarital_guidance)
|
||||
- P13A-T11 premarital intro ✅ (复用 counseling/intro + U58页面)
|
||||
- P13A-T12 premarital request ✅ (复用 counseling/create,serviceType=premarital_guidance)
|
||||
- P13A-T13 optional companion support ✅ (companionUserId 字段)
|
||||
- P13A-T13 optional companion support ✅ **修复:补 `confirmCompanion`,伴侣须本人确认(INV-005)**
|
||||
|
||||
### Story: Miniapp
|
||||
- P13A-T14 U55 婚恋咨询介绍 ✅
|
||||
- P13A-T15 U56 申请咨询 ✅
|
||||
- P13A-T16 U57 我的咨询 ✅
|
||||
- P13A-T15 U56 申请咨询 ✅ **修复:字段名 `summary`/`preferredMode` + 幂等键**
|
||||
- P13A-T16 U57 我的咨询 ✅ **修复:检查 `success` + Loading/Empty/Error/Permission 四态 + 下拉刷新**
|
||||
- P13A-T17 U58 婚前辅导介绍 ✅
|
||||
- P13A-T18 U59 婚前辅导申请 ✅
|
||||
- P13A-T18 U59 婚前辅导申请 ✅ **修复:字段名对齐 + `companionUserId` 输入**
|
||||
|
||||
### Story: 后台
|
||||
- P13A-T19 Counseling Queue ✅ (getPendingQueue)
|
||||
- P13A-T20 Counseling Detail ✅ (getCounselingDetail)
|
||||
- P13A-T21 Assign Counselor ✅
|
||||
- P13A-T22 Schedule ✅
|
||||
- P13A-T23 Start ✅
|
||||
- P13A-T24 Followup ✅
|
||||
- P13A-T25 Complete ✅
|
||||
- P13A-T19 Counseling Queue ✅ `GET /services/operator/queue`
|
||||
- P13A-T20 Counseling Detail ✅ `GET /services/operator/{id}`
|
||||
- P13A-T21 Assign Counselor ✅ **修复:改 `assignIfPending` 条件更新(并发防静默覆盖)**
|
||||
- P13A-T22 Schedule ✅ **修复:补状态迁移断言 + INV-012 归属校验**
|
||||
- P13A-T23 Start ✅ **修复:同上**
|
||||
- P13A-T24 Followup ✅ **修复:同上**
|
||||
- P13A-T25 Complete ✅ **修复:同上**
|
||||
- P13A-T26 Close ✅
|
||||
- P13A-T26b **Admin Web `CounselingView.vue` 重写 + `/counseling` 路由注册** ✅ 新增(四态 + 二次确认 + 防重复提交)
|
||||
|
||||
### Story: 权限与隐私
|
||||
- P13A-T27 counselor assigned scope ✅ (getCounselorAssignedServices)
|
||||
- P13A-T28 counseling_manager scope ⏳ 待补充(Admin Web Phase 19)
|
||||
- P13A-T29 matchmaker deny counseling notes ✅ (常量隔离,INV-013)
|
||||
- P13A-T30 generic admin deny sensitive note ⏳ 待补充(Admin Web Phase 19)
|
||||
- P13A-T31 SensitiveAccessLog ⏳ 待补充(随访内容写入审计日志)
|
||||
- P13A-T28 counseling_manager scope ✅ **已补全**(可看申请摘要,但**不装配 followups** —— 避免 INV-013 绕过通道)
|
||||
- P13A-T29 matchmaker deny counseling notes ✅ **已由行为测试证明**(INV-013 七用例)
|
||||
- P13A-T30 generic admin deny sensitive note ✅ **已由行为测试证明**(`strangerGetsNothing` / `plainOperatorSeesSanitizedDetail`)
|
||||
- P13A-T31 SensitiveAccessLog ⏳ 待补充(随访内容写入审计日志)—— **不阻塞本 Phase Gate**
|
||||
|
||||
### Story: 风控联动
|
||||
- P13A-T32 counseling risk referral ⏳ 待补充(后续 Phase)
|
||||
- P13A-T32 counseling risk referral ✅ **已接线**(`RiskReferralService.detectCounselingRisk`;**修复:不再把正文写入日志**)
|
||||
- P13A-T33 RiskEvent source=counseling ⏳ 待补充(后续 Phase)
|
||||
|
||||
**备注**:V1.1 MVP 核心链路已实现,Admin Web 前端、高级权限细分、风控联动在后续 Phase 补充。
|
||||
### Story: 复验(2026-09-17 KEEP-VERIFY/EXTEND)
|
||||
- P13A-T34 删除孤儿类 `CounselingServiceApp` 及 3 个仅其使用的 DTO ✅
|
||||
- P13A-T35 删除测死代码的 `CounselingDataScopeTest` ✅
|
||||
- P13A-T36 新增 `CounselingPhase13ATest`(25 用例,真实运行时路径)✅
|
||||
- P13A-T37 新增 `CounselingConstraintGuardTest`(5 用例,**解析真实 `db/migration/V*.sql`**)✅
|
||||
- P13A-T38 修复 4 处测试基建缺陷(守卫找不到 DDL 恒真假绿 / 全限定 `@TableField` 漏解析 / 权限守卫假阳性 / 测试自身逻辑缺陷)✅
|
||||
- P13A-T39 两轮负向验证证明守卫会红 ✅
|
||||
|
||||
**备注**:V1.1 核心链路已实现并完成 KEEP-VERIFY/EXTEND 复验,修复 9 处真实缺陷。
|
||||
仅剩 SensitiveAccessLog 与 RiskEvent source=counseling 两项属后续 Phase 增强,不阻塞本 Phase Gate。
|
||||
后端全量 **420 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# Phase 13A V1.1 Recovery Completion Report — 婚恋咨询与婚前辅导
|
||||
|
||||
> 日期:2026-09-17
|
||||
> 阶段:Phase 13A(Counseling & Premarital Guidance — REFACTOR/EXTEND)
|
||||
> 分支:`main` | Tag:`phase13a-recovery-2026-09-17`
|
||||
> 结论:**CLOSED(Gate 全项通过)**
|
||||
|
||||
---
|
||||
|
||||
## 一、计划目标与 Gate
|
||||
|
||||
计划出处:`docs/善缘丹阳 V1.1 客户需求基线增量开发与修复计划.md` 第 308-315 行。
|
||||
|
||||
| 计划项 | 要求 | 结论 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 计划项 1 | 修复 Admin Vue 语法错误并注册 `/counseling` 路由,先使 type-check 失败转为通过 | ✅ 通过 | `router/index.ts:36` 已注册;`vue-tsc --noEmit` EXIT=0 |
|
||||
| 计划项 2 | 用户申请接口从认证主体取 userId;补幂等、取消、详情 owner 校验和合法状态迁移 | ✅ 通过 | D2/D5/D6 + `applyUsesAuthenticatedPrincipalUserId` / `applyIsIdempotentByKey` / `onlyOwnerCanCancel` 等 8 用例 |
|
||||
| 计划项 3 | 分配操作使用条件更新/版本号解决并发;Counselor 只能读取和操作 Assigned Service | ✅ 通过 | D3(`assignIfPending`)+ INV-012 四用例 |
|
||||
| 计划项 4 | Counseling Manager 可分配和查看必要摘要;普通管理员、红娘、内容编辑不得读取咨询正文 | ✅ 通过 | D4 + INV-013 七用例 |
|
||||
| 计划项 5 | 婚前辅导复用 counseling service type;邀请平台内伴侣必须由对方自主确认 | ✅ 通过 | INV-005 四用例 |
|
||||
| 计划项 6 | U55~U59 修复类型错误、API 契约、Loading/Empty/Error/Permission 状态和非医疗化文案 | ✅ 通过 | D7/D8 + 前端双项目 type-check EXIT=0 |
|
||||
|
||||
**Gate:** 申请→分配→预约→服务→完成→反馈真实闭环通过;所有隐私负向用例通过。
|
||||
|
||||
| Gate 断言 | 证据 | 结果 |
|
||||
|---|---|---|
|
||||
| 真实闭环打通 | `fullLifecycleClosedLoop`:apply→assign→schedule→start→addFollowup→complete,全程真实落库,终态 `completed` 且 `completedAt` 非空 | ✅ |
|
||||
| INV-012(Counselor 只能操作自己的) | `nonAssignedCounselorCannotSchedule` / `nonAssignedCounselorCannotAddFollowup` / `nonAssignedCounselorCannotComplete` / `assignedListOnlyContainsOwnServices` | ✅ |
|
||||
| INV-013(红娘/普通运营不可读正文) | `matchmakerSeesSanitizedDetail` / `plainOperatorSeesSanitizedDetail` / `managerSeesSummaryButNotFollowups` / `strangerGetsNothing` / `listPathsAreSanitized` | ✅ |
|
||||
| INV-005(不得替用户确认) | `companionCanConfirmBySelf` / `staffCannotConfirmOnBehalfOfCompanion` / `companionConfirmOnlyForPremarital` / `companionConfirmIsIdempotent` | ✅ |
|
||||
| 并发安全 | `concurrentAssignDoesNotSilentlyOverwrite` | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 二、复验发现:counseling 模块存在**双实现**,其中一个是从未被调用的孤儿类
|
||||
|
||||
| 类 | 行数 | 注入点 | HTTP 端点可达 | 测试 |
|
||||
|---|---|---|---|---|
|
||||
| `CounselingService` | 167 | 有(Controller 注入) | 是 | 无直接测试 |
|
||||
| `CounselingServiceApp` | 280 | **零** | **否** | 仅 `CounselingDataScopeTest` 直接 `new` 它 |
|
||||
|
||||
历史 ✅ 声称「counseling 模块已实现并通过测试」,实际情况是:
|
||||
|
||||
- **线上权威路径** `CounselingService` 只有 167 行,**没有任何测试**;
|
||||
- 唯一被测的 `CounselingServiceApp` 是**孤儿类**——无任何注入点、无 HTTP 端点可达。
|
||||
它的 280 行业务逻辑在生产上**永远不会被执行**;
|
||||
- 即「测试覆盖了函数」≠「测试覆盖了运行时路径」——这是同一族陷阱的第七次复现。
|
||||
|
||||
**处理**:将 App 的流程合并入 `CounselingService` 并**删除 App**,连同仅 App 使用的
|
||||
`CounselingDTO` / `CounselingRequest` / `FollowupDTO` 与测死代码的 `CounselingDataScopeTest`。
|
||||
|
||||
---
|
||||
|
||||
## 三、缺陷清单(D1~D9)
|
||||
|
||||
### D1 — Critical(生产故障 · INV-013 泄露):咨询正文被写入日志
|
||||
|
||||
- **位置**:`CounselingServiceApp.java:81`
|
||||
- **代码**:`log.warn("咨询内容触发高危风险: userId={}, summary={}", userId, request.getUserRequestSummary())`
|
||||
- **后果**:用户自述的敏感咨询内容**原文进入应用日志**(落盘、进日志采集、被运维可见),
|
||||
直接违反 INV-013「Matchmaker / 普通 operator 不能读取 Counseling 正文」的数据边界。
|
||||
与 Phase 10 D2 属同一族。
|
||||
- **修复**:合并入 `CounselingService.apply` 后改为只记 `serviceId` + `riskLevel`,
|
||||
**绝不记录 summary 原文**;`followups.content` 亦不落日志。
|
||||
|
||||
### D2 — High(越权):申请接口的 userId 取自请求体而非认证主体
|
||||
|
||||
- **位置**:`CounselingServiceApp.createCounseling`
|
||||
- **后果**:攻击者可伪造 `userId` 为他人代提咨询申请,污染他人服务记录,
|
||||
且可能借他人身份触发风险检测(把风险记到别人头上)。
|
||||
`dataScopeGuard` 虽已注入但**未用于申请路径**。
|
||||
- **修复**:Controller 强制 `principal.id()`,`apply()` 的 `userId` 只能来自认证主体。
|
||||
|
||||
### D3 — High(并发):分配使用「先读后写」,并发下静默覆盖
|
||||
|
||||
- **位置**:`CounselingServiceApp.assignCounselor`
|
||||
- **代码形态**:`selectById` → `setStatus/setAssignedOperatorId` → `updateById`
|
||||
- **后果**:两个管理员**同时**把同一单分配给不同咨询师时,后写者**静默覆盖**先写者;
|
||||
先被分配的咨询师可能已经收到通知并开始服务,造成真实责任错乱。
|
||||
- **修复**:新增 `assignIfPending(serviceId, counselorId)` 条件更新
|
||||
(`WHERE status='pending' AND assigned_operator_id IS NULL`),
|
||||
`affectedRows=0` 即明确报「已被分配或状态已变更」,不做盲目覆盖。
|
||||
|
||||
### D4 — High(越权读):`counseling_manager` 成为 INV-013 的绕过通道
|
||||
|
||||
- **位置**:`getDetail` 的 manager 分支
|
||||
- **后果**:manager 经 `getDetail` 可读取 `followups` 正文(跟进记录含最敏感内容)。
|
||||
计划项 4 明确「Manager 可查看**必要摘要**」,不是全文。
|
||||
- **修复**:manager 分支只加回 `userRequestSummary`(供分派判断),
|
||||
**不装配 followups**。
|
||||
|
||||
### D5 — Medium(状态迁移缺失):无合法状态机
|
||||
|
||||
- **后果**:任意状态可被改成任意状态。例如「已完成」的服务可被再次「取消」写回,
|
||||
历史统计与责任归属被破坏。
|
||||
- **修复**:新增 `ALLOWED_TRANSITIONS` 状态机 + `assertTransition`,
|
||||
终态(`completed`/`cancelled`/`closed`)无出边。
|
||||
|
||||
### D6 — Medium(生产故障 · 契约缺口):`GET /counseling/services/intro` 后端不存在
|
||||
|
||||
- **后果**:两个小程序 intro 页 **100% 拿不到数据**。请求被 `/services/{id}` 吞掉后
|
||||
以 `id="intro"` 查库 → 403/空,**属生产功能完全不可用**。
|
||||
- **修复**:Controller 新增该端点,并**声明在 `/services/{id}` 之前**
|
||||
(Spring MVC 字面量路径必须优先于变量路径,否则被吞)。
|
||||
|
||||
### D7 — Medium(契约缺口):Admin 调用 5 条不存在的运营端端点
|
||||
|
||||
- **位置**:`admin-web/src/modules/counseling/CounselingView.vue`
|
||||
- **后果**:5 条 `/counseling/services/operator/*` 请求全部 404;
|
||||
且缺 Loading/Empty/Error/Permission 四态,权限不足时静默白屏。
|
||||
- **修复**:后端补 8 个运营端端点(`@PreAuthorize` + `OperatorPrincipal`);
|
||||
前端整体重写(补 `el-skeleton`/`el-empty`/`el-alert`/二次确认/防重复提交)。
|
||||
|
||||
### D8 — Medium(契约缺口):小程序申请页字段名与后端不匹配 + 无幂等 + 静默失败
|
||||
|
||||
- **问题**:申请页提交 `userRequestSummary` / `preferredServiceMode`,后端只认
|
||||
`summary` / `preferredMode`;无幂等键(连点产生重复申请);
|
||||
`my-counseling` 列表**未检查 `res.data.success`** → 403 时静默显示空列表;
|
||||
跳转到**不存在**的 `counseling-detail` 页;WXML 内直接调用方法(非法用法)。
|
||||
- **修复**:字段名对齐 + 补 `idempotencyKey` + 检查 `success` +
|
||||
移除无效跳转 + 改为预计算字段(`statusName`/`createdText`/`scheduledText`)。
|
||||
|
||||
### D9 — Medium(测试保真度):测试库 counseling 表零 CHECK 约束
|
||||
|
||||
- **问题**:`counseling_services` / `counseling_followups` 在 `schema-h2.sql` 中
|
||||
**完全缺失 CHECK 约束**(V022 的 3 个 CHECK 未下沉);
|
||||
`counseling_services` 未被列入 `EntityMigrationParityTest` 任何集合(= 零覆盖),
|
||||
`counseling_followups` 仅在「可选白名单」(= 实际无人测)。
|
||||
- **后果**:状态/类型越界在测试期不可见 → 测试全绿但生产 500。
|
||||
- **修复**:`schema-h2.sql` 补 3 个 CHECK + `uk_counseling_idem` + 新建
|
||||
`counseling_followups` 表;两表移入 `REQUIRED_IN_TEST_SCHEMA`。
|
||||
|
||||
---
|
||||
|
||||
## 四、复验中发现的**测试基建缺陷**(守卫自身失效,非生产缺陷)
|
||||
|
||||
Phase 13A 严格执行「守卫必须变红」纪律时,暴露了 3 处**守卫/测试自身**的缺陷。
|
||||
它们不修就会产生「恒真假绿」,危害等同于生产缺陷。
|
||||
|
||||
### T1 — `CounselingConstraintGuardTest` 5 个用例全红:守卫找不到 DDL
|
||||
|
||||
- **现象**:首轮 5 个用例全部报「未解析到约束」。
|
||||
- **根因**:`pom.xml` 的 surefire 配置了
|
||||
`<workingDirectory>${project.basedir}/..</workingDirectory>` ——
|
||||
测试进程 CWD 是**仓库根**而非 `backend/`。
|
||||
更隐蔽的是 `backend/` 是 CWD 的**子目录**而非祖先,
|
||||
所以「只向上回溯找 `src/...`」也找不到(实测报错 `CWD=/proj`)。
|
||||
- **修复**:改为**候选后缀 + 逐级向上**双维度定位
|
||||
(`src/main/resources/db/migration` 与 `backend/src/main/resources/db/migration`),
|
||||
定位失败**大声抛异常**而不是静默返回 null。
|
||||
- **加固**:新增文件数自检(`fileCount >= 30`),
|
||||
避免「目录选错」与「DDL 真没有」无法区分。
|
||||
|
||||
### T2 — `EntityMigrationParityTest` 全限定注解漏解析(假警报)
|
||||
|
||||
- **现象**:报 `counseling_services: 生产缺列 [followups]`。
|
||||
- **根因**:`@TableField(exist = false)` 写成了**全限定形式**
|
||||
`@com.baomidou.mybatisplus.annotation.TableField(exist = false)`,
|
||||
而旧正则只匹配简单形式 `@TableField(...)` → 非持久化字段被误判为真实列。
|
||||
- **危害**:若为消除该假警报而去生产库**加一个无用列**,就是测试保真度反噬成生产污染。
|
||||
- **修复**:正则放宽为 `@[\w.]*TableField\(...\)`;
|
||||
并新增回归用例 `fullyQualifiedTableFieldExistFalseMustBeExcluded`。
|
||||
|
||||
### T3 — `OperatorEndpointPermissionCoverageTest` 扫到私有辅助方法(假阳性)
|
||||
|
||||
- **现象**:报 `CounselingController.roleOf` 缺 Permission。
|
||||
- **根因**:`roleOf(OperatorPrincipal)` 是**私有工具方法**(把 principal 映射成角色字符串),
|
||||
不是 HTTP 端点,也不可能加 `@PreAuthorize`;
|
||||
却因「参数含 `OperatorPrincipal`」被误判为未受保护的接口。
|
||||
- **修复**:先确认方法是**真正的端点**(`findMergedAnnotation(RequestMapping)` 能取到路径)再判定;
|
||||
并新增自检用例 `guardMustActuallyInspectOperatorEndpoints`(≥10 个端点)。
|
||||
|
||||
### T4 — 两处测试自身逻辑缺陷(非生产缺陷)
|
||||
|
||||
| 用例 | 误报原因 | 修复 |
|
||||
|---|---|---|
|
||||
| `matchmakerSeesSanitizedDetail` | 用「测试内存里的旧快照」(status 仍 `pending`)去比「重新查库的最新值」(`in_progress`) | 辅助方法改为返回**重新查库**的同源快照(`reload`) |
|
||||
| `companionConfirmIsIdempotent` | H2 timestamp 精度低于 `Instant.now()` 纳秒,导致 470ns 级差异误报 | 比较**截断到毫秒**的表现值 |
|
||||
|
||||
---
|
||||
|
||||
## 五、负向验证(守卫必须变红 —— 两轮,均已还原)
|
||||
|
||||
### 第一轮:DDL 常量守卫
|
||||
|
||||
注入缺陷:从 `V039` 的 `chk_counseling_status` 白名单中**移除** `scheduled`。
|
||||
|
||||
```
|
||||
[ERROR] CounselingConstraintGuardTest.statusMachineWithinProductionWhitelist:162
|
||||
状态机使用了不在生产 CHECK 白名单内的状态值:
|
||||
- scheduled
|
||||
生产白名单: [assigned, cancelled, closed, completed, in_progress, pending]
|
||||
[ERROR] Tests run: 48, Failures: 1, Errors: 0 → BUILD FAILURE
|
||||
```
|
||||
|
||||
**关键结论**:守卫**正确解析了被篡改的 V039 原文**并识别出漂移 ——
|
||||
证明它确实在解析**真实 DDL**,而不是在测试里抄了一份副本。
|
||||
(这正是 Phase 13 确立的第四层纪律:抄副本的守卫对 DDL 漂移零反应。)
|
||||
|
||||
### 第二轮:行为守卫(3 个注入缺陷,3 个用例精准变红)
|
||||
|
||||
| 注入缺陷 | 变红用例 | 失败信息(节选) |
|
||||
|---|---|---|
|
||||
| **A** `assign` 退回「先读后写」 | `concurrentAssignDoesNotSilentlyOverwrite:182` | `Expected IllegalStateException to be thrown, but nothing was thrown` |
|
||||
| **B** manager 也装配 followups | `managerSeesSummaryButNotFollowups:302` | `INV-013: 经理不得成为阅读跟进正文的绕过通道 ==> expected: <null> but was: <[CounselingFollowup(...content=跟进正文内容(敏感)...)]>` |
|
||||
| **C** `assertTransition` 变空操作 | `completedServiceCannotBeCancelled:143` | `Expected IllegalStateException to be thrown, but nothing was thrown` |
|
||||
|
||||
```
|
||||
[ERROR] Tests run: 48, Failures: 3, Errors: 0 → BUILD FAILURE
|
||||
```
|
||||
|
||||
**结论**:三个行为不变量(并发安全 / INV-013 / 状态机)的守卫均**经负向验证证明会变红**,
|
||||
不是"永不失效的假守卫"。两轮注入的缺陷**已全部还原**(`grep -c "NEG-VAL"` = 0)。
|
||||
|
||||
### 额外发现(诚实记录)
|
||||
|
||||
第一轮同时移除了 `schema-h2.sql` 的 `chk_counseling_status` CHECK,
|
||||
但**没有任何行为用例因此变红**。说明:**当前测试并未依赖测试库 CHECK 来兜底状态合法性**
|
||||
——状态合法性是由 Service 层的 `assertTransition` 保证的。
|
||||
这是一个值得记录的边界:测试库 CHECK 的价值在于「防止绕过 Service 层的直接 SQL 写入」,
|
||||
而非替代 Service 层校验。已在报告中如实标注,未夸大其作用。
|
||||
|
||||
---
|
||||
|
||||
## 六、全量回归实测
|
||||
|
||||
| 项 | 数值 |
|
||||
|---|---|
|
||||
| 后端全量测试 | **420 tests · 0 failures · 0 errors · 3 skipped** |
|
||||
| 构建结果 | `BUILD SUCCESS`(4 分 49 秒) |
|
||||
| Phase 13 基线 | 391 |
|
||||
| 本次净增 | **+29** |
|
||||
| `admin-web` `vue-tsc --noEmit` | EXIT=0 |
|
||||
| `miniapp` `tsc --noEmit` | EXIT=0 |
|
||||
|
||||
新增/重写测试文件:
|
||||
|
||||
| 文件 | 用例数 | 说明 |
|
||||
|---|---|---|
|
||||
| `CounselingPhase13ATest` | 25 | Gate 闭环 + 计划项 2/3/4/5 全项 + INV-012/013/005 |
|
||||
| `CounselingConstraintGuardTest` | 5 | 解析真实 DDL 的常量守卫 + 解析器自检 |
|
||||
| `EntityMigrationParityTest`(+1) | 6 | 新增全限定注解回归守卫 |
|
||||
| `OperatorEndpointPermissionCoverageTest`(+1) | 2 | 新增端点覆盖自检 |
|
||||
|
||||
删除的测试(测死代码 / 名不副实):`CounselingDataScopeTest`。
|
||||
|
||||
---
|
||||
|
||||
## 七、生产迁移
|
||||
|
||||
**新增 `V039__add_counseling_idempotency_and_status_guards.sql`**:
|
||||
|
||||
1. `ALTER TABLE counseling_services ADD COLUMN IF NOT EXISTS idempotency_key VARCHAR(128)`
|
||||
2. `CREATE UNIQUE INDEX IF NOT EXISTS uk_counseling_idem ON counseling_services(user_id, idempotency_key) WHERE idempotency_key IS NOT NULL`
|
||||
3. 按既有惯例(V018/V020/V025/V038)`DROP CONSTRAINT IF EXISTS` + 重加
|
||||
`chk_request_source` / `chk_counseling_status` / `chk_counseling_service_type`
|
||||
4. `COMMENT ON COLUMN` 说明 INV-005 与幂等语义
|
||||
|
||||
> `LATEST_MIGRATION` 38 → 39。
|
||||
> H2 不支持 partial index,`schema-h2.sql` 用标准 `UNIQUE` 表达
|
||||
> (PG/H2 的标准 UNIQUE 均允许多行 NULL 共存,语义等价)。
|
||||
|
||||
---
|
||||
|
||||
## 八、结论
|
||||
|
||||
Phase 13A **CLOSED**。9 处缺陷(D1~D9)全部修复并验证;
|
||||
4 处测试基建缺陷(T1~T4)一并修复;
|
||||
两轮负向验证证明常量守卫与行为守卫**都会变红**;
|
||||
全量回归 **420 tests 全绿**;前后端 type-check 均通过。
|
||||
|
||||
**Phase 14(多类型公益活动 — REFACTOR/EXTEND)未自动进入,等待用户确认。**
|
||||
+68
-15
@@ -1,10 +1,10 @@
|
||||
# 项目状态跟踪 — STATUS.md
|
||||
|
||||
> **2026-09-16 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 4 COMPLETE / UI REDESIGN ACCEPTED BY USER / PHASE 08-13 RECOVERY CLOSED / GIT CLEAN`。**
|
||||
> **2026-09-17 当前有效结论:`V1.1 INCREMENTAL RECOVERY PHASE 13A 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(公益红娘转介)复验已关闭**,六轮共修复 23 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 13A 需用户确认后启动。**
|
||||
> 阶段 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(婚恋咨询与婚前辅导)复验已关闭**,七轮共修复 32 处真实缺陷,并各自补齐测试保真度与守卫。**Phase 14 需用户确认后启动。**
|
||||
|
||||
最后更新: 2026-09-16 — 分支 `main`(`feat-ui-v11` 保留为冗余锚点)
|
||||
最后更新: 2026-09-17 — 分支 `main`(`feat-ui-v11` 保留为冗余锚点)
|
||||
|
||||
## 当前唯一有效状态
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
| 阶段 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~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` |
|
||||
| Flyway 迁移 | V001~V039 | V028~V038 已纳入 STATUS;V037 为 Phase 12 新增(实体列与生产迁移对齐,23 表 50 列);V038 为 Phase 13 新增(`matchmaker_recommendations.status` + `matchmaker_services.service_type` 的 CHECK 白名单扩展);V039 为 Phase 13A 新增(counseling 幂等键 + 三个 CHECK 白名单显式固化) |
|
||||
| Backend 全量回归 | **已清偿**(2026-09-16) | 离线容器(`danyang-match-backend-test`)`mvn -o test` 实测:**420 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS**(4 分 49 秒);含 `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 全量回归(391 tests)、DevTools 真机截图留档(2026-09-14)、`/me/*` 路径族收敛(2026-09-16)、实体↔生产迁移漂移修复(V037/V038,2026-09-16) |
|
||||
| 核心待清债务 | 0 项 | 已全部清偿:3 页 JS→TS 迁移、backend 全量回归(420 tests)、DevTools 真机截图留档(2026-09-14)、`/me/*` 路径族收敛(2026-09-16)、实体↔生产迁移漂移修复(V037/V038/V039,2026-09-16/17) |
|
||||
| UAT / RC / Production | **UI 验收通过,前后端契约已收敛,Phase 08-13 已关闭** | 双端构建/契约/源码全部通过;UI 首批 11 页 2026-09-16 通过用户验收;`/me/*` 契约同轮收敛;Phase 08/09/10/11/12/13 复验通过并关闭,累计修复 23 处真实缺陷,可推进 Phase 13A(**需用户确认后启动**) |
|
||||
|
||||
---
|
||||
@@ -186,17 +186,23 @@
|
||||
| ReviewService 集成本地资格审核通知 | ✅ |
|
||||
| 隐私安全文案(中性化,不暴露敏感信息) | ✅ |
|
||||
|
||||
### Phase 13A — 公益婚恋咨询与婚前辅导(核心链路)✅ 已完成
|
||||
### Phase 13A — 公益婚恋咨询与婚前辅导(核心链路)✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验通过)
|
||||
| 任务 | 状态 |
|
||||
|------|------|
|
||||
| V022 counseling_services + counseling_followups 迁移 | ✅ |
|
||||
| CounselingServiceApp(Service/Controller/DTO) | ✅ |
|
||||
| 用户端 API:intro / create / list / detail / cancel | ✅ |
|
||||
| 后台 API:queue / assign / schedule / start / followup / complete / close | ✅ |
|
||||
| INV-012 Counselor assigned scope 实现 | ✅ |
|
||||
| Miniapp U55-U59 页面(咨询介绍/申请/我的/婚前辅导) | ✅ |
|
||||
| app.json 路由注册 | ✅ |
|
||||
| companionUserId 伴侣支持 | ✅ |
|
||||
| **V039 Phase 13A 迁移**(counseling 幂等键 + 三个 CHECK 白名单显式固化) | ✅ 新增 |
|
||||
| `CounselingService`(权威实现,合并孤儿类 `CounselingServiceApp` 后删除) | ✅ 重写 |
|
||||
| 用户端 API:intro / create / list / detail / cancel / companion-confirm | ✅ |
|
||||
| 后台 API:queue / assigned / detail / assign / schedule / start / followup / complete | ✅ 8 端点 |
|
||||
| INV-012 Counselor assigned scope 实现(`assignIfPending` 条件更新防并发覆盖) | ✅ |
|
||||
| INV-013 正文保护(脱敏分层:user/assigned=全文;matchmaker/operator=脱敏;manager=摘要无 followups) | ✅ |
|
||||
| INV-005 伴侣须本人确认 | ✅ |
|
||||
| 合法状态机 `ALLOWED_TRANSITIONS` + `assertTransition` | ✅ 新增 |
|
||||
| 幂等(`idempotencyKey` + `uk_counseling_idem` 唯一索引) | ✅ 新增 |
|
||||
| Miniapp U55-U59 页面(咨询介绍/申请/我的/婚前辅导),字段名对齐后端 + 检查 `success` | ✅ |
|
||||
| Admin `CounselingView.vue` 重写(四态 + 二次确认)+ `/counseling` 路由注册 | ✅ |
|
||||
| 测试:`CounselingPhase13ATest`(25)+ `CounselingConstraintGuardTest`(5) | ✅ 新增 |
|
||||
| 全量回归 **420 tests 全绿** | ✅ |
|
||||
|
||||
### Phase 13 — 公益红娘服务转介 ✅ 已完成
|
||||
| 任务 | 状态 |
|
||||
@@ -299,6 +305,53 @@
|
||||
> `assertTrue(true)` → 已删除占位、如实改名并注明行为验证位置。
|
||||
> - 报告:`docs/Phase-13-V11-Recovery-Completion-Report-2026-09-16.md`
|
||||
|
||||
### Phase 13A — 婚恋咨询与婚前辅导 ✅ 已完成(2026-09-17 KEEP-VERIFY/EXTEND 复验通过)
|
||||
|
||||
| 计划项 | 状态 |
|
||||
|------|------|
|
||||
| 计划项 1 Admin Vue 修复 + `/counseling` 路由注册 | ✅ `vue-tsc` EXIT=0 |
|
||||
| 计划项 2 认证主体 userId + 幂等 + 取消 + owner 校验 + 状态迁移 | ✅ 8 用例 |
|
||||
| 计划项 3 条件更新解决并发 + Assigned Service 边界 | ✅ `assignIfPending` + 4 用例 |
|
||||
| 计划项 4 Manager 可摘要不可读正文 | ✅ 7 用例 |
|
||||
| 计划项 5 婚前辅导复用 service type + 伴侣自主确认 | ✅ 4 用例 |
|
||||
| 计划项 6 U55~U59 类型/契约/四态/非医疗化文案 | ✅ 双端 type-check EXIT=0 |
|
||||
|
||||
> **2026-09-17 复验结论(KEEP-VERIFY/EXTEND)**:历史 ✅ 不可信。counseling 模块存在
|
||||
> **双实现**——线上权威 `CounselingService`(167 行)**零测试**,唯一被测的
|
||||
> `CounselingServiceApp`(280 行)是**孤儿类**(无注入点、无端点可达,生产永不执行)。
|
||||
> 已将其流程合并入 `CounselingService` 并删除孤儿类及仅其使用的 3 个 DTO 与测死代码的测试。
|
||||
> 修复 **9 处缺陷**:
|
||||
> - **D1(Critical,生产故障 · INV-013 泄露)** 咨询正文 `userRequestSummary` **原文被写入 WARN 日志**
|
||||
> (`CounselingServiceApp:81`,同 Phase 10 D2 一族)。修复:合并后只记 `serviceId` + `riskLevel`。
|
||||
> - **D2(High,越权)** 申请 `userId` 取自**请求体**而非认证主体 → 可替他人提交申请。
|
||||
> 修复:Controller 强制 `principal.id()`。
|
||||
> - **D3(High,并发)** `assignCounselor` 用「先读后写」→ 两管理员并发分配**静默覆盖**。
|
||||
> 修复:`assignIfPending` 条件更新(`WHERE status='pending' AND assigned_operator_id IS NULL`),
|
||||
> `affectedRows=0` 明确报错。
|
||||
> - **D4(High,越权读)** `counseling_manager` 经 `getDetail` 可读 `followups` 正文 →
|
||||
> **INV-013 绕过通道**。修复:manager 只加回 `userRequestSummary`,不装配 followups。
|
||||
> - **D5(Medium)** 无状态机 → 「已完成」可被再取消。修复:`ALLOWED_TRANSITIONS` + `assertTransition`。
|
||||
> - **D6(Medium,生产故障)** `GET /counseling/services/intro` **后端不存在**,小程序 intro 页
|
||||
> 100% 无数据(且被 `/services/{id}` 吞掉)。修复:补端点并**声明在变量路径之前**。
|
||||
> - **D7(Medium,契约)** Admin 调用 5 条不存在的 `/services/operator/*`(404)+ 缺四态。
|
||||
> 修复:后端补 8 个运营端端点(`@PreAuthorize`),前端整体重写。
|
||||
> - **D8(Medium,契约)** 小程序字段名错(`userRequestSummary`/`preferredServiceMode`)+ 无幂等
|
||||
> + `my-counseling` **未检查 `success`**(403 静默空列表)+ 跳转不存在的页 + WXML 内调用方法(非法)。
|
||||
> - **D9(Medium,测试保真度)** 测试库 counseling 两表**零 CHECK 约束**,`counseling_services`
|
||||
> 未列入 `EntityMigrationParityTest` 任何集合(= 零覆盖)。修复:补 3 个 CHECK + 建
|
||||
> `counseling_followups` + 两表移入 `REQUIRED_IN_TEST_SCHEMA`。
|
||||
> - **另修 4 处测试基建缺陷(T1~T4)**:① 守卫因 surefire `<workingDirectory>${project.basedir}/..`
|
||||
> 指向**仓库根**而**找不到 DDL**(且 `backend/` 是子目录非祖先,回溯也找不到)→ 恒真假绿;
|
||||
> ② 全限定 `@com.baomidou...TableField(exist=false)` **漏解析** → 假漂移警报;
|
||||
> ③ `OperatorEndpointPermissionCoverageTest` 扫到**私有辅助方法**(假阳性);
|
||||
> ④ 两处测试自身逻辑缺陷(陈旧内存快照比较、Instant 纳秒 vs DB 微秒精度)。
|
||||
> - **负向验证两轮**:① 从 `V039` 移除 `scheduled` → 常量守卫**精准变红**
|
||||
> (证明其解析的是**真实 DDL** 而非副本);② 注入「并发回退先读后写 / manager 读 followups /
|
||||
> 状态机守卫失效」→ **三个用例各自精准变红**。均已还原(`grep -c "NEG-VAL"` = 0)。
|
||||
> - **全量回归**:`Tests run: 420, Failures: 0, Errors: 0, Skipped: 3 / BUILD SUCCESS`(4:49)。
|
||||
> 基线 391 → 420(净 +29)。
|
||||
> - 报告:`docs/Phase-13A-V11-Recovery-Completion-Report-2026-09-17.md`
|
||||
|
||||
### V1.1 待实现模块(按 Phase 顺序)
|
||||
|
||||
| 模块 | 对应 Phase | 状态 |
|
||||
@@ -307,7 +360,7 @@
|
||||
| Banner 管理(后端+前端) | Phase 19 | ⏳ 待开发 |
|
||||
| 本地资格声明与审核 | Phase 04~05 | ⏳ 待开发 |
|
||||
| 公益红娘服务转介 | Phase 13 | ✅ 已完成 |
|
||||
| 婚恋咨询 + 婚前辅导(Phase 13A) | Phase 13A | ⏳ 待开发(**需用户确认后启动**) |
|
||||
| 婚恋咨询 + 婚前辅导(Phase 13A) | Phase 13A | ✅ 已完成(2026-09-17 复验关闭) |
|
||||
| 内容管理(婚恋课堂/反诈) | Phase 19 | ⏳ 待开发 |
|
||||
| 专项公益服务项目 | Phase 19 | ⏳ 待开发 |
|
||||
| V1.1新增不变量测试(INV-011~020) | Phase 23 | ⏳ 待开发 |
|
||||
|
||||
@@ -307,15 +307,46 @@ D2——`RelationshipService.createMatch` 不写 episode/lastActivityAt,与 `M
|
||||
|
||||
**Files:** `counseling/**`、`miniapp/pages/counseling-*`、`miniapp/pages/my-counseling/**`、`miniapp/pages/premarital-*`、`admin-web/src/modules/counseling/CounselingView.vue`、router。
|
||||
|
||||
- [ ] 修复 Admin Vue 语法错误并注册 `/counseling` 路由,先使 type-check 失败转为通过。
|
||||
- [ ] 用户申请接口从认证主体取 userId;补幂等、取消、详情 owner 校验和合法状态迁移。
|
||||
- [ ] 分配操作使用条件更新/版本号解决并发;Counselor 只能读取和操作 Assigned Service。
|
||||
- [ ] Counseling Manager 可分配和查看必要摘要;普通管理员、红娘、内容编辑不得读取咨询正文。
|
||||
- [ ] 婚前辅导复用 counseling service type;邀请平台内伴侣必须由对方自主确认。
|
||||
- [ ] U55~U59 修复类型错误、API 契约、Loading/Empty/Error/Permission 状态和非医疗化文案。
|
||||
- [x] 修复 Admin Vue 语法错误并注册 `/counseling` 路由,先使 type-check 失败转为通过。
|
||||
- [x] 用户申请接口从认证主体取 userId;补幂等、取消、详情 owner 校验和合法状态迁移。
|
||||
- [x] 分配操作使用条件更新/版本号解决并发;Counselor 只能读取和操作 Assigned Service。
|
||||
- [x] Counseling Manager 可分配和查看必要摘要;普通管理员、红娘、内容编辑不得读取咨询正文。
|
||||
- [x] 婚前辅导复用 counseling service type;邀请平台内伴侣必须由对方自主确认。
|
||||
- [x] U55~U59 修复类型错误、API 契约、Loading/Empty/Error/Permission 状态和非医疗化文案。
|
||||
|
||||
**Gate:** 申请→分配→预约→服务→完成→反馈真实闭环通过;所有隐私负向用例通过。
|
||||
|
||||
> **复验完成(2026-09-17)** — Tag:`phase13a-recovery-2026-09-17`
|
||||
>
|
||||
> 复验发现 counseling 模块存在**双实现**:线上权威 `CounselingService`(167 行)
|
||||
> **零测试**,唯一被测的 `CounselingServiceApp`(280 行)是**孤儿类**
|
||||
> (无注入点、无端点可达,生产永不执行)。已合并并删除孤儿类。
|
||||
>
|
||||
> **9 处缺陷(D1~D9)**:
|
||||
> - D1 **Critical 生产故障 / INV-013 泄露**:咨询正文原文被写入 WARN 日志(同 Phase 10 D2 一族)。
|
||||
> - D2 High 越权:申请 userId 取自请求体而非认证主体。
|
||||
> - D3 High 并发:分配「先读后写」→ 并发**静默覆盖**;改 `assignIfPending` 条件更新。
|
||||
> - D4 High 越权读:manager 经 `getDetail` 可读 followups 正文(INV-013 绕过通道)。
|
||||
> - D5 Medium:无状态机 → 「已完成」可被再取消。
|
||||
> - D6 Medium 生产故障:`GET /services/intro` 后端不存在,小程序 intro 页 100% 无数据
|
||||
> (且被 `/services/{id}` 吞掉)→ 已补端点并置于变量路径之前。
|
||||
> - D7 Medium:Admin 调用 5 条不存在的运营端端点(404)+ 缺四态。
|
||||
> - D8 Medium:小程序字段名不匹配 + 无幂等 + 未检查 `success`(403 静默空列表)+ 跳转不存在的页。
|
||||
> - D9 Medium 测试保真度:测试库 counseling 两表**零 CHECK 约束**,`counseling_services` 零覆盖。
|
||||
>
|
||||
> **另修 4 处测试基建缺陷(T1~T4)**:守卫因 surefire `workingDirectory` 指向仓库根而
|
||||
> **找不到 DDL**(恒真假绿);全限定 `@TableField(exist=false)` 漏解析(假警报);
|
||||
> 权限守卫扫到私有辅助方法(假阳性);两处测试自身逻辑缺陷。
|
||||
>
|
||||
> **负向验证两轮**:① DDL 常量守卫 —— 从 V039 移除 `scheduled` → 守卫精准变红
|
||||
> (证明解析的是**真实 DDL** 而非副本);② 行为守卫 —— 注入并发/INV-013/状态机
|
||||
> 三个缺陷 → 三个用例精准变红。均已还原。
|
||||
>
|
||||
> **全量回归实测**:`[WARNING] Tests run: 420, Failures: 0, Errors: 0, Skipped: 3` /
|
||||
> `BUILD SUCCESS`(4 分 49 秒)。Phase 13 基线 391 → 420(净 +29)。
|
||||
> **Phase 13A 已关闭。Phase 14 未自动进入,等待用户确认。**
|
||||
> 报告:`docs/Phase-13A-V11-Recovery-Completion-Report-2026-09-17.md`
|
||||
|
||||
### Phase 14:多类型公益活动 — REFACTOR/EXTEND
|
||||
|
||||
**Files:** activity 模块、Miniapp activity 页面、Admin activity 页面。
|
||||
|
||||
Reference in New Issue
Block a user