Commit Graph
24 Commits
Author SHA1 Message Date
WorkBuddy 7a2693b239 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
2026-09-17 01:25:12 +08:00
Codex ce172ffd4f feat(ui): activity 页迁 TS + 修复过期契约 + 新增 11 页静态预览器
## 背景
2026-09-13 UI 重设计方案的 13 个契约门禁中 3 个为红,逐一查明性质后分类处理。

## 1. activity 页迁移 TypeScript(11/11 页现已全 TS)
- activity.js → activity.ts(139 行),业务逻辑零改动,仅补类型
- 此前 STATUS.md 记「9/11 TS-only、3 页待迁」,但 messages/matchmaker
  已在 4f91be0 迁完,实际只剩 activity 一页,本提交后清零

## 2. miniapp-reference-contracts 契约自身过期(非代码问题)
- 契约引用 messages.js / matchmaker.js / activity.js,但页面已 TS 化,
  readFileSync 抛裸 ENOENT 栈,看不出「契约引用了陈旧文件名」这一真实原因
- 改为统一走 readPageLogic(page):断言 .ts 存在 + .js 不存在(锁单一运行源),
  找不到文件时给出可读断言而非崩溃
- 核心 11 页循环内统一加 TS-only 校验,防止 JS 回流

## 3. phase07-home-contracts 契约断言架构写反(非代码问题)
- 契约要求 HomeApplicationService 字面出现 userIdentityMapper.findByUserId,
  但实际是 capabilityService.isIdentityVerified(userId)(HomeApplicationService:116)
- 这符合 AGENTS.md #26「前端入口可见性由服务端 CapabilityService 返回决定」,
  让首页穿透 Capability 直接持有 mapper 反而是架构重复
- 且契约自身另一条已断言 CapabilityService 必须有 isIdentityVerified,
  两条要求互相矛盾
- 改为:查库断言落在 caps 侧;首页断言必须委托 capabilityService.isIdentityVerified;
  新增 doesNotMatch(home, /userIdentityMapper/) 禁止首页绕过 Capability
- 保留原有 doesNotMatch(home, /setVerified\(true\)/) 防硬编码断言

## 4. 新增 11 页静态预览器(本机无微信开发者工具)
- scripts/preview-miniapp.mjs:把 WXML + WXSS 转成可浏览器打开的静态 HTML
- 能力:wx:if/wx:elif/wx:else 链、wx:for 列表、嵌套三元、比较/取反运算、
  自定义组件按属性展开、slot 替换、rpx→px 换算、组件样式收集
- mock 数据严格对齐 types/index.d.ts 与各页 WXML 实际读取字段
  (如 regionName/heightCm/educationCode、status: registration_open),
  不用想象字段,避免误导验收判断
- 产出 artifacts/ui-redesign/miniapp/preview.html(11 页可切换,284K)
- 明确边界:非真机渲染、不执行页面 JS、不连后端,
  只用于视觉走查,不得作为功能验收证据

## 验证
- 契约门禁 12/13 通过(唯一红的 ui-document-baseline-contracts 涉及
  STATUS.md 验收口径,待用户看真机效果后裁决,本轮不动)
- 11 页渲染体量:116/70/47/56/94/68/39/39/41/37/38 个类名元素,
  零残留 {{}} 插值、零残留原始组件标签
- 小程序 tsc --noEmit 通过
2026-09-16 12:33:48 +08:00
Codex 687a156350 fix(chat): 会话/消息响应契约对齐小程序,修复聊天页字段断链
## 问题(三处真实断链)
后端 chat 模块字段名与小程序契约不一致,但自动化测试此前未覆盖,故一直未被发现:

1. 会话 DTO 用 otherUserId/otherDisplayName/otherAvatarUrl,
   而前端(types/index.d.ts + pages/chat/chat.ts + components/conversation-item)
   只读 targetUserId/targetDisplayName/targetAvatarUrl。
   → chat.ts 取不到对方 userId(无兜底,落到空串),
     导致"查看资料 / 拉黑 / 举报"点击后提示"暂无更多信息"并失效。
2. 消息响应只给 createdAt(ISO 字符串),前端读 Message.sentAt: number(epoch 毫秒)。
   → chat.ts 时间分隔 ts 恒为 0,消息时间分隔与时间文案不显示。
3. 会话对象缺 relationStatus,前端 conversation-item 依赖它做灰显与"已拉黑"提示。

## 裁决依据
按"以文件修改时间为准"的裁决统一到前端约定:
- 前端 types/index.d.ts 被 86 个页面引用,是声明式契约
- pages/chat/chat.ts(09-04)是最新的真实消费者
- targetUserId 已是项目内既有惯例(ActivityMatchItemDTO)
- admin-web 不消费 /conversations,无第二方受影响

## 变更
- ConversationDTO:字段改名 target*;新增 relationStatus;lastMessageAt 改 epoch 毫秒
- ChatService.toConversationDTO:同步映射;relationStatus 按 Block > unmatched > mutual_match > neutral 推导
- Constants:新增 RELATION_STATUS_NEUTRAL / MUTUAL_MATCH / UNMATCHED(不硬编码)
- MessageResponse:新增 sentAt(epoch 毫秒)
- miniapp/utils/datetime.js(新增):收敛时间格式化,
  补上模板依赖但全仓从未计算的 lastMessageAtText
