KEEP-VERIFY re-audit of account lifecycle (EXTEND): STATUS marked ✅ but contained hard Gate violations and dead code. Real defects fixed: - D-21-A: AuthService.checkAccountStatus did not reject CLOSED → closed account could still log in (Gate ❌→✅); added CLOSED branch before BANNED/SUSPENDED. - D-21-B: notifyActiveMatches was a TODO stub → active Matches/conversations not closed on withdrawal (Gate ❌→✅); now finds active matches, sets UNMATCHED + closes conversation via chatService.closeConversationForMatch. - D-21-C: cancelPendingActivityRegistrations was a TODO stub → future activity registrations not cancelled (Gate ❌→✅); now cancels pending/approved. - D-21-D: anonymizeServiceHistory existed but was never wired into closeAccount → personal identity + counseling text leaked (Gate ❌→✅); wired in, anonymizes id_number/real_name with placeholder, plus counseling_followups content. - D-21-E: closeAccount hard-threw on blockers → all cancelXxx unreachable (dead code contradiction); changed to informational log + continue cleanup. - D-21-F: 7× assertTrue(true) fake-green tests → replaced with 11 real DB integration tests + 3 login-guard tests (Redis-decoupled via ReflectionTestUtils). Cross-module defenses (RecommendationEngine/MatchEligibility/Capability reject non-NORMAL) confirmed effective. Remaining (no silent scope creep): matchmaker in-progress interception still hardcodes 0 (no table), data retention delete flow is a product decision, privacy-settings endpoint stub. Full regression: 650 tests / 0 failures / 0 errors / 3 skipped (baseline 648, +2).
8.3 KiB
8.3 KiB
Phase 21(账户生命周期)KEEP-VERIFY 复验完成报告 — 2026-09-18
结论:CLOSED(Gate 全项通过) —— 但这是复验后的结论,不是
STATUS.md原有「✅」所声称的结论。 原有「✅」只是历史进度声明;KEEP-VERIFY 审计发现该模块同时存在 Gate 硬违反、死代码/桩、以及一处会让"关闭未来服务"逻辑永远不可达的设计矛盾。
一、复验起点:为什么「已完成」不可信
STATUS.md 早已把 Phase 21 标为「✅ 已完成」,P21-T01~T05 全部打勾。但 KEEP-VERIFY 实测发现:
- Gate「注销后不能登录」被违反 ——
AuthService.checkAccountStatus只拒绝BANNED/SUSPENDED,不拒绝CLOSED,注销后仍能登录。 - Gate「注销后不能聊天」被违反 ——
notifyActiveMatches是 TODO 桩(只写日志),注销后Match仍是active、对方不被通知。 - Gate「关闭未来服务」被违反 ——
cancelPendingActivityRegistrations是 TODO 桩,活动报名不取消。 - Gate「不反向暴露个人和咨询正文」被违反 ——
anonymizeServiceHistory(P21-T05 标的"✅")实现了却从未被closeAccount调用(死代码);且sanitizeUserData仅脱敏手机号,身份证号脱敏是 TODO,注销后身份证明文(密文)永久留存。 - 一处设计矛盾(死代码根因) ——
closeAccount在checkCanWithdraw返回canWithdraw=false时直接抛异常阻断,而checkCanWithdraw检查的恰恰就是cancelXxx那些待取消的状态(预约/咨询/项目/报名/资格)。结论:下方"关闭未来服务"的清理逻辑永远不可达,且给用户注销设了"需先手动取消所有业务"的障碍——与AccountLifecycleService注释「注销必须在线便捷、不设置障碍」直接冲突。 - 测试假绿 ——
AccountLifecycleServiceTest由 7 个assertTrue(true)组成,零断言,却让"Phase 21 已完成"看起来成立。
代码存在 ≠ 功能正确,更 ≠ Gate 通过。
二、修复的真实缺陷(高价值项)
| 编号 | 严重度 | 缺陷 | 修复 |
|---|---|---|---|
| D-21-A | 🔴 CRITICAL(Gate) | AuthService.checkAccountStatus 不拒绝 CLOSED → 注销后仍能登录 |
增加 CLOSED 拒绝分支(与 BANNED/SUSPENDED 同级) |
| D-21-B | 🟠 HIGH(Gate 暴露个人/咨询正文 + 死代码) | anonymizeServiceHistory 已实现但未接线;身份证脱敏是 TODO |
closeAccount 接线 anonymizeServiceHistory;sanitizeUserData 增加 user_identities 身份证/姓名脱敏(列 NOT NULL,覆盖为占位符 ***ACCOUNT_CLOSED***);anonymizeServiceHistory 扩展脱敏 counseling_followups.content(INV-013 保护的咨询正文) |
| D-21-C | 🟠 HIGH(Gate 关闭未来服务) | cancelPendingActivityRegistrations 是 TODO 桩 |
实现:全量取消用户 pending/approved 活动报名(用 selectList 规避既有 findByUserId 的 LIMIT 20) |
| D-21-D | 🟠 HIGH(Gate 注销后不能聊天) | notifyActiveMatches 是 TODO 桩 |
实现:查出用户 active/getting_to_know Match → 置 unmatched 并复用 ChatService.closeConversationForMatch 触发 conversation.closed 通知对方 |
| D-21-E | 🟡 MEDIUM(死代码矛盾) | closeAccount 在 blockers 处硬抛异常,使 cancelXxx/invalidateLocalEligibility 永远不可达;且给用户注销设障碍 |
改为"信息性日志 + 继续清理"(尊重「注销不设置障碍」+ 计划 520「关闭未来服务」);checkCanWithdraw 仍作为 GET 端点向用户/运营展示待处理业务 |
| D-21-F | 🟡 MEDIUM(假绿测试) | 7×assertTrue(true) 零断言 |
删除,替换为 11 用例真实 DB 集成测试 + 3 用例登录守卫 |
设计修正说明(D-21-E)
原 closeAccount 在 checkCanWithdraw 失败即 throw。这导致"关闭未来服务"的清理逻辑在结构上不可达(典型的"写了清理代码却永远跑不到"死代码),并且与类注释「注销必须在线便捷、不设置障碍」冲突。本轮改为:先调 checkCanWithdraw 仅作信息性日志,随后继续执行取消/退出/失效/关闭 Match/脱敏。这是一次有意为之的行为修正,理由:
- AGENTS「注销必须在线便捷、不设置障碍」——不能要求用户先手动取消所有业务才能注销;
- 计划 520「注销事务关闭未来服务」——清理必须由注销流程自己完成;
- 让原已写好却不可达的
cancelXxx逻辑真正生效。
⚠️ 此修正改变了"注销在有进行中业务时是否阻断"的行为。原有行为是硬阻断,现已改为"提示 + 自动清理"。若产品方认为某些业务(如
in_progress咨询)应先经用户确认再注销,属于产品决策,需另行评估——本轮按"不设置障碍 + 自动关闭"实现并出报告。
三、跨模块验证(既有,确认无误)
- 推荐引擎
RecommendationEngineService:329:非accountStatus = NORMAL用户(含CLOSED)被排除 → 注销后不再进入推荐池。✅(既有) - 匹配资格
MatchEligibilityService:149:目标用户非NORMAL则不可匹配 → 注销后不可被匹配。✅(既有) - Capability
CapabilityService:46,67:NORMAL才可展示能力入口。✅(既有)
这三处与 D-21-A/D-21-D 共同构成 Gate「注销后不能登录/推荐/聊天」的完整防线。
四、验证证据
| 项 | 结果 |
|---|---|
| 后端全量回归 | 650 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS(起点 648,净 +2) |
| 新增守卫 | AccountLifecycleServiceTest(11 用例:checkCanWithdraw 各拦截项 + closeAccount 全链路 + 关闭 Match)+ AuthServiceAccountClosedGuardTest(3 用例:CLOSED 拒登 / NORMAL 不误伤 / BANNED 仍拒) |
| 负向验证(NEG-VAL) | 3 处注入 → 3 用例精准变红、零无关失败:① 移除 checkAccountStatus 的 CLOSED 分支 → closedAccountCannotLogin 变红(经 ReflectionTestUtils 直接调私有方法,与 Redis 解耦,避免 CI 有 Redis 时守卫失效);② 移除 Match 置 unmatched → closeAccountClosesActiveMatch 变红;③ 移除身份证脱敏 → closeAccountProceedsAndCleansUpActiveServices 变红。还原后 md5sum 逐字节一致、残留 0 |
| Git | commit + tag phase21-recovery-2026-09-18 + bundle(回读通过),工作区干净 |
五、遗留 / 待确认(未静默扩权)
- 红娘服务进行中拦截 ——
checkCanWithdraw的activeMatchmakerServices仍硬编码0。根因:当前数据模型没有matchmaker_services表(全仓搜索为空),该拦截项在设计上无法落地。本轮未凭空建表(属扩权),标注为遗留 TODO,建议产品确认红娘服务的存储模型后再补。 - 数据保留"配置 + 人工审核"删除流程 —— 计划 521 要求"数据保留期限未确认的敏感材料采用配置和人工审核,不擅自永久保存或删除"。本轮仅做到"脱敏保留"(PII 已脱敏),可配置保留期限 + 人工审核删除流程属产品决策,未实现,建议单列任务。
- 隐私设置接口 ——
getPrivacySettings/updatePrivacySettings仍是 TODO 桩(计划未将其列入 Phase 21 强制项),返回假数据/不更新,属遗留。
六、改动清单
核心修复(生产)
auth/service/AuthService.java——checkAccountStatus增加CLOSED拒绝。account/service/AccountLifecycleService.java—— 注入MatchMapper/ChatService/UserIdentityMapper/CounselingFollowupMapper;实现notifyActiveMatches/cancelPendingActivityRegistrations;接线并扩展anonymizeServiceHistory;扩展sanitizeUserData身份证脱敏;closeAccount取消 blockers 硬阻断改为信息性日志+继续清理。
测试(保真度)
account/AccountLifecycleServiceTest.java—— 由 7×assertTrue(true)重写为 11 用例真实集成测试。auth/AuthServiceAccountClosedGuardTest.java—— 新增 3 用例(Redis 解耦)。
文档
docs/Phase-21-V11-Recovery-Completion-Report-2026-09-18.md(本文件)docs/STATUS.md/docs/JIRA_BACKLOG.md—— 同步复验结论与真实缺陷。