chore(docs): Wave 0 调研产出 + P0/P1/P2 反馈调研

建立项目级标杆文档 docs/_overview/ 作为产品全景索引,
解决"PRD 零碎、文档膨胀、跨子系统调研无入口"的问题。

主要内容:
- 00-index 总索引 + 维护协议 + 与 CLAUDE.md 关系
- 01-product-overview 产品全景脑图(6 角色 / 6 子系统 / 数据流 /
  7 业务概念 / 8+1 AI 矩阵 / 22 术语)
- 02a-miniprogram-page-matrix 小程序 21 页业务指纹
- 02b-adminweb-page-matrix admin-web 19 路由业务指纹
- 03-test-spec 测试规范 (L1-L5 分层 + 走查模板 + 75-95 case 估算)
- 04-doc-conflicts 39 条冲突索引(P0×8 / P1×13 / P2×13 + 5 子项)
- 04a/b/c-conflicts-*-detail 业务故事卡(7 字段:关联/逻辑/影响/选项/判定)
- 05-orphan-pages-cleanup admin-web 6 孤儿页面处置(1 归档 + 4 保留)
- WAVES-MASTER-PLAN.md 全 Wave 主计划(0-5,共 22-32 工作日)
- WAVE-1-KICKOFF.md Wave 1 实施 kickoff
- GLOBAL-DECISION-DASHBOARD.md 全局决策仪表板

反馈调研产物:
- 04a-feedback/ P0 两轮反馈(8+8 项决策 + D-1/2/3 + F-1/2 子代理产出)
- 04b-feedback/ P1 两轮反馈(13+1+5 项 + E-1/2/3/4 + G-1/2 子代理产出)
- 04c-feedback/ P2 反馈(13 项 + 5 子项 + H-1/2/3 子代理产出)
- NEO-DECISIONS-LOG 累积决策记录

关键追加发现 8 处 D Bug(原蓝本 0):
- P0-3 看板沙箱接入(Wave 1 W1-T1)
- P0-5 致命 1 (4 处 fdw_etl 残留, 已修 commit 17f045a)
- P0-5 致命 2 (JWT aud 缺失, 已修 commit 17f045a)
- P0-6 clearAllTasks 守卫 (Wave 3)
- P0-8 DBViewer 黑名单漏 (已修 commit 17f045a)
- P1-3 task-detail 跳转传 task_id 而非 customer_id
- P2-7 board-finance 隐式 null
- 2 个独立 Bug (page_context.created_at + ClueCategory 字典)

参考: docs/_overview/00-index.md
This commit is contained in:
Neo
2026-05-04 07:38:28 +08:00
parent c6453829a6
commit 509cf43284
44 changed files with 10789 additions and 0 deletions

View File