- messageStore:会话进入 store 时统一装饰 lastMessageAtText
- chat.ts:_formatTime 改为复用共享工具;移除已失效的 otherDisplayName 兜底

## 验证
- 新增 ChatContractJsonTest(4 例)锁定字段名、sentAt 数值类型、
  并断言 other*/status 旧字段名不再出现
- 后端 294 tests, 0 failures, 0 errors(原 290 + 新增 4)
- 小程序 tsc --noEmit 通过

遗留(未在本轮处理):平台尚未实现头像 URL 解析(user_photos 只存 fileKey),
targetAvatarUrl 保持 null,前端 ui-avatar 回退昵称首字,不影响展示。
2026-09-15 22:45:46 +08:00
Codex 4f91be0cab feat(miniapp): final 3 pages migrated to TypeScript (matchmaker/messages/mine)
D1 debt cleared: all 11 miniapp core pages now TS-only.
tsc --noEmit -> 0 errors. JS sources deleted.
2026-09-14 14:59:28 +08:00
Codex 22325ec2b0 feat(discover): chip-targeted filter panel — scroll to + highlight target section
- Each filter chip (年龄/学历/婚姻) carries data-target pointing to its section
- openFilterPanel receives TouchEvent, maps target to section id, sets panelScrollTo + panelHighlight
- Panel uses scroll-view scroll-into-view with scroll-with-animation for smooth scroll
- Targeted section gets pink outline highlight via .hl class
- Non-targeted sections remain normally visible for context
2026-09-14 11:27:52 +08:00
Codex adfa4ff31e fix(discover): filter panel as bottom sheet with mask, add debug log
- Changed filter panel from in-flow to fixed bottom drawer with semi-transparent mask
- Mask click closes panel (closeFilterPanel)
- Added console.log in openFilterPanel for debugging event firing
- Panel now z-index:11 above mask z-index:10, guaranteeing visibility
2026-09-14 11:19:25 +08:00
Codex 3e5d2314aa fix(discover): restore discover-filter-chips class for contract 2026-09-14 11:09:53 +08:00
Codex 146ebab61d feat(discover): rewrite filter UI to align with backend DiscoverController 5 params
- Top chips: 推荐(重置) / 丹阳本地(regionCode=321181) / 年龄 / 学历 / 婚姻
- Each chip has real active state bound to applied filter value
- Panel expanded to 4 sections: age slider + region + education + maritalStatus
- Param names fixed: minAge/maxAge/regionCode/educationCode/maritalStatus
- Data flow: panel temp -> onApplyFilter -> applied -> _buildParams -> API
- Panel opens with current applied values, closes with openFilterPanel/closeFilterPanel
2026-09-14 11:09:23 +08:00
Codex 0f06d82a92 fix(discover): filter chips use applied state and apply immediately on top-chip tap
- Top chip '推荐' now active only when no active filter (was hardcoded)
- Top chip '丹阳本地' uses applied.region (was tempRegion)
- Top chip '年龄' uses applied age range
- New onTopRegionTap applies region filter immediately and refreshes results
- Panel onRegionTap preserved for filter-panel multi-select flow
2026-09-14 10:59:43 +08:00
Codex 751f14d5ee chore: remove obsolete .js sources for migrated C-group pages 2026-09-14 10:14:27 +08:00
Codex 93fc9b0d88 feat(miniapp): migrate C-group pages to TypeScript, update contracts 2026-09-14 10:14:05 +08:00
Codex 63cbbff2de feat: implement miniapp C-group pages with real API calls and design tokens 2026-09-14 10:02:22 +08:00
Codex 1aea6f7791 feat: redesign public relationship services 2026-09-14 08:51:26 +08:00
Codex a6037ded59 feat: redesign activity and profile tabs 2026-09-14 07:05:20 +08:00
Codex b7dc52b517 feat: redesign discovery and messages tabs 2026-09-14 06:49:49 +08:00
Codex 36b15d9ca8 feat: rebuild miniapp reference home 2026-09-14 06:45:36 +08:00
Codex df47543501 feat: add miniapp reference assets and primitives 2026-09-14 06:38:44 +08:00
Codex 311f251609 test: lock miniapp reference page scope 2026-09-14 06:19:11 +08:00
Codex 097c4c4778 chore: finalize miniapp page source cleanup 2026-09-13 21:25:50 +08:00
Codex eed4400611 refactor: remove duplicate miniapp page sources 2026-09-13 21:19:05 +08:00
admin ac2327b499 chore: checkpoint before switching to main 2026-09-11 09:07:41 +08:00
admin 76a10e5de0 fix: resolve miniapp compile errors 2026-09-10 18:37:53 +08:00
admin 45ca118453 feat: deliver V1.1 dual-client UI fidelity milestone 2026-09-10 16:06:11 +08:00
admin b46b48eb33 chore: establish V1.1 recovery baseline 2026-09-09 22:05:00 +08:00