Files
danyang-match/ui-tests/ui-document-baseline-contracts.mjs
T
Codex bc4531a734 feat(api): 补齐 /me/* 命名空间,恢复 17 个 404 调用 + 修 /me/status 过期桩
背景:小程序 services/*.js 按 REST 规范(§16 / §22-§40 / §80 / §83-§84)统一调用
/me/*,但后端从未注册 /me 基路径,个人资料、照片、择偶条件、隐私设置、关系状态、
我的举报/申诉等约 17 个调用全部 404。

后端
- 新增 me/controller/MeController:把既有 service 能力暴露到规范路径(纯委托,不复制
  业务规则,AGENTS.md #26)。旧路径(/profile/*、/photos、/relationship/*、
  /account/privacy-settings、/risk/reports/my、/appeals/my)全部保留。
- 响应形状按小程序 JSDoc 适配(小程序被 86 页引用,是权威契约):
  · /me/relationship-status 返回 status(旧 /relationship/status 返回 relationshipStatus,
    字段名不同,前端读的是 result.status)
  · /me/profile/review-status 返回 {status, items, submittedAt, reviewedAt},时间统一 epoch 毫秒
  · /me/profile/submit-review 返回 {reviewStatus, submittedAt}
- 小程序三条历史写法一并兼容:/me/privacy-settings、/me/photos/{id}/main、/me/appeals

修复
- AuthService.getMeStatus:verified / profileStatus / reviewStatus 三项写死为
  false / not_started / not_submitted,注释标注"Phase 03/04/05 填充",但那三个 Phase
  早已交付,导致 /me/status 长期返回错误的实名与审核状态。改为 capabilityService
  + userProfileMapper + reviewTaskMapper 的真实来源(与首页聚合同一判定)。
- PhotoController.deletePhoto:原用 ((Map<?,?>) p).get("id") 强转 UserPhoto 实体,
  必然抛 ClassCastException,删除照片恒定 500。改用实体主键比对。
- PhotoService.setMainAvatar(新增):数据模型以 photo_type='avatar' 标识头像(见
  UserPhotoMapper#findAvatar),无 is_main 字段,故"设主图"= 换类型并保证至多一张。
  注意:插入该方法时曾把 deletePhoto 原有的 @Transactional 挤到新方法上,导致
  "Transactional is not a repeatable annotation" 并引发 Lombok 全面停摆(满屏
  找不到 getter/setter),已修正注解归属。

验证
- 后端全量:299 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS(+2 为新增契约测试)
- 新增 MeRouteContractTest(2 例):同时断言 HTTP 方法,锁定 21 条 /me/* 与 13 条旧路径
- 小程序 tsc --noEmit 通过;UI 契约门禁 13/13

文档
- STATUS.md:/me/* 从"待裁决"改为"已完成";核心待清债务 1 项 → 0 项
- API_INVENTORY.md:顶部"待收敛"警告改为"已收敛",补全 /me/* 端点表
2026-09-16 12:54:39 +08:00

71 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const read = (path) => readFileSync(resolve(root, path), 'utf8')
const spec = 'docs/superpowers/specs/2026-09-13-reference-driven-dual-client-ui-redesign-design.md'
const obsolete = [
'docs/superpowers/specs/2026-09-10-ui-v11-fidelity-design.md',
'docs/superpowers/specs/2026-09-11-codex-baseline-cleanup-design.md',
'docs/superpowers/specs/2026-09-11-dual-client-full-ui-redesign-design.md',
'docs/superpowers/plans/2026-09-10-ui-v11-fidelity.md',
'docs/superpowers/plans/2026-09-11-codex-baseline-cleanup.md',
'docs/superpowers/plans/2026-09-11-admin-full-ui-redesign.md',
'docs/superpowers/plans/2026-09-11-miniapp-full-ui-redesign.md',
'docs/superpowers/plans/2026-09-11-ui-foundation-and-pilot.md',
'docs/UI-V11-Visual-Acceptance-2026-09-10.md',
'docs/善缘丹阳 V1.1 前端 UI-UX 高保真原型修订说明.md',
'design-qa.md',
'artifacts/phase07-miniapp-home.png',
'artifacts/phase07-miniapp-discover.png',
'artifacts/phase07-home-comparison.png',
'artifacts/phase07-discover-comparison.png',
'artifacts/ui-v11/admin-counselor-1366.png',
'artifacts/ui-v11/admin-dashboard-1440.png',
'artifacts/ui-v11/admin-station-1366.png',
'artifacts/ui-v11/miniapp-home-320.png',
'artifacts/ui-v11/miniapp-home-375.png',
'artifacts/ui-v11/miniapp-home-390.png',
'artifacts/ui-v11/miniapp-home-430.png',
'artifacts/ui-v11/miniapp-home-preview.html',
'artifacts/tabbar-icons-preview.png',
'ui-tests/admin-redesign-contracts.mjs',
'ui-tests/miniapp-redesign-contracts.mjs',
]
const agents = read('AGENTS.md')
assert.ok(agents.includes(spec), 'AGENTS.md 未指向唯一视觉规格')
assert.doesNotMatch(agents, /善缘丹阳 V1\.1 前端 UI-UX 高保真原型修订说明\.md/, 'AGENTS.md 仍引用旧高保真规范')
for (const file of obsolete) {
assert.equal(existsSync(resolve(root, file)), false, `旧视觉入口仍存在: ${file}`)
}
const status = read('docs/STATUS.md')
const backlog = read('docs/JIRA_BACKLOG.md')
const phase07 = read('docs/Phase-07-V11-Recovery-Completion-Report-2026-09-13.md')
const matrix = read('docs/UI_REDESIGN_PAGE_MATRIX_V1.1.md')
// —— 文档基线口径 ——
// UI 重设计首批 11 页已于 2026-09-16 通过用户验收(结论:基本达标)。
// 四份文档必须一致记录该结论。若有人把口径改回"视觉否决 / 待重新实现 / REOPENED",
// 本门禁必须变红 —— 这是防止验收状态被静默回退的哨兵。
assert.match(status, /UI REDESIGN ACCEPTED BY USER/, 'STATUS 头部未记录 UI 已通过用户验收')
assert.match(status, /Phase 07[^\n]*已通过用户验收/, 'STATUS 未标记 Phase 07 UI 验收通过')
// JIRA 表格行必须已关闭;主表中的 REOPENED 不得复活(历史校正段落中的引用不算)。
assert.match(backlog, /^\|\s*Phase 07[^\n]*已通过用户验收/m, 'JIRA 未关闭 Phase 07 UI 条目')
assert.doesNotMatch(backlog, /^\|\s*Phase 07[^\n]*REOPENED/m, 'JIRA 主表仍将 Phase 07 UI 标记为 REOPENED')
// Phase 07 报告是历史文档:既要保留 2026-09-13 的失效判定,也要记录重新实现后的验收结论。
assert.match(phase07, /视觉结论已失效/, 'Phase 07 报告未保留 2026-09-13 历史判定')
assert.match(phase07, /首批 11 页于 2026-09-16 通过用户验收/, 'Phase 07 报告未记录重新实现后的验收结论')
assert.match(matrix, /小程序首批 11 个核心页面/, '页面矩阵未切换到当前首批范围')
assert.match(matrix, /2026-09-16 验收通过/, '页面矩阵未记录 11 页验收结论')
assert.doesNotMatch(matrix, /待重新实现|待开始/, '页面矩阵仍残留未完成状态')
assert.doesNotMatch(matrix, /63 个页面.*(全部|全量).*重构/, '页面矩阵仍要求本批全量重构')
console.log('UI document baseline contracts passed')