@@ -0,0 +1,497 @@
# 小程序 21 页业务指纹矩阵
> 生成日期:2026-05-04
> 用途:每页设计目的 + 测试时的"应当展示"判据
> 数据源:`docs/miniprogram-dev/api-audit/*.md`(19 份) + 3 份页面 ts 源码 + `docs/miniprogram-dev/design-system/*` + `docs/prd/Neo_Specs/storyboard-*.md`
> 注:本矩阵仅做指纹索引,不重复 api-audit 的"硬编码与 Mock 处置方案"
---
## 一、tabBar 与角色映射
小程序 `app.json` 的 tabBar 是固定 3 项("任务 / 看板 / 我的"),但实际渲染走自定义 `custom-tab-bar`,按角色 `visibleTabs` 动态过滤,首登选用第一个可见 tab。来源:`docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md` GAP-01、`apps/miniprogram/miniprogram/utils/auth-guard.ts``pages/dev-tools/dev-tools.ts` 中的 ROLE_LIST。
| 角色 (`role_code`) | 中文 | 可见 tab | 默认落地页 | 主要使用范围 |
| --- | --- | --- | --- | --- |
| `coach` | 助教 | 任务、看板(部分)、我的 | `/pages/task-list/task-list` | 教练自己的任务 + 业绩 + 备注/对话 |
| `head_coach` | 教练 | 任务、看板、我的 | `/pages/task-list/task-list` | 上述加上跨助教看板查看 |
| `staff` | 员工 | 看板、我的 | `/pages/board-finance/board-finance` | 顾问视角,无任务 |
| `manager` | 管理人员 | 看板、我的(+dev-tools) | `/pages/board-finance/board-finance` | 全门店看板 + 调试入口 |
| `site_admin` / `tenant_admin` | 门店管理员 | 看板、我的 | `/pages/board-finance/board-finance` | 主要在 `tenant-admin` 后台,小程序仅查阅 |
> 跨店切换、`isCurrentMonth`、当前业务时钟由 `apps/miniprogram/miniprogram/utils/runtime-clock.ts → getBusinessClock()` 提供;非当月走真实时间,sandbox 模式按 `business_year/month` 显示(详见 `pages/customer-records/customer-records.ts` L8-L14)。
---
## 二、页面分组与流转图
```text
[认证流]
login ──(success)──> task-list / reviewing / apply / no-permission
[任务流] (角色: coach / head_coach)
task-list ─tap卡片─> task-detail ─问问助手─> chat
└─tap绩效卡─> performance ──查看全部──> performance-records
[看板流] (角色: head_coach / staff / manager)
board-finance <─tab─> board-customer <─tab─> board-coach
│ │ │
│ └─tap客户卡─> customer-detail
│ │
│ ├─查看消费记录─> customer-records
│ ├─查看服务记录─> customer-service-records
│ └─问问助手──> chat
└─tap助教卡─> coach-detail ─近期服务明细─> coach-service-records
└─问问助手──> chat
[我的/AI 流]
my-profile ─备注记录─> notes
└─对话记录─> chat-history ──tap对话──> chat
[调试]
dev-tools (manager 专属;reLaunch 跳到任意已注册页面,用于切角色 / 切状态)
```
页面共 21 个,分四组:认证 4(login/apply/reviewing/no-permission) + 任务 4(task-list/task-detail/performance/performance-records) + 看板与详情 8(board-finance/board-customer/board-coach/customer-detail/customer-records/customer-service-records/coach-detail/coach-service-records) + 杂项 5(my-profile/notes/chat/chat-history/dev-tools)。
---
## 三、每页指纹卡(21 页 × 9 字段)
### 3.1 pages/login/login
- **入口**:小程序首页(冷启动落地);`reviewing` 页"切换账号"按钮也会回到这里
- **设计目的**:把微信用户换成后端 JWT,并按 `user_status` 路由到下一站
- **适用角色**:全部(未登录态)
- **关键 API**:`POST /api/xcx/dev-login`(127.0.0.1) / `POST /api/xcx/login`(线上,需先 `wx.login()` 拿 code) / `POST /api/xcx/refresh`(401 自动)
- **必现字段**:自定义状态栏占位、应用图标 + "球房运营助手" 标题 + 副标题 "为台球厅提升运营效率的内部管理工具"、3 个功能标签卡片(任务管理/数据看板/智能助手)、协议勾选、"使用微信登录" 按钮(disabled 态需可见但灰)、底部"仅限球房内部员工使用"
- **数据展示标准**:无金额/日期展示;按钮在 `agreed && !loading` 时为 active 蓝
- **空态文案**:无空态(此页是入口,任何情况都展示登录卡)
- **典型异常路径**:403→`账号已被禁用`、401→`登录凭证无效,请重试`、其他→`登录失败,请稍后重试`(统一 toast)
- **测试时校核重点**:`isDevMode` 判断(127.0.0.1 才走 dev-login)、登录成功后按 `user_status` 跳页(approved→task-list,pending→reviewing,new→apply,rejected/disabled→no-permission)。参考 `docs/miniprogram-dev/api-audit/login.md` L84-L94
---
### 3.2 pages/apply/apply
- **入口**:`login → status='new'` 自动 reLaunch;`onShow` 调用 `/me` 重新校验
- **设计目的**:首登未审核员工提交入驻表单(球房 ID + 申请身份 + 手机 + 编号 + 昵称),交付给管理员审核
- **适用角色**:仅 `status='new'` 的用户
- **关键 API**:`GET /api/xcx/me`(进页校验) / `POST /api/xcx/apply`(提交)
- **必现字段**:返回箭头(可选)、自定义导航栏 "申请访问权限"、欢迎语 "欢迎加入球房运营助手"、4 步进度条(提交申请→等待审核→审核通过→开始使用)、5 个表单项(球房 ID/申请身份/手机号/编号(选填)/昵称)、提示 "请认真填写,信息不完整可能导致审核不通过"、"提交申请" 按钮、底部 "审核通常需要 1-3 个工作日"
- **数据展示标准**:球房 ID `maxlength=5`、手机号正则 `^\d{11}$`、空字段不传 `employee_number`(JSON 序列化忽略)
- **空态文案**:无空态(只有表单)
- **典型异常路径**:409→`您已有待审核的申请`、422→`表单信息有误,请检查`、其他→`提交失败,请稍后重试`;成功 toast 后 800ms reLaunch 到 `reviewing`
- **测试时校核重点**:确认表单 5 项必填校验生效;`POST /api/xcx/apply` 字段名要为 snake_case(`site_code/applied_role_text/phone/employee_number/nickname`),与后端 schema 对齐。参考 `docs/miniprogram-dev/api-audit/apply.md` L73-L82
---
### 3.3 pages/reviewing/reviewing
- **入口**:`apply` 提交成功 / `login → status='pending'` 自动 reLaunch;`onShow` + `onPullDownRefresh` 都会重新拉 `/me`
- **设计目的**:告知员工"申请已收到,正在审核",同时持续轮询状态(状态变更后立刻 reLaunch 到目标页)
- **适用角色**:`status='pending'``status='rejected'`(显示拒绝原因)
- **关键 API**:`GET /api/xcx/me`(获取 status + latest_application)
- **必现字段**:自定义导航栏、状态图标(沙漏/失败)、`main-title`(`申请审核中` / `申请未通过`)、`sub-title`(说明文案)、审核进度卡(已提交/审核中/通过 三步骤)、"通常需要 1-3 个工作日" 提示、申请信息卡(球房 ID/申请身份/手机号)、底部 "更换登录账号" 按钮
- **数据展示标准**:`latest_application` 缺失则申请信息卡隐藏;状态切换是 reLaunch 而非 redirectTo
- **空态文案**:`latest_application=null` 时申请信息卡不渲染(不展示空文案)
- **典型异常路径**:`/me` 失败 toast `获取状态失败`;切换账号清 4 个 storage key 后回 login
- **测试时校核重点**:5 种 status 路由是否正确;rejected 时 `reject_reason` 是否展示在拒绝原因卡。参考 `docs/miniprogram-dev/api-audit/reviewing.md` L46-L66
---
### 3.4 pages/no-permission/no-permission
- **入口**:`login`/`apply`/`reviewing` 检测到 `status='rejected' or 'disabled'` 时 reLaunch
- **设计目的**:终态页,告诉员工无访问权限并指明联系人
- **适用角色**:`status` 为 rejected/disabled 的用户
- **关键 API**:`GET /api/xcx/me`(`onShow` 复检,状态恢复时跳走)
- **必现字段**:错误图标、`无访问权限` 标题、说明副标题、3 条原因解释卡片、"如有疑问请联系管理员 厉超"(管理员姓名当前硬编码)、"更换登录账号" 按钮
- **数据展示标准**:无业务数据
- **空态文案**:无空态
- **典型异常路径**:`/me` 失败保持当前 UI 不动
- **测试时校核重点**:管理员姓名硬编码 `厉超`(已记录,需要后端下发后改造);状态从 disabled→approved 时是否立刻 reLaunch。参考 `docs/miniprogram-dev/api-audit/no-permission.md` L37-L51
---
### 3.5 pages/task-list/task-list
- **入口**:tabBar 第 1 项;`login → status='approved'` 默认落地;`reviewing → status='approved'` reLaunch
- **设计目的**:助教/教练每日任务工作台,看到三组任务(置顶/正常/已放弃) + 当月业绩进度卡
- **适用角色**:`coach` / `head_coach`
- **关键 API**:`GET /api/xcx/tasks`(列表+performance) / `POST /tasks/{id}/pin|unpin|abandon|restore`(操作) / `POST /notes`(添加备注)
- **必现字段**:用户 banner(头像 + 昵称"小燕" + 角色"助教" + 门店"广州朗朗桌球")、当月业绩进度卡 perf-progress-bar(档位 0/100/130/160/190/220 刻度 + 当前定档课时数字 + 距升档差距 + 闪光动画 SHINE_SPEED)、"今日 客户维护" section header、3 个分组(📌 置顶 / 正常任务 / 已放弃)、每张任务卡(客户头像 + 客户名 + heart-icon 关系分 + 任务类型徽章 + deadline 标签 + "最近到店:N天前 · 余额:¥X" 行 + AI inline icon + AI 建议截断文案 + hasNote 角标)、放弃任务多了"放弃原因:..."行、空态/加载失败/已加载全部三态、AI 悬浮按钮、自定义 tabBar
- **数据展示标准**:金额走 `formatMoney``¥12,680`(整数千分位);课时走 `formatHours``2h` / `2.5h`;deadline 走 `formatDeadline` → 今天到期(橙)/还剩 N 天(蓝)/MM-DD(灰)/逾期 N 天(红)(参考 DISPLAY-STANDARDS.md §7);进度条 `filledPct = total/220 * 100` clamp 0-100;分页大小由 `hasMore` 控制
- **空态文案**:`暂无待办任务` / 加载失败 `加载失败,请重试` / 已加载全部 `没有更多了`
- **典型异常路径**:零 API 全 mock(联调前需注意);分页未实现仅显示"没有更多了";置顶/放弃刷新即丢失;userName/userRole/storeName 当前硬编码,多用户场景必崩
- **测试时校核重点**:`buildPerfData()` 期望的 15+ 字段(tierNodes/basicHours/bonusHours/currentTier/nextTierHours/tierCompleted/bonusMoney/incomeTrend/incomeTrendDir/prevMonth/incomeMonth 等)是否齐全 — 这是契约 TASK-1 最大 Gap,见 GAP-02;`enrichTask` 期望的 `lastVisitDays/balance/aiSuggestion` 是否后端返回(GAP-03);pin/unpin API 端点契约缺失(GAP-04);createNote 缺 score 参数(GAP-05)。参考 `docs/miniprogram-dev/api-audit/task-list.md` L80-L99 + `docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md` L75-L97
---
### 3.6 pages/task-detail/task-detail
- **入口**:`task-list` 卡片 tap;`performance`/`coach-detail` 中点服务记录(传 customerName,有 GAP-17)
- **设计目的**:单任务工单页 — 客户档案 + AI 建议 + 维客线索 + 话术参考 + 60 天服务记录 + 备注
- **适用角色**:`coach` / `head_coach`
- **关键 API**:`GET /api/xcx/tasks/{id}` / `POST /tasks/{id}/abandon|restore` / `POST /notes` / `DELETE /notes/{noteId}`
- **必现字段**:Banner(任务类型 SVG 背景 + 客户头像 + 客户名 + 任务类型标签 + 关系等级心形/文字/分数)、手机号(默认 `138****5678` 脱敏 + "查看/复制" 按钮)、储值等级卡(非常多/多/一般/少)、"💡 建议执行" + AI 建议文案、"💬 话术参考" 5 条(可复制,2 秒后回退)、"维客线索" 8 条(分类标签 + 来源 By:xxx)、"60天内服务记录" + 服务汇总(总课时/总收入/平均)、"备注记录"(按时间倒序 + 满意度 score)、底部"问问助手" + "备注" 按钮
- **数据展示标准**:金额 `¥160`(整数);课时 `2.0h`;关系等级走 `vi-colors.ts → getRelationshipLevel()`(excellent>8.5/good 6-8.5/normal 3.5-6/poor<3.5);备注时间走 `formatRelativeTime`
- **空态文案**:加载中 `加载中...` / 未找到 `未找到任务信息` / 加载失败 `加载失败` / 备注空 `暂无备注`
- **典型异常路径**:`detail.id` 实际是 taskId,跳 chat 时却被当 customerId 用(GAP-09);跳 customer-service-records 同样 bug(GAP-10);需要后端在 TASK-2 响应增加 `customer_id`
- **测试时校核重点**:维客线索的 `tag` 格式(含换行符)、`source` 格式(`By:小燕` vs `manual/ai_consumption/ai_note` 类型定义)是否一致(GAP-06/07);AI 分析的 `cache_type` 来源未明确(GAP-08)。参考 `docs/miniprogram-dev/api-audit/task-detail.md` L67-L130
---
### 3.7 pages/my-profile/my-profile
- **入口**:tabBar 第 3 项
- **设计目的**:用户个人中心 — 看用户信息、跳转备注/对话记录,以及退出登录
- **适用角色**:全部已登录角色
- **关键 API**:目前 0 个(从 `app.globalData.authUser` 读取);需对接 `GET /api/xcx/me` 刷新
- **必现字段**:用户卡片(头像 + 姓名 + 角色标签 + 门店名)、菜单 3 项("备注记录" → notes、"助手对话记录" → chat-history、"退出账号" → 弹窗确认 → login)、AI 悬浮按钮、自定义 tabBar
- **数据展示标准**:头像缺省走 `/assets/images/avatar-coach.png`;角色文案走中文(助教/教练/...)
- **空态文案**:无列表态;`authUser` 缺失时姓名/角色/门店字段会留空
- **典型异常路径**:globalData 字段缺失(authUser 当前未存 storeName/coachLevel/avatar,GAP-01)
- **测试时校核重点**:onShow 是否每次刷新 `authUser`、确认退出弹窗 `confirmColor=#e34d59`(走 `CONFIRM_DANGER_COLOR` 常量)。参考 `docs/miniprogram-dev/api-audit/my-profile.md` L38-L48
---
### 3.8 pages/notes/notes
- **入口**:`my-profile` 菜单 "备注记录"
- **设计目的**:展示当前用户记录过的全部备注(跨客户/跨任务),按时间倒序
- **适用角色**:全部
- **关键 API**:`GET /api/xcx/notes`(待对接) / `DELETE /api/xcx/notes/{noteId}`(待对接)
- **必现字段**:加载中 toast、备注卡片列表(每条:标签 + 客户名/任务名 + 内容 + 相对时间 + 删除图标)、底部 "— 已加载全部记录 —"、AI 悬浮按钮
- **数据展示标准**:时间走 `formatRelativeTime`(刚刚/N分钟前/N小时前/N天前/MM-DD/YYYY-MM-DD);确认删除弹窗 `confirmColor=#e34d59`
- **空态文案**:`暂无备注记录`(t-empty)
- **典型异常路径**:加载失败 `加载失败,请重试` + `重新加载`;触底加载未实现(GAP-36)
- **测试时校核重点**:`tagType` 枚举仅 customer/coach/system 三类(GAP-37);路由参数是否需要按 customerId/taskId 过滤(待 Neo 确认)。参考 `docs/miniprogram-dev/api-audit/notes.md` L23-L32
---
### 3.9 pages/performance/performance
- **入口**:`task-list` 业绩进度卡 tap;tabBar 不直接进
- **设计目的**:助教个人当月业绩总览 — 收入构成 + 档位升级激励 + 服务记录明细 + 新客/常客
- **适用角色**:`coach` / `head_coach`
- **关键 API**:`GET /api/xcx/performance?year=&month=` (待对接,需扩展契约)
- **必现字段**:Banner(头像 + 助教名 + 角色 + 门店 + 本月预估收入 + 上月收入)、"收入情况" 区(4 项 incomeItems:基础课/激励课/充值激励/TOP3 销冠奖,每项 icon+label+desc+value)、当前档位卡(基础课到手/激励课到手 单价)、下一阶段卡 + 升级提示("距离下一阶段 需完成 XXh,到达即得 ¥XXX")、本月合计预估行、"📋 我的服务记录明细" + 默认显示前 2 个日期组 + "展开更多/收起"、"查看全部" 跳 performance-records、"我的新客"(8 条 + 展开)、"我的常客"(8 条 + 展开)、AI 悬浮按钮
- **数据展示标准**:金额 `¥6,206`(千分位整数);课时 `87.5h` / `0h`;新客 `count + '次'` 后缀;常客 `hours` 数值无后缀(WXML `h` 字面量)
- **空态文案**:加载 `加载中...`、空 `暂无业绩数据`、错误 `加载失败,请点击重试`
- **典型异常路径**:`thisMonthRecords` 当前是按日分组的 DateGroup,但契约 PERF-1 是扁平数组(GAP-12);收入档位 currentTier/nextTier/upgradeHoursNeeded/upgradeBonus 全未在契约定义(GAP-13)
- **测试时校核重点**:`incomeItems``desc` 字段(如 `80元/h × 75h`)需后端拆分(GAP-15);跳 task-detail 传 `customerName` 而非 `id`(GAP-17)。参考 `docs/miniprogram-dev/api-audit/performance.md` L82-L99
---
### 3.10 pages/performance-records/performance-records
- **入口**:`performance` 页"查看全部";`task-list` 业绩进度卡也可
- **设计目的**:助教自己的业绩明细 — 月份切换 + 月度统计概览 + 按日期分组的服务记录
- **适用角色**:`coach` / `head_coach`(看自己)
- **关键 API**:`GET /api/xcx/performance/records?year=&month=&page=&pageSize=` (待对接)
- **必现字段**:Banner(coachName/coachLevel/storeName + 珊瑚极光渐变背景)、月份切换器(`<` `2026年2月` `>`,canPrev/canNext 边界)、统计概览(总记录 N笔 / 总业绩时长 Nh + 折前 Nh / 收入 ¥N)、按日期分组的服务卡片(date + totalHoursLabel + totalIncomeLabel + records 列表;每条 record 含 customerName/avatarChar/timeRange/hours/courseType/courseTypeClass/location/income)、底部 `— 已加载全部记录 —`
- **数据展示标准**:总笔数 `formatCount(n,'笔')``32笔`;课时 `formatHours``59h`;金额 `formatMoney``¥4,720`;`hoursRaw` 折前课时仅在 raw≠hours 且 raw>0 时展示;`courseTypeClass``tag-basic/tag-vip/tag-tip` 前缀
- **空态文案**:`暂无数据`、加载失败 `加载失败,请点击重试``重试`
- **典型异常路径**:Banner 字段当前从 `globalData.authUser` 读,但 `coachLevel/storeName` 还没存(GAP-22);月份切换未重置 page=1(GAP-21)
- **测试时校核重点**:折前/折后课时的差额展示规则(只在不同时显示括号);分页参数 `coachId` 是否传(自己 / 管理者两种视角)。参考 `docs/miniprogram-dev/api-audit/performance-records.md` L31-L99 + `docs/miniprogram-dev/design-system/DISPLAY-STANDARDS.md` §2.3
---
### 3.11 pages/board-finance/board-finance
- **入口**:tabBar 第 2 项(看板组首页);staff/manager 默认落地页
- **设计目的**:门店财务全景 — 经营一览 + 6 大板块(预收资产/应计收入/现金流入/现金流出/助教分析) + AI 洞察
- **适用角色**:`head_coach` / `staff` / `manager` / `site_admin` / `tenant_admin`
- **关键 API**:0 个,全量内联 mock(待设计:整体接口或 6 板块拆分)
- **必现字段**:角色 Banner(顶部头部带筛选)、5 区域 segmented(全部/大厅/A/B/C/麻将房/团建房 共 7 项)、时间选择器(本月/上月/...8 项)、环比开关、目录吸顶导航(emoji + 板块名)、6 大板块 sections:
- § 经营一览(8 主指标:发生额/总优惠/优惠占比/确认收入/实收/现金支出/现金结余/结余率,每项含环比)
- § 预收资产(储值实收 + 首充/续费/消耗 + 储值卡余额 + 全类别会员卡余额 + 赠送卡明细 3 行)
- § 应计收入确认(收入结构 9 行 + 项目正价 4 项 + 优惠扣减 4 项 + 收款渠道 3 项)
- § 现金流入(消费收入 3 项 + 充值收入 1 项 + 合计)
- § 现金流出(进货 3 + 固定 4 + 助教薪资 4 + 平台服务费 3)
- § 助教分析(基础课 + 激励课,各 4 等级明细)
- "💡 AI 洞察" 12 项指标卡(优惠率Top/差异最大/建议关注 等)
- **数据展示标准**:金额 `¥823,456`、负数 `-¥113,336`、百分比 `12.5%`、环比方向(↑/↓ + 颜色);助教费用必须区分陪打(`assistant_pd_money`)/超休(`assistant_cx_money`)而非 `service_fee`(踩坑参考 DWD-DOC)
- **空态文案**:`pageState='normal'` 默认直入(联调后需改 `loading`/`empty`/`error`)
- **典型异常路径**:零 API;激励课表格只渲染合计行,缺 `wx:for` 明细(已记录);区域筛选 `areaOptions` 应来自 API 而非硬编码
- **测试时校核重点**:走查时必看的 5 区域 segmented + 台桌占用率卡片 + 近 7 日营收折线 + AI 洞察 12 项指标;`balance_pay = recharge_card_pay + gift_card_pay`(支付渠道恒等式);`discount_manual``discount_other` 互斥;`consume_money` 禁止直接计算 → 用 `items_sum`(参考 ETL DWD-DOC)。参考 `docs/miniprogram-dev/api-audit/board-finance.md` L22-L240
---
### 3.12 pages/board-customer/board-customer
- **入口**:`board-finance` 顶部 Tab 切换;`board-coach` 顶部 Tab 切换
- **设计目的**:客户列表(前 100 名) — 8 维度切换(召回/潜力/余额/充值/最近/60天消费/60天频率/60天专一)展示不同的卡片字段
- **适用角色**:`head_coach` / `staff` / `manager`
- **关键 API**:`GET /api/v1/board/customers?dimension=&project=&limit=100&site_id=`(待开发)
- **必现字段**:Tab 栏(财务/客户/助教,选中"客户")、维度下拉(8 项)、项目下拉(全部/中式追分/斯诺克/麻将棋牌/团建K歌)、列表标题 `客户列表 · 前100名 · 共{{totalCount}}名客户`、客户卡片(每条按维度变换):
- 召回维度:理想间隔/已过/超期(天数 + danger 标签 if overdueDays>7)
- 潜力维度:30天消费/月均到店/余额
- 频率维度:8 周到店柱状图
- 专一维度:Top 助教 + 服务次数表
- 通用底部:主助教 + 服务占比 + 关系指数(>=8 显蓝)
- **数据展示标准**:金额走 `formatMoney`;余额预格式化 `¥2,680`(联调后改前端格式化);`avgInterval=5.0天`;近 30 天到店 `visits30d` 数值
- **空态文案**:加载中 `加载中...`、空 `暂无客户数据`、错误 `加载失败` + `点击重试`
- **典型异常路径**:零 API 全 mock;维度切换仅切 dimType 不重新请求;`initPageAiColor` 已 import 未调用
- **测试时校核重点**:`balance_pay` = `recharge_card_pay + gift_card_pay`;DQ-6 会员姓名必须 JOIN `dim_member.nickname`(`settlement_head.member_phone` 自 2025-12 起 NULL);消费金额用 `items_sum` 而非 `consume_money`。参考 `docs/miniprogram-dev/api-audit/board-customer.md` L24-L93 + L286-L296
---
### 3.13 pages/board-coach/board-coach
- **入口**:`board-finance` / `board-customer` 顶部 Tab 切换
- **设计目的**:助教看板(6 人样例) — 4 维度切换(perf/salary/sv/task)
- **适用角色**:`head_coach` / `staff` / `manager`
- **关键 API**:`GET /api/v1/board/coach/list?sort=&skill=&time=`(待开发)
- **必现字段**:Tab 栏(选中"助教")、排序下拉(perf_desc/perf_asc/salary_desc/...6 项)、技能下拉(全部/🎱中式/斯诺克/🀄麻将/🎤K歌)、时间下拉(month/quarter/last_month/last_3m/last_quarter/last_6m)、助教卡片(头像首字 + 渐变色 + 等级 tag + 技能 tags + Top 客户 3 个 + 4 维度专属字段):
- perf 维度:定档课时 + 折前 + 距升档 hint / ✅已达标
- salary 维度:工资 ¥X + 定档/折前
- sv 维度:储值 ¥X + 客户 N人 / 消耗 ¥X
- task 维度:召回 N次 + 回访 N次
- **数据展示标准**:课时 `formatHours``86.2h`;金额 `formatMoney``¥12,680`;客户/任务计数 `formatCount(n,'人')` / `formatCount(n,'次')`;等级英文 key (`star/senior/middle/junior`,展示文案见 vi-colors)
- **空态文案**:`暂无助教数据``加载失败` + `点击重试`
- **典型异常路径**:零 API;筛选切换不重新请求(F1 已记录);`time=last_6m + sort=sv_desc` 注释标注不兼容
- **测试时校核重点**:契约 BOARD-1 仅 10 字段 vs 前端需 20 字段(GAP-3);`skills` 类型 string[] vs `Array<{text,cls}>`(GAP-3);`topCustomers` 字段缺失(GAP-3)。参考 `docs/miniprogram-dev/api-audit/board-coach.md` L26-L51 + `docs/prd/Neo_Specs/miniprogram-storyboard-walkthrough-gaps.md` L60-L78
---
### 3.14 pages/customer-detail/customer-detail
- **入口**:`board-customer` 客户卡 tap;`task-detail` 跳转(无参 bug GAP-31)
- **设计目的**:单客户全景 — Banner + AI 洞察 + 维客线索 + 助教任务分配 + 最喜欢的助教 + 消费记录 + 备注
- **适用角色**:`head_coach` / `staff` / `manager`(读);`coach` 不进
- **关键 API**:`GET /api/xcx/customer/{member_id}/profile`(待开发) + 消费记录/线索/备注/AI 缓存等 6 个子接口(部分已有)
- **必现字段**:返回箭头、自定义导航 `客户详情`、Banner(头像首字 + 客户名 + 4 项统计:储值余额/60天消费/理想间隔/距今到店)、手机号 `138****5678` + 查看/复制、"AI 智能洞察" 卡片(summary + 3 条策略,色彩区分)、"维客线索" 7 条(分类标签 + emoji + text + source + 详情可展开)、"助教任务分配 · 当前进行中" 4 张助教卡(等级/任务类型/状态:normal/pinned/abandoned + 最后服务 + 3 项 metrics)、"最喜欢的助教 · 近60天" 2 张(emoji + 关系指数 + 4 项 stats)、"消费记录 / 商城订单"(总金额 + 列表;每条:type=table/shop/recharge,台桌行带助教明细 + 食品酒水 + 总原价/优惠 + 支付方式 + 充值额)、"备注"(按时间倒序)、底部 "问问助手" + "备注" 按钮
- **数据展示标准**:金额 `8,600`(无 ¥ 前缀,WXML 字面量加);天数 `12天`;关系指数 `9.2`;消费记录金额需要拆分 tableFee/foodAmount/totalAmount,且区分 `tableOrigPrice/foodOrigPrice`(原价 vs 折后)
- **空态文案**:加载 `加载中...`、空 `未找到客户信息`、错误 `加载失败` + `点击重试`、消费记录空 `暂无消费记录`、备注空 `暂无备注`
- **典型异常路径**:`onLoad(options)` 当前未读取 memberId(GAP-31);跳 chat/customer-service-records 也未传 customerId(GAP-31);手机号脱敏 `'138****5678'` 是 WXML 硬编码,应从 `detail.phone` 派生
- **测试时校核重点**:契约 CUST-1 严重不全 — `balance/consumption60d/idealInterval/daysSinceVisit/aiInsight/coachTasks/favoriteCoaches/消费记录拆分/备注` 9 类字段缺失(GAP-23~30);DQ-6 会员手机号需 JOIN `dim_member.mobile`;助教费用拆分 `assistant_pd_money` / `assistant_cx_money`。参考 `docs/miniprogram-dev/api-audit/customer-detail.md` L42-L120
---
### 3.15 pages/customer-service-records/customer-service-records
- **入口**:`customer-detail` "查看服务记录"(注意 GAP-31 当前未传参);其他详情入口
- **设计目的**:某客户的历史服务流水 — 月份切换 + 当月统计 + 服务记录列表
- **适用角色**:`head_coach` / `staff` / `manager`
- **关键 API**:`GET /api/customers/{id}/service-records`(待开发) + `GET /api/customers/{id}`
- **必现字段**:Banner(客户名首字 + 姓名 + "服务 X 次" 徽章 + 手机号 `139****5678` + 查看/复制)、月份切换 + monthLabel + canPrev/canNext、月度统计 3 项(本月服务 X次 / 服务时长 Xh / 关系指数 0.85)、服务记录卡片列表(每条:台号/课程类型 + typeClass + 时长 + 时间段 + drinks + income + isEstimate)、底部 `— 已加载全部记录 —`
- **数据展示标准**:服务次数 `monthCount + '次'`;时长 `(min/60).toFixed(1) + 'h'`;`recordType=course|recharge`;`typeClass=basic|vip|tip|recharge`
- **空态文案**:`暂无服务记录`、本月空 `本月暂无服务记录``加载失败,请点击重试` + `重试`
- **典型异常路径**:零 API;月份切换是本地筛选(GAP-35);`durationRaw/isEstimate/drinks` 都固定值需后端补;台号当前 `getTableNo(id)` 取模生成
- **测试时校核重点**:`recordType/isEstimate/customerPhoneFull/totalServiceCount` 在契约 CUST-2 缺失(GAP-32~34);时间段 startTime/endTime 当前是 `generateTimeRange()` 随机,需后端真返回。参考 `docs/miniprogram-dev/api-audit/customer-service-records.md` L24-L102
---
### 3.16 pages/customer-records/customer-records
> **api-audit 未覆盖**;以下信息基于源码 `apps/miniprogram/miniprogram/pages/customer-records/customer-records.ts` + AI_CHANGELOG 注释。
- **入口**:`customer-detail` "查看消费记录"(从 customer-detail 复用 Banner + 月份切换);2026-03-29 新建
- **设计目的**:与 customer-service-records 区分 — 这里是**消费**(支付/充值)记录视角,而非"服务"视角
- **适用角色**:`head_coach` / `staff` / `manager`
- **关键 API**:`fetchCustomerConsumptionRecords({ customerId, year, month })`(已对接,见 `services/api.ts`)
- **必现字段**:Banner 复用 customer-detail 头部(姓名首字/客户名/4 项统计/手机号 + 脱敏)、月份切换(走业务时钟 `getBusinessClock()` 而非 `new Date()`,sandbox 模式按 `business_year/month` 显示)、月度汇总(visitCount + consumeTotal + rechargeTotal)、消费记录列表(records 数组)
- **数据展示标准**:`detail.balance/consumption60d/idealInterval/daysSinceVisit` 都是 number 或 null,WXML 端用 `fmt.money` 等格式化;Pydantic 字段名兼容 `consumption_60d``consumption60D`(大写 D,踩坑标注)
- **空态文案**:`pageState='loading'/'error'/'normal'`;monthLoading 二级加载态
- **典型异常路径**:`monthLoading` 时切月份会被防抖;`maxYearMonth` 走业务时钟,sandbox 不会越界到未来
- **测试时校核重点**:`detail.consumption60D` 大写 D 字段名兼容;`minYearMonth=202501` 是硬编码下边界;月份切换逻辑(`onPrevMonth/onNextMonth` 同时检查 `monthLoading`);手机号脱敏正则 `(\d{3})\d{4}(\d{4})`。参考源文件 L1-L158
---
### 3.17 pages/coach-detail/coach-detail
- **入口**:`board-coach` 助教卡 tap(`?id={coachId}`);其他详情页相关跳转
- **设计目的**:助教全景 — Banner + 绩效指标(6 项) + 档位进度条 + 收入明细(本月/上月各 4 项) + 任务执行 + 客户关系 TOP20 + 近期服务 + 历史月份 + 备注
- **适用角色**:`head_coach` / `manager`(管理视角);助教自己看自己也可
- **关键 API**:`GET /api/xcx/coaches/:id`(待对接) + 6 个子接口(tasks/top-customers/service-records/history) + `POST /notes`
- **必现字段**:Banner(头像 + 姓名 + 等级 + 技能 + 工龄/客户数/入职日期)、"绩效概览" 4 张 perfCards(本月定档业绩 + 折算前 / 本月工资预估 / 客源储值余额 + N位客户 / 本月任务完成 + 覆盖客户数)、"绩效档位进度" 进度条(动画:tierNodes [0,100,130,160,190,220] + maxHours=220 + 距升档差距)、"收入明细" 标签切换(本月/上月) + 4 行 income items(基础课时费/激励课时费/充值提成/酒水提成) + 合计行(预估或确认)、"任务执行" 本月完成统计(回访 N 个 / 召回 N 个) + 可见任务列表 + 折叠展开 + 已放弃任务、"客户关系 TOP20 · 近60天"(20 张卡:头像+ 心 emoji + score + 服务次数 + 余额 + 消费)、"近期服务明细" 4 条 + "查看更多服务记录 →" 跳 coach-service-records、"更多信息"(入职日期)、"备注记录 共 N 条"(空态 `暂无备注`)、底部 "问问助手" + "备注"
- **数据展示标准**:金额 `¥6,950`;课时 `87.5h`;tierNodes/maxHours 后端返回;perfCurrent/perfTarget 派生 perfPercent
- **空态文案**:加载 `加载中...`、空 `未找到助教信息`、错误 `加载失败` + `点击重试`、备注空 `暂无备注`
- **典型异常路径**:零 API;`tierNodes` Mock 注释 "实际由接口返回";`taskStats` 应由接口 #2 聚合
- **测试时校核重点**:契约 COACH-1 严重不全(GAP-38~44):performance(6 指标)/income(8 项)/tierNodes/TopCustomer(heartEmoji/score/balance/consume)/HistoryMonth/ServiceRecord.perfHours/TaskItem.notes/AbandonedTask.reason/workYears/hireDate;查看更多服务记录跳 `coach-service-records?coachId=` 而非 performance-records。参考 `docs/miniprogram-dev/api-audit/coach-detail.md` L23-L156
---
### 3.18 pages/coach-service-records/coach-service-records
> **api-audit 未覆盖**;以下信息基于源码 `apps/miniprogram/miniprogram/pages/coach-service-records/coach-service-records.ts` 文件头注释 + 实现。
- **入口**:`coach-detail` "近期服务明细" 卡片"查看更多"按钮(必传 `?coachId=`)
- **设计目的**:**管理者视角**的助教业绩明细页 — 与"任务"tab 下的 performance-records 同源 API,但视角不同(看别人 vs 看自己)
- **适用角色**:`head_coach` / `manager`(`view_board_coach` 权限)
- **关键 API**:`GET /api/xcx/performance/records?coach_id=&year=&month=&page=&pageSize=` + `fetchCoachBanner(coachId)`
- **必现字段**:Banner(走 `fetchCoachBanner` 拿 name/level/storeName,3 字段轻量接口)、自定义页面标题 `<助教名>的业绩`(同步原生 navbar 标题)、月份切换 + monthLabel、统计概览(总笔数/总课时 + 折前/总收入)、按日期分组的 dateGroups(date + totalHours + totalIncome + records 列表;每条 record 含 customerName/memberId/avatarColor/timeRange/hours/courseType/courseTagClass/location/income + isScattered 散客标记)、底部分页 `hasMore` 控制
- **数据展示标准**:金额 `formatMoney`、课时 `formatHours`、笔数 `formatCount(n,'笔')`;单条记录右下角显示"助教预估收入"(去第一人称);记录右下角对照 performance-records 自己看的"我的预估收入"
- **空态文案**:`pageState='loading'/'empty'/'error'/'normal'`
- **典型异常路径**:必传参数 `coachId` 缺失会 toast `缺少助教标识` + navigateBack;`isCurrentMonth` 判断(当月且 day<=5 才标记预估);点击单条记录跳 customer-detail(管理者关心客户),散客 memberId<=0 时 toast `散客无详情可查看`
- **测试时校核重点**:Banner 拼接 `<助教名>的业绩` 是否覆盖原生 navbar;月份切换 reset page=1 + dateGroups=[];`courseTagClass` 走 COURSE_TAG_MAP(陪打/基础课→basic、包厢/包厢课→room、超休/激励课/打赏课→incentive)。参考源文件 L1-L275
---
### 3.19 pages/chat/chat
- **入口**:`task-detail`/`customer-detail`/`coach-detail` 底部"问问助手";`chat-history` 列表项 tap
- **设计目的**:AI 对话页 — 历史消息 + 流式回复 + 引用卡片
- **适用角色**:全部
- **关键 API**:`GET /api/xcx/chat/{chatId}/messages`(待对接) + `POST /api/xcx/chat/stream` SSE(待补)
- **必现字段**:自定义导航栏 `AI 助手` + 状态栏占位、引用卡片(`reference-tag` 标签 + 标题 + 摘要 + 键值对数据,可选)、消息列表(头像 + 气泡 + IM 时间 HH:mm / MM-DD HH:mm + 时间分割线≥5min)、流式输出区(打字指示器 + streamingContent)、空对话提示("你好,我是 AI 助手" + "有什么可以帮你的?")、加载失败 + `重新加载`、输入框 + 发送按钮(`isStreaming` 时禁用)
- **数据展示标准**:`timeLabel`(相对) + `imTimeLabel`(IM 格式)由前端 `formatRelativeTime` / `formatIMTime` 派生;`showTimeDivider` 间隔≥5min 显示
- **空态文案**:`你好,我是 AI 助手` + `有什么可以帮你的?`(空消息态)
- **典型异常路径**:`customerId` vs `chatId` 不匹配(GAP-49) — 前端传 customerId 但 API 路径是 chatId;`historyId/coachId` 多入口路由未实现(GAP-50);流式当前是 `simulateStreamOutput` 模拟逐字 50ms
- **测试时校核重点**:`referenceCard.data` 是否后端结构化下发(GAP-45);`timestamp` 字段名 vs 契约 `created_at`(GAP-46);SSE 端点是否存在(GAP-51)。参考 `docs/miniprogram-dev/api-audit/chat.md` L20-L100
---
### 3.20 pages/chat-history/chat-history
- **入口**:`my-profile` 菜单 "助手对话记录"
- **设计目的**:对话记录列表 — 看历史会话标题 + 最后消息 + 时间
- **适用角色**:全部
- **关键 API**:`GET /api/xcx/chat/history?page=&pageSize=`(待对接)
- **必现字段**:自定义导航栏、对话列表(每条:AI 渐变图标 + title + lastMessage 摘要 + relativeTime + customerName 标签 if 有)、底部 `— 已加载全部记录 —`、AI 悬浮按钮
- **数据展示标准**:时间走 `formatRelativeTime` ISO 8601 输入;icon 走 6 色 ICON_GRADIENTS,2026-03-18 已改为 `hashIndex(id)` 按对话 ID 哈希固定(此前是随机刷新就变)
- **空态文案**:`暂无对话记录`(t-empty)、`加载失败,请重试` + `重新加载`、错误 emoji `😵`
- **典型异常路径**:零 API;`statusBarHeight` 已计算但 WXML 未消费(代码遗留);分页未实现(一次拉全)
- **测试时校核重点**:`title` 字段在契约 CHAT-1 缺失(GAP-47);`timestamp` vs `last_time` 字段名不一致(GAP-48);跳转 chat 传 `historyId`(目标页 GAP-50 未处理)。参考 `docs/miniprogram-dev/api-audit/chat-history.md` L19-L93
---
### 3.21 pages/dev-tools/dev-tools
> **api-audit 未覆盖**;以下信息基于源码 `apps/miniprogram/miniprogram/pages/dev-tools/dev-tools.ts`。开发调试用,无明确产品 spec。
- **入口**:my-profile / 任意页面手动 reLaunch(无生产入口);仅 `manager` 角色可用
- **设计目的**:开发期切换角色/状态/快速跳转任意页,验收用户上下文(roles/permissions/binding/storeName)
- **适用角色**:开发调试,生产环境应隐藏
- **关键 API**:`GET /api/xcx/dev-context`(获取调试上下文) / `POST /api/xcx/dev-switch-role`(切角色重签 token) / `POST /api/xcx/dev-switch-status`(切 user_status 重签 token)
- **必现字段**:当前用户上下文(roles/permissions/binding/storeName 4 行)、角色切换按钮组(coach/staff/head_coach/manager 4 个)、状态切换按钮组(new/pending/approved/rejected/disabled 5 个)、3 段页面跳转列表(正在迁移 4 / 已完成 14 / 未完成 0)、操作消息提示(success/error 3s 自动消失)
- **数据展示标准**:无金额/日期 — 这是调试 UI;按钮 disable 当 code===currentRole;reLaunch 跳转覆盖 tabBar 也能进
- **空态文案**:未登录时 `未登录,请先通过 dev-login 获取 token`
- **典型异常路径**:无 token 时不发请求避免 401 死循环;切角色/切状态失败 toast `切换角色失败: ${detail}`;路径列表写死 18 项页面
- **测试时校核重点**:`/api/xcx/dev-context` 返回字段(roles/permissions/binding{binding_type/assistant_id/staff_id}/status);切角色后 `app.globalData.token/refreshToken` + 4 个 storage 是否同步刷新。参考源文件 L19-L184
---
## 四、跨页共性约定
### 金额展示
- 整数无小数,千分位逗号 → `¥12,680`
- 负数 `-¥368`(负号在 ¥ 前,严禁 `¥-368`)
- 零值 `¥0`(严禁 `¥0.00`)
- 空值 / undefined → `--`(严禁直显 undefined/null/NaN/空串)
- 大额不简写(严禁 `¥12万`)
- Mock 字段统一 `number` 类型,不带 `¥` 前缀(由 `formatMoney` / WXS `fmt.money` 加)
- 来源:`docs/miniprogram-dev/design-system/DISPLAY-STANDARDS.md` §1
### 时间展示
- < 120s → `刚刚`
- 2~59min → `N分钟前`
- 1~23h → `N小时前`
- 1~3d → `N天前`(严禁 4天前/7天前/30天前 之类)
- > 3d 同年 → `MM-DD`(`03-10`)
- > 3d 跨年 → `YYYY-MM-DD`(`2025-08-21`)
- 严禁中文 `年月日`、严禁混 `/` 分隔符、严禁 > 3d 后展示时分秒
- 时间戳基准必须服务端 UTC,前端按设备时区渲染
- IM 场景另用 `formatIMTime``HH:mm` / `MM-DD HH:mm`
- 任务截止日期 `formatDeadline``今天到期 / 还剩 N 天 / MM-DD / 逾期 N 天`
- 来源:`docs/miniprogram-dev/design-system/DATETIME-DISPLAY-STANDARD.md` §2 + DISPLAY-STANDARDS.md §7
### 课时展示
- 整数小时 `2h`、非整数 `2.5h`(保留 1 位)
- 折算备注 `2.0h(折后 2.5h)`(仅当 hoursRaw 存在且≠hours)
- 零值 `0h`、空值 `--`
- 来源:DISPLAY-STANDARDS.md §2
### 计数展示
- 单位前端拼接,Mock 只存 number(`32``32笔`)
- 计数零值/空值都展示 `--`(注:与金额零值 `¥0` 不同)
- 单位:笔(交易/记录) / 次(到店) / 人(客户) / 个(任务) / 年(工龄)
- 来源:DISPLAY-STANDARDS.md §3
### 百分比与进度条
- 文字 `58.3%`(保留 1 位);进度条 CSS 宽度 `width:58.3%` 截断 0~100
- 超 100% 文字正常显示,进度条截断到 100
- 来源:DISPLAY-STANDARDS.md §5
### 等级文案(单一数据源)
- 助教等级:`junior=初级 / middle=中级 / senior=高级 / star=⭐ 星级`(API 用英文 key,渲染层翻中文)
- 关系等级:`excellent=很好(>8.5) / good=良好(6-8.5) / normal=一般(3.5-6) / poor=待发展(<3.5)`
- 客户标签:`basic_info / consumption / play_pref / promo_pref / social / feedback`
- 来源:DISPLAY-STANDARDS.md §6
### 错误兜底
- `--` 占位(严禁 `暂无` / `无` / `N/A` / `-`)
- 列表空走 `<t-empty description="..."/>` 而非 `--`
- 加载失败统一文案 `加载失败,请重试` + `重新加载`(/ `加载失败,请点击重试` + `重试` 两套)
- 网络错误一般 toast,非占位
### 加载态
- 进入页 `pageState='loading'`(走 `g-toast-loading-text` 浮层 `加载中...`)
- 成功后切换到 `'normal'/'empty'`,失败 `'error'`
- 二级加载(月份切换 / 触底)走 `monthLoading=true` + `wx.showLoading`
- 联调前许多页用 `setTimeout(400~600)` 模拟,联调时移除
### 路由跳转
- 进 tabBar 用 `wx.switchTab`、跨 tab 详情用 `wx.navigateTo`、登录/审核状态切用 `wx.reLaunch`
- 跳转失败统一 toast `页面跳转失败`
- 关键参数:`?id=` / `?customerId=` / `?taskId=` / `?coachId=` / `?historyId=`(详见每页"路由参数"段)
### globalData 与 storage
- `globalData.{ token, refreshToken, authUser:{ userId, status, nickname, role?, storeName?, coachLevel?, avatar? } }`
- 4 个 storage key:`token / refreshToken / userId / userStatus`
- `authUser` 当前缺 4 字段是已知 GAP-01,需后端 `/me` 扩展或前端各页二次拉取
---
## 五、设计标准锚点(DISPLAY-STANDARDS-1/2 摘要)
1. 金额绝不简写,绝不 toLocaleString(各设备行为不一致),绝不带 `.00`
2. 时间相对值上限是 3 天,>3 天必须切日期格式;同年省略年份
3. 课时折算用 `hours` + 可选 `hoursRaw`(number 字段),不要把 `(折0.5h)` 字符串塞进金额
4. 等级、客户标签类别统一英文 key,中文文案渲染时再翻;`coach-level-tag` 组件、`vi-colors.ts` 是 SSOT
5. 评分一律 0~10 分制(后端),展示走 `scoreToHalfStar` 映射 0~5 星(支持半星);0/null/undefined 视为未评分,展示 `--`
6. Mock 数据迁移目标:页面内联 mock → `utils/mock-data.ts`,联调时统一替换;字段名与接口契约一致以减少重命名
---
## 六、文档冲突待确认清单
| # | 冲突点 | 涉及文档 | 待 Neo 确认 |
| --- | --- | --- | --- |
| C1 | tabBar 是 3 项还是动态过滤 | `app.json` 写死 3 项 vs `auth-guard.ts` + storyboard 描述按角色过滤 | 待 Neo 确认 — 是否仍用 custom-tab-bar 动态隐藏 |
| C2 | login 跳转 approved → `/pages/mvp/mvp` 还是 `/pages/task-list/task-list` | `_hardcode-summary.md` 第 16-18 项已统一改为 task-list,但 `reviewing.md` L52、`no-permission.md` L50 仍写 `/pages/mvp/mvp` | 待 Neo 确认 — 旧文档过期,以代码为准 |
| C3 | 维客线索 `tag` 字段格式 | task-detail 内联 mock 用 `"客户\n基础"`(含 `\n`) vs `mock-data.ts` 类型定义 vs 契约 TASK-2 | 待 Neo 确认 — 含换行 vs 单字符串 vs 枚举 key |
| C4 | 维客线索 `source` 字段格式 | 前端 mock `'By:小燕'` / `'By:系统'` vs 类型定义 `'manual' \| 'ai_consumption' \| 'ai_note'` | 待 Neo 确认 — 是字符串显示还是枚举 |
| C5 | 课程类型 class 前缀 | performance-records 用 `tag-basic/tag-vip/tag-tip`、coach-service-records 用 `basic/room/incentive` | 待 Neo 确认 — 统一一种 |
| C6 | task-detail 跳 chat/customer-service-records 时传的 id 语义 | 当前传 `detail.id`(taskId),但目标页期望 customerId | 待 Neo 确认 — TASK-2 响应应增加 `customer_id` |
| C7 | performance 跳 task-detail 时传 customerName 而非 id | 不可定位任务 | 待 Neo 确认 — 改为传 task_id |
| C8 | customer-detail "查看消费记录" 跳 customer-records 还是 customer-service-records | 两个页面同时存在但语义不同(消费 vs 服务);customer-records 是 2026-03-29 新建 | 待 Neo 确认 — 两入口都保留还是合并 |
| C9 | chat 入口参数语义 | 多个入口分别传 customerId / historyId / coachId,但 `loadMessages` 仅用 customerId | 待 Neo 确认 — 多入口路由逻辑由谁实现 |
| C10 | ChatMessage.timestamp vs created_at 字段名 | 前端 `timestamp`,契约 `created_at` | 待 Neo 确认 — 前端改还是后端改 |
| C11 | ChatHistoryItem `title` 是否后端返回 | 契约无,前端展示需要 | 待 Neo 确认 — title 是 AI 摘要还是首条消息截断 |
| C12 | board-coach `time=last_6m + sort=sv_desc` 不兼容约束 | 写在前端 TIME_OPTIONS 注释 | 待 Neo 确认 — 是否后端会拒绝该组合 |
| C13 | board-finance "AI 洞察" 12 项指标的 cache_type | 前端 3 行硬编码;后端 `biz.ai_cache` 没有约定 type | 待 Neo 确认 — 新建 cache_type 枚举 |
| C14 | dev-tools 角色列表 vs `auth-guard.ts` 实际枚举 | `ROLE_LIST=[coach,staff,head_coach,manager]` 缺 site_admin/tenant_admin | 待 Neo 确认 — 调试是否需要覆盖全角色 |
| C15 | no-permission 管理员姓名 `厉超` 硬编码 | `_hardcode-summary.md` 第 6 项决策"保持硬编码(只有一个管理员)" | 待 Neo 确认 — 多门店上线时如何下发 |
| C16 | customer-records 字段名 `consumption60D`(大写 D) | Pydantic 反序列化后变 D,代码注释标 "踩坑" | 待 Neo 确认 — 后端 alias 规范统一 |
| C17 | apply 跳 approved 应到 task-list 还是 mvp | `apply.md` L57 仍写 `/pages/mvp/mvp` | 待 Neo 确认 — 与 C2 同源,旧文档过期 |
| C18 | 散客 memberId 取值约定 | coach-service-records 用 `memberId<=0` 判散客,toast `散客无详情可查看` | 待 Neo 确认 — 后端是否真用 0/-1/NULL |
---
> 最后更新:2026-05-04 / 维护者:NeoZQYY 小程序前端组