feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
826
docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md
Normal file
826
docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md
Normal file
@@ -0,0 +1,826 @@
|
||||
# 小程序 Storyboard 走查报告 — 助教视角(小燕)
|
||||
|
||||
> 走查日期:2026-03-18
|
||||
> 走查角色:助教"小燕",已登录,status=approved
|
||||
> 对照文档:`docs/miniprogram-dev/API-contract.md`(契约)、`docs/prd/Neo_Specs/NS1-xcx-backend-api.md`(spec)
|
||||
> 目标:提取 spec 未记录的接口需求细节
|
||||
|
||||
---
|
||||
|
||||
## 场景 1:登录 → 任务列表(app.ts)
|
||||
|
||||
### API 调用
|
||||
- `checkAuthStatus()` → `GET /api/xcx/me` → 返回 `ApiUserInfo`
|
||||
- 参数来源:token 从 `wx.getStorageSync('token')` 读取
|
||||
- 成功后根据 `data.status` 路由:`approved` → `reLaunch('/pages/task-list/task-list')`
|
||||
|
||||
### 前端期望字段(/api/xcx/me 响应)
|
||||
| 字段 | 类型 | 说明 | 契约定义 |
|
||||
|------|------|------|---------|
|
||||
| `user_id` | number | 用户 ID | ✅ |
|
||||
| `status` | enum | `new/pending/approved/rejected/disabled` | ✅ |
|
||||
| `nickname` | string | 昵称 | ✅ |
|
||||
| `role` | string | 角色 | ✅ REQ-4 |
|
||||
| `store_name` | string | 门店名 | ✅ REQ-4 |
|
||||
| `coach_level` | string? | 助教等级 | ✅ REQ-4 |
|
||||
| `avatar` | string? | 头像 URL | ✅ REQ-4 |
|
||||
|
||||
### globalData 结构
|
||||
```typescript
|
||||
globalData: {
|
||||
token?: string
|
||||
refreshToken?: string
|
||||
authUser?: { userId: number, status: string, nickname: string }
|
||||
}
|
||||
```
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-01**:`globalData.authUser` 未存储 `role`、`store_name`、`coach_level`、`avatar`,但下游页面(task-list banner、performance banner、performance-records banner)需要这些字段。前端需扩展 `globalData.authUser` 或各页面单独请求 `/me`。
|
||||
|
||||
---
|
||||
|
||||
## 场景 2:任务列表页(task-list.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchTasks(params?)` → `GET /api/xcx/tasks?status=&page=&pageSize=`
|
||||
- 返回:`{ tasks: Task[], performance: PerformanceData, total: number, hasMore: boolean }`
|
||||
- 页面加载时无参数调用(获取全部任务)
|
||||
|
||||
### 前端期望字段 — Task(任务卡片)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 任务 ID | ✅ |
|
||||
| `customerName` | string | 客户姓名 | ✅ |
|
||||
| `customerAvatar` | string | 客户头像 | ✅ |
|
||||
| `taskType` | enum | `callback/priority_recall/relationship/high_priority` | ✅ |
|
||||
| `taskTypeLabel` | string | 类型中文标签 | ✅ |
|
||||
| `deadline` | string | 截止日期 ISO 8601 | ✅ |
|
||||
| `heartScore` | number | 爱心评分 0-10 | ✅ |
|
||||
| `hobbies` | string[] | 爱好标签 | ✅ |
|
||||
| `isPinned` | boolean | 是否置顶 | ✅ |
|
||||
| `hasNote` | boolean | 是否有备注 | ✅ |
|
||||
| `status` | enum | `pending/completed/abandoned` | ✅ |
|
||||
|
||||
### 前端期望字段 — PerformanceData(绩效概览卡片)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-1 performance | 前端实际使用 |
|
||||
|------|------|------|------------------------|-------------|
|
||||
| `monthlyIncome` | number | 月收入 | ❌ 契约仅有 `total_income` | `buildPerfData()` 用 |
|
||||
| `incomeChange` | number | 收入变化百分比 | ❌ 契约无 | `incomeTrend` 展示 |
|
||||
| `currentTier` | string | 当前档位名称 | ❌ 契约无 | `bannerTitle` |
|
||||
| `nextTierGap` | number | 距下一档差距 | ❌ 契约无 | `remainHours` |
|
||||
| `todayServiceCount` | number | 今日服务次数 | ❌ 契约无 | 未使用 |
|
||||
| `weekServiceCount` | number | 本周服务次数 | ❌ 契约无 | 未使用 |
|
||||
| `monthServiceCount` | number | 本月服务次数 | ❌ 契约无 | 未使用 |
|
||||
|
||||
### 前端期望字段 — buildPerfData() 需要后端返回的绩效字段
|
||||
| 字段 | 类型 | 说明 | 契约/spec 定义 |
|
||||
|------|------|------|---------------|
|
||||
| `tierNodes` | number[] | 档位节点数组,如 `[0,100,130,160,190,220]` | ❌ 未定义 |
|
||||
| `totalHours` | number | 当月总工时(折算后) | 契约有 `total_hours` |
|
||||
| `basicHours` | number | 基础课时 | ❌ 未定义 |
|
||||
| `bonusHours` | number | 激励课时 | ❌ 未定义 |
|
||||
| `currentTier` | number | 当前档位索引 | ❌ 未定义 |
|
||||
| `nextTierHours` | number | 下一档位工时阈值 | ❌ 未定义 |
|
||||
| `tierCompleted` | boolean | 是否已达标(满档) | ❌ 未定义 |
|
||||
| `bonusMoney` | number | 升档奖金 | ❌ 未定义 |
|
||||
| `incomeFormatted` | string | 格式化收入 | 契约有 `total_income` |
|
||||
| `incomeTrend` | string | 收入趋势(如 `↓368`) | ❌ 未定义 |
|
||||
| `incomeTrendDir` | enum | `up/down` | ❌ 未定义 |
|
||||
| `incomeMonth` | string | 当前月份标签 | 契约有 `month_label` |
|
||||
| `prevMonth` | string | 上月标签 | ❌ 未定义 |
|
||||
|
||||
### enrichTask() 需要后端返回的扩展字段
|
||||
| 字段 | 类型 | 说明 | 契约/spec 定义 |
|
||||
|------|------|------|---------------|
|
||||
| `lastVisitDays` | number | 距上次到店天数 | ❌ 前端 mock 计算,后端需返回 |
|
||||
| `balance` | number | 客户余额 | ❌ 未定义在 TASK-1 |
|
||||
| `aiSuggestion` | string | AI 建议摘要 | ❌ 未定义在 TASK-1 |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击任务卡片 | `/pages/task-detail/task-detail` | `?id={taskId}` |
|
||||
| 点击绩效卡片 | `/pages/performance/performance` | 无参数 |
|
||||
| 长按→AI助手 | `/pages/ai-chat/ai-chat` | `?taskId={id}&customerName={name}` |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 请求体 | 成功后行为 |
|
||||
|------|-----|--------|-----------|
|
||||
| 长按→置顶/取消置顶 | `POST /tasks/{id}/pin` 或 `/unpin` | 无 | 本地更新 `isPinned`,重新分组 |
|
||||
| 长按→放弃 | `POST /tasks/{id}/abandon` | `{ reason: string }` | 本地移至 abandonedTasks |
|
||||
| 长按→取消放弃 | `POST /tasks/{id}/restore` | 无 | 本地移至 normalTasks |
|
||||
| 长按→添加备注 | `POST /notes` | `{ content, taskId?, customerId? }` | Toast 提示 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-02**:契约 TASK-1 `performance` 仅有 4 个字段(`total_hours/total_income/total_customers/month_label`),但前端 `buildPerfData()` 需要 15+ 个字段(`tierNodes`、`basicHours`、`bonusHours`、`currentTier`、`nextTierHours`、`tierCompleted`、`bonusMoney`、`incomeTrend`、`incomeTrendDir`、`prevMonth` 等)。这是最大的 Gap。
|
||||
- **GAP-03**:`enrichTask()` 需要 `lastVisitDays`(距上次到店天数)、`balance`(客户余额)、`aiSuggestion`(AI 建议),这 3 个字段在 TASK-1 契约中未定义。后端需在任务列表 item 中附带这些字段,或前端改为进入详情页后再加载。
|
||||
- **GAP-04**:`pin/unpin` 操作的 API 端点在契约中未定义。spec 提到已实现 `pin/unpin`,但 `api.ts` 中无对应函数,前端仅做本地状态更新。需确认 API 路径(`POST /tasks/{id}/pin` 和 `POST /tasks/{id}/unpin`)。
|
||||
- **GAP-05**:`createNote` 的 `score` 参数 — 前端备注弹窗 `onNoteConfirm` 传递 `{ score, content }`,但 `api.ts` 的 `createNote` 签名仅有 `{ content, taskId?, customerId? }`,缺少 `score` 字段。
|
||||
|
||||
---
|
||||
|
||||
## 场景 3:任务详情页(task-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchTaskDetail(taskId)` → `GET /api/xcx/tasks/{taskId}`
|
||||
- 参数来源:`options.id`(从 task-list 跳转传入)
|
||||
- `deleteNote(noteId)` → `DELETE /api/xcx/notes/{noteId}`
|
||||
|
||||
### 前端期望字段 — TaskDetail(完整清单)
|
||||
|
||||
#### 基础信息(继承 Task)
|
||||
同 TASK-1 item 字段,契约已覆盖。
|
||||
|
||||
#### 维客线索 retentionClues(内联 mock,8 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `tag` | string | 线索大类标签(如 `"客户\n基础"`) | ❌ 契约用 `tag` 但格式不同 |
|
||||
| `tagColor` | enum | `primary/success/purple/error` | ✅ `tag_color` |
|
||||
| `emoji` | string | 表情符号 | ✅ |
|
||||
| `text` | string | 线索摘要 | ✅ |
|
||||
| `source` | string | 来源(如 `"By:系统"`、`"By:小燕"`) | ⚠️ 契约有 `source` 但格式不同 |
|
||||
| `desc` | string? | 详细描述(展开后显示) | ✅ |
|
||||
| `expanded` | boolean | 展开状态(前端本地) | — |
|
||||
|
||||
#### 话术参考 talkingPoints(内联 mock,5 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `talkingPoints` | string[] | 话术文本数组 | ✅ |
|
||||
|
||||
#### 服务记录 serviceRecords(内联 mock,4 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `table` | string | 台桌号 | ✅ |
|
||||
| `type` | string | 课程类型标签 | ✅ |
|
||||
| `typeClass` | enum | `basic/vip/tip/recharge/incentive` | ✅ `type_class` |
|
||||
| `recordType` | enum? | `course/recharge` | ✅ `record_type` |
|
||||
| `duration` | number | 折算后课时(小时) | ✅ |
|
||||
| `durationRaw` | number? | 折算前课时(小时) | ✅ `duration_raw` |
|
||||
| `income` | number | 收入(元) | ✅ |
|
||||
| `isEstimate` | boolean? | 是否预估金额 | ✅ `is_estimate` |
|
||||
| `drinks` | string | 酒水描述 | ✅ |
|
||||
| `date` | string | 日期(格式化后) | ✅ |
|
||||
|
||||
#### 服务汇总 serviceSummary
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `totalHours` | number | 总工时 | ✅ |
|
||||
| `totalIncome` | number | 总收入 | ✅ |
|
||||
| `avgIncome` | number | 平均收入 | ✅ |
|
||||
|
||||
#### AI 分析 aiAnalysis
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `summary` | string | AI 分析摘要 | ✅ |
|
||||
| `suggestions` | string[] | AI 建议列表 | ✅ |
|
||||
|
||||
#### 备注 notes(内联 mock,5 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ✅ |
|
||||
| `content` | string | 备注内容 | ✅ |
|
||||
| `tagType` | enum | `customer/coach/system` | ✅ `tag_type` |
|
||||
| `tagLabel` | string | 标签文案 | ✅ `tag_label` |
|
||||
| `createdAt` | string | 创建时间 | ✅ `created_at` |
|
||||
| `score` | number? | 满意度评分 0-10 | ✅ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 问问助手 | `/pages/chat/chat` | `?customerId={detail.id}` |
|
||||
| 查看全部服务记录 | `/pages/customer-service-records/customer-service-records` | `?customerId={detail.id}` |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 请求体 | 成功后行为 |
|
||||
|------|-----|--------|-----------|
|
||||
| 添加备注 | `POST /notes` | `{ content }` | 本地 prepend 到 sortedNotes |
|
||||
| 删除备注 | `DELETE /notes/{noteId}` | 无 | 本地 filter 移除 |
|
||||
| 放弃任务 | `POST /tasks/{id}/abandon` | `{ reason }` | 更新 `detail.status='abandoned'` |
|
||||
| 取消放弃 | `POST /tasks/{id}/restore` | 无 | 更新 `detail.status='pending'` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-06**:维客线索的 `tag` 字段格式不一致 — 前端内联 mock 使用 `"客户\n基础"` 格式(含换行符),契约定义为普通 string。后端需明确 tag 的取值枚举和格式。
|
||||
- **GAP-07**:维客线索的 `source` 字段格式不一致 — 前端显示 `"By:系统"`、`"By:小燕"`,但 mock-data.ts 中 `RetentionClue.source` 定义为 `'manual' | 'ai_consumption' | 'ai_note'`。前端内联 mock 与类型定义不匹配,后端需明确返回格式。
|
||||
- **GAP-08**:`aiAnalysis` 数据来源 — spec §3.2 提到来自 `biz.ai_cache`(app4/app5/app6/app7),但契约未说明 `cache_type` 映射关系。需明确:`ai_analysis.summary` 来自哪个 `cache_type`?`talking_points` 来自哪个 `cache_type`?
|
||||
- **GAP-09**:task-detail 跳转 chat 时传 `customerId={detail.id}`,但 `detail.id` 实际是 **taskId** 不是 customerId。前端需要一个 `customerId` 字段(TASK-2 响应中需包含 `customer_id`)。
|
||||
- **GAP-10**:task-detail 跳转 customer-service-records 时同样传 `customerId={detail.id}`,存在同样的 taskId/customerId 混淆问题。TASK-2 响应需包含 `customer_id`。
|
||||
- **GAP-11**:`storageLevel`(储值等级,如"非常多")和 `relationLevel/relationLevelText/relationColor`(关系等级)在前端本地计算,但后端未定义这些字段。后端是否需要返回,还是前端根据 `heartScore` 自行计算?
|
||||
|
||||
---
|
||||
|
||||
## 场景 4:绩效概览页(performance.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchPerformanceOverview({ year, month })` → `GET /api/xcx/performance?year=&month=`
|
||||
- 参数来源:`new Date()` 取当前年月
|
||||
- 返回类型:`PerformanceData`(mock-data.ts 定义)
|
||||
|
||||
### 前端期望字段 — 完整清单(对比契约 PERF-1)
|
||||
|
||||
#### Banner 数据(内联 mock,非 API 返回)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 | 数据源 |
|
||||
|------|------|------|------------|--------|
|
||||
| `coachName` | string | 助教姓名 `'小燕'` | ❌ 未定义 | 应从 globalData.authUser 或 /me |
|
||||
| `coachRole` | string | 角色 `'助教'` | ❌ 未定义 | 同上 |
|
||||
| `storeName` | string | 门店名 `'广州朗朗桌球'` | ❌ 未定义 | 同上 |
|
||||
| `monthlyIncome` | string | 本月收入 `'¥6,206'` | ⚠️ 契约有 `total_income` 但是 number | 需格式化 |
|
||||
| `lastMonthIncome` | string | 上月收入 `'¥16,880'` | ❌ 未定义 | 需后端额外返回 |
|
||||
|
||||
#### 收入档位(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `currentTier.basicRate` | number | 当前档基础费率 80 | ❌ 未定义 |
|
||||
| `currentTier.incentiveRate` | number | 当前档激励费率 95 | ❌ 未定义 |
|
||||
| `nextTier.basicRate` | number | 下一档基础费率 90 | ❌ 未定义 |
|
||||
| `nextTier.incentiveRate` | number | 下一档激励费率 114 | ❌ 未定义 |
|
||||
| `upgradeHoursNeeded` | number | 距升档所需工时 15 | ❌ 未定义 |
|
||||
| `upgradeBonus` | number | 升档奖金 800 | ❌ 未定义 |
|
||||
|
||||
#### 收入明细 incomeItems(内联 mock,4 项)
|
||||
| 项目 | icon | desc 格式 | 契约 PERF-1 |
|
||||
|------|------|----------|------------|
|
||||
| 基础课 | 🎱 | `80元/h × 75h` | ⚠️ 契约有 `income_items` 但字段不同 |
|
||||
| 激励课 | ⭐ | `95.05元/h × 10h` | 同上 |
|
||||
| 充值激励 | 💰 | `客户充值返佣` | 同上 |
|
||||
| TOP3 销冠奖 | 🏆 | `全店业绩前三名奖励` | 同上 |
|
||||
|
||||
契约 `income_items` 定义:`Array<{ label, amount, icon }>`,缺少 `desc` 字段。
|
||||
|
||||
#### 服务记录 thisMonthRecords(内联 mock,按日期分组)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `date` | string | 日期标签 `'2月7日'` | ❌ 契约 `this_month_records` 结构完全不同 |
|
||||
| `totalHours` | string | 当日总工时 `'4.0h'` | ❌ |
|
||||
| `totalIncome` | string | 当日总收入 `'¥350'` | ❌ |
|
||||
| `records[].customerName` | string | 客户名 | ⚠️ 契约有 `customer_name` |
|
||||
| `records[].avatarChar` | string | 头像首字 | ❌ |
|
||||
| `records[].avatarColor` | string | 头像颜色 | ❌ |
|
||||
| `records[].timeRange` | string | 时间段 `'20:00-22:00'` | ❌ |
|
||||
| `records[].hours` | string | 工时 `'2.0h'` | ⚠️ 契约有 `hours` 但是 number |
|
||||
| `records[].courseType` | string | 课程类型 | ❌ |
|
||||
| `records[].courseTypeClass` | string | 样式类 | ❌ |
|
||||
| `records[].location` | string | 台号 | ❌ |
|
||||
| `records[].income` | string | 收入 `'¥160'` | ⚠️ 契约有 `income` 但是 number |
|
||||
|
||||
#### 新客列表 newCustomers
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 契约有 `avatar_char` |
|
||||
| `avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `lastService` | string | 最后服务日期 | ❌ 未定义 |
|
||||
| `count` | number | 服务次数 | ❌ 未定义 |
|
||||
|
||||
#### 常客列表 regularCustomers
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 契约有 `avatar_char` |
|
||||
| `avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `hours` | number | 总工时 | ❌ 未定义 |
|
||||
| `income` | string | 总收入 | ❌ 未定义 |
|
||||
| `count` | number | 到店次数 | ⚠️ 契约有 `visits` |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 查看全部记录 | `/pages/performance-records/performance-records` | 无参数 |
|
||||
| 点击客户卡片 | `/pages/task-detail/task-detail` | `?customerName={name}` |
|
||||
| 点击服务记录 | `/pages/task-detail/task-detail` | `?customerName={name}&taskId={taskId}` |
|
||||
| 点击收入概览 | `/pages/performance-records/performance-records` | 无参数 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-12**:契约 PERF-1 `this_month_records` 是扁平数组 `Array<{ customer_name, hours, income, date }>`,但前端需要 **按日期分组的 DateGroup 结构**(含 `date`、`totalHours`、`totalIncome`、`records[]`),且每条记录需要 `timeRange`、`courseType`、`courseTypeClass`、`location` 等字段。契约需大幅扩展。
|
||||
- **GAP-13**:收入档位数据(`currentTier`、`nextTier`、`upgradeHoursNeeded`、`upgradeBonus`)在契约 PERF-1 中完全未定义。这些数据来自 `dws_assistant_salary_calc`,后端需返回。
|
||||
- **GAP-14**:`lastMonthIncome`(上月收入)在契约中未定义,前端 Banner 需要展示。
|
||||
- **GAP-15**:`incomeItems` 的 `desc` 字段(如 `"80元/h × 75h"`)在契约中未定义。后端需返回费率和工时的拆分数据,或直接返回格式化的 desc 字符串。
|
||||
- **GAP-16**:`newCustomers` 缺少 `lastService`(最后服务日期)和 `count`(服务次数)字段;`regularCustomers` 缺少 `hours`(总工时)和 `income`(总收入)字段。
|
||||
- **GAP-17**:跳转 task-detail 时传 `customerName` 而非 `taskId`,但 task-detail 页面 `onLoad` 读取的是 `options.id`。参数传递不匹配,联调时会导致页面无法加载正确数据。
|
||||
- **GAP-18**:页面无月份切换功能(F8),API 支持 `year/month` 参数但页面固定当前月。
|
||||
|
||||
---
|
||||
|
||||
## 场景 5:绩效明细页(performance-records.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchPerformanceRecords({ year, month })` → `GET /api/xcx/performance/records?year=&month=&page=&pageSize=`
|
||||
- 参数来源:`currentYear`/`currentMonth`(页面 data,支持月份切换)
|
||||
- Banner 数据从 `getApp().globalData.authUser` 读取
|
||||
|
||||
### 前端期望字段 — summary
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-2 |
|
||||
|------|------|------|------------|
|
||||
| `totalCount` | number | 总笔数 32 | ✅ `total_count` |
|
||||
| `totalHours` | number | 总工时 59.0 | ✅ `total_hours` |
|
||||
| `totalHoursRaw` | number | 折算前总工时 63.5 | ✅ `total_hours_raw` |
|
||||
| `totalIncome` | number | 总收入 4720 | ✅ `total_income` |
|
||||
|
||||
### 前端期望字段 — dateGroups(按日期分组)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-2 |
|
||||
|------|------|------|------------|
|
||||
| `date` | string | 日期标签 `'2月7日'` | ✅ |
|
||||
| `totalHours` | number | 当日总工时 | ✅ |
|
||||
| `totalIncome` | number | 当日总收入 | ✅ |
|
||||
| `records[].id` | string | 记录 ID | ✅ |
|
||||
| `records[].customerName` | string | 客户名 | ✅ |
|
||||
| `records[].avatarChar` | string | 头像首字 | ❌ 未定义 |
|
||||
| `records[].avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `records[].timeRange` | string | 时间段 | ✅ |
|
||||
| `records[].hours` | number | 折算后工时 | ✅ |
|
||||
| `records[].hoursRaw` | number? | 折算前工时 | ✅ |
|
||||
| `records[].courseType` | string | 课程类型 | ✅ |
|
||||
| `records[].courseTypeClass` | string | 样式类 | ✅ |
|
||||
| `records[].location` | string | 台号 | ✅ |
|
||||
| `records[].income` | number | 收入 | ✅ |
|
||||
|
||||
### courseTypeClass 完整枚举
|
||||
前端使用的样式类:`tag-basic`(基础课)、`tag-vip`(包厢课)、`tag-tip`(打赏课)。
|
||||
契约定义的 `course_type_class`:未明确枚举值。
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-19**:`avatarChar`(头像首字)和 `avatarColor`(头像颜色)在契约 PERF-2 中未定义。前端通过 `nameToAvatarColor()` 工具函数从姓名生成,但后端是否需要返回?建议前端自行计算。
|
||||
- **GAP-20**:`courseTypeClass` 的完整枚举值未在契约中明确。前端使用 `tag-basic`/`tag-vip`/`tag-tip`,但后端返回的是 `basic`/`vip`/`tip` 还是带 `tag-` 前缀?需统一。
|
||||
- **GAP-21**:月份切换时未重置分页(F9 bug),`switchMonth()` 中 `page` 未重置为 1。
|
||||
- **GAP-22**:Banner 字段 `coachName`/`coachLevel`/`storeName` 从 `globalData.authUser` 读取,但 `authUser` 当前未存储 `coachLevel` 和 `storeName`(见 GAP-01)。
|
||||
|
||||
---
|
||||
|
||||
## 场景 6:客户详情页(customer-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCustomerDetail(customerId)` → `GET /api/xcx/customers/{customerId}`
|
||||
- 参数来源:页面路由参数(但当前代码从 `__route__` 解析,有 bug)
|
||||
|
||||
### 前端期望字段 — 完整清单
|
||||
|
||||
#### 客户基本信息(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 客户 ID | ✅ |
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 未定义 |
|
||||
| `phone` | string | 手机号(完整) | ⚠️ 契约有 `phone`(脱敏)和 `phone_full`(完整) |
|
||||
| `balance` | string | 余额 `'8,600'` | ❌ 未定义 |
|
||||
| `consumption60d` | string | 近60天消费 `'2,800'` | ❌ 未定义 |
|
||||
| `idealInterval` | string | 理想到店间隔 `'7天'` | ❌ 未定义 |
|
||||
| `daysSinceVisit` | string | 距上次到店 `'12天'` | ❌ 未定义 |
|
||||
|
||||
#### AI 洞察 aiInsight(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `summary` | string | AI 分析摘要 | ❌ 未定义 |
|
||||
| `strategies` | Array<{color, text}> | 策略建议列表 | ❌ 未定义 |
|
||||
|
||||
#### 维客线索 clues(内联 mock,7 条)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `category` | string | 线索大类(含换行符) | ⚠️ 契约说"同 TASK-2 格式" |
|
||||
| `categoryColor` | string | 颜色 | ⚠️ |
|
||||
| `text` | string | 含 emoji 的摘要 | ⚠️ |
|
||||
| `source` | string | 来源 `'系统'`/`'小燕'` | ⚠️ |
|
||||
| `detail` | string? | 详细描述 | ⚠️ |
|
||||
|
||||
#### 关联助教 coachTasks(内联 mock,4 位助教)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 助教名 | ❌ 完全未定义 |
|
||||
| `level` | string | 等级 `senior/middle/junior` | ❌ |
|
||||
| `levelColor` | string | 等级颜色 | ❌ |
|
||||
| `taskType` | string | 任务类型标签 | ❌ |
|
||||
| `taskColor` | string | 任务颜色 | ❌ |
|
||||
| `bgClass` | string | 背景样式类 | ❌ |
|
||||
| `status` | string | 状态 `normal/pinned/abandoned` | ❌ |
|
||||
| `lastService` | string | 最后服务 `'02-20 21:30 · 2.5h'` | ❌ |
|
||||
| `metrics` | Array<{label,value,color}> | 指标(近60天次数/总时长/次均时长) | ❌ |
|
||||
|
||||
#### 最亲密助教 favoriteCoaches(内联 mock,2 位)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `emoji` | string | 爱心 emoji | ❌ 完全未定义 |
|
||||
| `name` | string | 助教名 | ❌ |
|
||||
| `relationIndex` | string | 关系指数 `'9.2'` | ❌ |
|
||||
| `indexColor` | string | 指数颜色 | ❌ |
|
||||
| `bgClass` | string | 背景样式类 | ❌ |
|
||||
| `stats` | Array<{label,value,color}> | 统计(基础/激励/上课/充值) | ❌ |
|
||||
|
||||
#### 消费记录 consumptionRecords(内联 mock,3 条,3 种类型)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 记录 ID | ✅ |
|
||||
| `type` | enum | `table/shop/recharge` | ⚠️ 契约有 `type` 但枚举不同 |
|
||||
| `date` | string | 日期 | ✅ |
|
||||
| `tableName` | string? | 台桌名 | ❌ 契约有 `table` |
|
||||
| `startTime` | string? | 开始时间 | ❌ 未定义 |
|
||||
| `endTime` | string? | 结束时间 | ❌ 未定义 |
|
||||
| `duration` | string? | 时长 `'3h 20min'` | ⚠️ 契约有 `duration` 但是 number |
|
||||
| `tableFee` | number? | 台费 | ❌ 未定义 |
|
||||
| `tableOrigPrice` | number? | 台费原价 | ❌ 未定义 |
|
||||
| `coaches` | Array<{name,level,levelColor,courseType,hours,perfHours?,fee}> | 助教服务明细 | ❌ 完全未定义 |
|
||||
| `foodAmount` | number? | 酒水金额 | ❌ 未定义 |
|
||||
| `foodOrigPrice` | number? | 酒水原价 | ❌ 未定义 |
|
||||
| `totalAmount` | number? | 总金额 | ⚠️ 契约有 `amount` |
|
||||
| `totalOrigPrice` | number? | 总原价 | ❌ 未定义 |
|
||||
| `payMethod` | string? | 支付方式 | ❌ 未定义 |
|
||||
| `rechargeAmount` | number? | 充值金额 | ❌ 未定义 |
|
||||
|
||||
#### 备注 sortedNotes(内联 mock,3 条)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ❌ 契约无备注字段 |
|
||||
| `tagLabel` | string | 标签 | ❌ |
|
||||
| `createdAt` | string | 创建时间 | ❌ |
|
||||
| `content` | string | 内容 | ❌ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 查看服务记录 | `/pages/customer-service-records/customer-service-records` | 无参数(⚠️ 未传 customerId) |
|
||||
| 问问助手 | `/pages/chat/chat` | 无参数(⚠️ 未传 customerId) |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-23**:`balance`(余额)、`consumption60d`(近60天消费)、`idealInterval`(理想到店间隔)、`daysSinceVisit`(距上次到店天数)在契约 CUST-1 中完全未定义。这些是客户详情页 Banner 的核心展示字段。
|
||||
- **GAP-24**:`aiInsight`(AI 洞察:summary + strategies)在契约 CUST-1 中完全未定义。数据来源应为 `biz.ai_cache`,但 `cache_type` 未明确(可能是 `app4_analysis` 或新增类型)。
|
||||
- **GAP-25**:`coachTasks`(关联助教任务列表)在契约 CUST-1 中完全未定义。这是一个复杂的数据结构,需要从 `biz.coach_tasks` + `fdw_etl.v_dwd_assistant_service_log` 聚合。每位助教需返回:任务类型、状态、最后服务时间、近60天服务次数/总时长/次均时长。
|
||||
- **GAP-26**:`favoriteCoaches`(最亲密助教)在契约 CUST-1 中完全未定义。需要从 `fdw_etl.v_dws_member_assistant_relation_index` 获取关系指数,并聚合基础课时/激励课时/上课次数/充值金额。
|
||||
- **GAP-27**:消费记录的 `coaches` 子数组(助教服务明细,含 `perfHours` 折算工时)在契约中完全未定义。这需要从 `fdw_etl.v_dwd_assistant_service_log` 按结算单关联查询。
|
||||
- **GAP-28**:消费记录的 `tableFee`/`tableOrigPrice`/`foodAmount`/`foodOrigPrice`/`totalOrigPrice`/`payMethod` 等拆分字段在契约中未定义。契约仅有 `amount`(总金额)。
|
||||
- **GAP-29**:消费记录的 `type` 枚举不一致 — 前端使用 `table/shop/recharge`(3 种),契约使用 `type_class`(未明确枚举)。
|
||||
- **GAP-30**:备注列表在契约 CUST-1 中未定义。前端需要展示客户相关的备注。
|
||||
- **GAP-31**:跳转 customer-service-records 和 chat 时未传 `customerId` 参数,联调时目标页面无法获取客户 ID。
|
||||
|
||||
---
|
||||
|
||||
## 场景 7:客户服务记录页(customer-service-records.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCustomerRecords({ customerId, year?, month?, table? })` → `GET /api/xcx/customers/{customerId}/records`
|
||||
- 参数来源:`options.customerId` 或 `options.id`
|
||||
- 当前实现:首次加载拉取全部记录,月份切换仅本地过滤
|
||||
|
||||
### 前端期望字段 — ServiceRecord
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-2 |
|
||||
|------|------|------|------------|
|
||||
| `table` | string | 台桌号 | ✅ |
|
||||
| `type` | string | 课程类型标签 | ✅ |
|
||||
| `typeClass` | enum | `basic/vip/tip/recharge` | ✅ `type_class` |
|
||||
| `recordType` | enum | `course/recharge` | ❌ 未定义 |
|
||||
| `duration` | number | 折算后小时 | ✅ |
|
||||
| `durationRaw` | number | 折算前小时 | ✅ `duration_raw` |
|
||||
| `income` | number | 到手金额 | ✅ |
|
||||
| `isEstimate` | boolean | 是否预估 | ❌ 未定义 |
|
||||
| `drinks` | string | 饮品描述 | ✅ |
|
||||
| `date` | string | 显示日期 | ✅ |
|
||||
|
||||
### 页面头部字段
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-2 |
|
||||
|------|------|------|------------|
|
||||
| `customerName` | string | 客户名 | ✅ |
|
||||
| `customerPhone` | string | 脱敏手机号 | ✅ `customer_phone` |
|
||||
| `customerPhoneFull` | string | 完整手机号 | ❌ 未定义(需从 CUST-1 获取) |
|
||||
| `relationIndex` | string | 关系指数 | ✅ |
|
||||
| `totalServiceCount` | number | 累计服务次数 | ❌ 未定义 |
|
||||
| `monthCount` | string | 当月次数 | ❌ 需前端计算 |
|
||||
| `monthHours` | string | 当月工时 | ❌ 需前端计算 |
|
||||
| `monthRelation` | string | 当月关系指数 | ❌ 未定义 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-32**:`recordType`(`course/recharge`)和 `isEstimate`(是否预估)在契约 CUST-2 中未定义。
|
||||
- **GAP-33**:`customerPhoneFull`(完整手机号)在 CUST-2 响应中未定义,前端需要点击查看完整号码。
|
||||
- **GAP-34**:`totalServiceCount`(累计服务次数)在 CUST-2 中未定义。
|
||||
- **GAP-35**:月份切换采用本地筛选(F10),联调后数据量大时需改为按月请求 API。
|
||||
|
||||
---
|
||||
|
||||
## 场景 8:备注列表页(notes.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchNotes({ page?, pageSize? })` → `GET /api/xcx/notes?page=&pageSize=`
|
||||
- `deleteNote(noteId)` → `DELETE /api/xcx/notes/{noteId}`
|
||||
|
||||
### 前端期望字段 — Note
|
||||
| 字段 | 类型 | 说明 | 契约 NOTE-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ✅ |
|
||||
| `content` | string | 备注内容 | ✅ |
|
||||
| `tagType` | enum | `customer/coach/system` | ✅ `tag_type` |
|
||||
| `tagLabel` | string | 标签文案 | ✅ `tag_label` |
|
||||
| `createdAt` | string | 创建时间 | ✅ `created_at` |
|
||||
| `score` | number? | 满意度评分 | ✅ |
|
||||
| `timeLabel` | string | 格式化时间(前端计算) | — |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 成功后行为 |
|
||||
|------|-----|-----------|
|
||||
| 删除备注 | `DELETE /notes/{noteId}` | 本地 filter 移除 + Toast |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-36**:无触底加载逻辑(F11),API 支持分页但 `onReachBottom()` 未实现。
|
||||
- **GAP-37**:`tagType` 枚举 — mock 数据中有 `'system'` 类型,但契约 NOTE-1 仅定义 `customer/coach/system`。需确认 `system` 类型的备注由谁创建、如何展示。
|
||||
|
||||
---
|
||||
|
||||
## 场景 9:助教详情页(coach-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCoachDetail(coachId)` → `GET /api/xcx/coaches/{coachId}`
|
||||
- 参数来源:`options.id`
|
||||
- 返回类型:`CoachCard`(api.ts 定义),但前端实际需要远超 `CoachCard` 的数据
|
||||
|
||||
### 前端期望字段 — CoachDetail(完整清单)
|
||||
|
||||
#### 基本信息
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `id` | string | 助教 ID | ✅ |
|
||||
| `name` | string | 助教名 | ✅ |
|
||||
| `avatar` | string | 头像 | ✅ |
|
||||
| `level` | string | 等级 `'星级'` | ✅ |
|
||||
| `skills` | string[] | 技能 | ✅ |
|
||||
| `workYears` | number | 工龄 3 | ❌ 未定义 |
|
||||
| `customerCount` | number | 客户数 68 | ❌ 未定义 |
|
||||
| `hireDate` | string | 入职日期 | ❌ 未定义 |
|
||||
| `store_name` | string | 门店名 | ✅ |
|
||||
|
||||
#### 绩效指标 performance(6 个指标)
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `monthlyHours` | number | 本月工时 87.5 | ❌ 未定义 |
|
||||
| `monthlySalary` | number | 本月工资 6950 | ❌ 未定义 |
|
||||
| `customerBalance` | number | 客源储值余额 86200 | ❌ 未定义 |
|
||||
| `tasksCompleted` | number | 任务完成数 38 | ❌ 未定义 |
|
||||
| `perfCurrent` | number | 当前绩效 80 | ❌ 未定义 |
|
||||
| `perfTarget` | number | 目标绩效 100 | ❌ 未定义 |
|
||||
|
||||
#### 绩效指标卡片 perfCards(4 张)
|
||||
| 卡片 | 需要字段 | 契约 |
|
||||
|------|---------|------|
|
||||
| 本月定档业绩 | `monthlyHours` + 折算前工时 | ❌ |
|
||||
| 本月工资(预估) | `monthlySalary` | ❌ |
|
||||
| 客源储值余额 | `customerBalance` + `customerCount` | ❌ |
|
||||
| 本月任务完成 | `tasksCompleted` + 覆盖客户数 | ❌ |
|
||||
|
||||
#### 进度条数据
|
||||
| 字段 | 类型 | 说明 | 契约 |
|
||||
|------|------|------|------|
|
||||
| `tierNodes` | number[] | 档位节点 `[0,100,130,160,190,220]` | ❌ 未定义 |
|
||||
|
||||
#### 收入明细 income(本月/上月各 4 项)
|
||||
| 项目 | 说明 | 契约 |
|
||||
|------|------|------|
|
||||
| 基础课时费 | `¥3,500` | ❌ 未定义 |
|
||||
| 激励课时费 | `¥1,800` | ❌ 未定义 |
|
||||
| 充值提成 | `¥1,200` | ❌ 未定义 |
|
||||
| 酒水提成 | `¥450` | ❌ 未定义 |
|
||||
|
||||
#### 任务执行
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `visibleTasks` | TaskItem[] | 可见任务(active) | ⚠️ 契约说"同 TASK-1 item 格式"但实际需要更多字段 |
|
||||
| `hiddenTasks` | TaskItem[] | 隐藏任务(inactive) | ⚠️ |
|
||||
| `abandonedTasks` | AbandonedTask[] | 已放弃任务 | ⚠️ |
|
||||
|
||||
TaskItem 需要的字段(超出 TASK-1 item):
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `typeLabel` | string | 任务类型标签 |
|
||||
| `typeClass` | string | 样式类 `high-priority/priority/relationship/callback` |
|
||||
| `customerName` | string | 客户名 |
|
||||
| `noteCount` | number | 备注数量 |
|
||||
| `pinned` | boolean | 是否置顶 |
|
||||
| `notes` | Array<{pinned?,text,date}> | 备注列表(含置顶标记) |
|
||||
|
||||
AbandonedTask 字段:
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `customerName` | string | 客户名 |
|
||||
| `reason` | string | 放弃原因 |
|
||||
|
||||
#### 客户关系 TOP20 — TopCustomer
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `id` | string | 客户 ID | ✅ |
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `initial` | string | 姓名首字 | ❌ |
|
||||
| `avatarGradient` | string | 头像渐变色 | ❌ |
|
||||
| `heartEmoji` | string | 爱心 emoji `❤️/💛/🤍` | ❌ |
|
||||
| `score` | string | 关系指数 `'9.5'` | ❌ 契约有 `total_spend`/`visit_count` 但无 score |
|
||||
| `scoreColor` | string | 指数颜色 | ❌ |
|
||||
| `serviceCount` | number | 服务次数 | ⚠️ 契约有 `visit_count` |
|
||||
| `balance` | string | 余额 `'¥8,600'` | ❌ 未定义 |
|
||||
| `consume` | string | 消费 `'¥12,800'` | ⚠️ 契约有 `total_spend` |
|
||||
|
||||
#### 近期服务明细 — ServiceRecord
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `customerId` | string? | 客户 ID | ❌ |
|
||||
| `customerName` | string | 客户名 | ⚠️ |
|
||||
| `initial` | string | 姓名首字 | ❌ |
|
||||
| `avatarGradient` | string | 头像渐变色 | ❌ |
|
||||
| `type` | string | 课程类型 | ⚠️ |
|
||||
| `typeClass` | string | 样式类 | ⚠️ |
|
||||
| `table` | string | 台号 | ⚠️ |
|
||||
| `duration` | string | 时长 `'2.5h'` | ⚠️ |
|
||||
| `income` | string | 收入 `'¥200'` | ⚠️ |
|
||||
| `date` | string | 日期时间 | ⚠️ |
|
||||
| `perfHours` | string? | 折算工时 `'2h'` | ❌ 未定义 |
|
||||
|
||||
#### 历史月份统计 — HistoryMonth
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `month` | string | 月份标签 `'本月'`/`'上月'`/`'4月'` | ❌ 完全未定义 |
|
||||
| `estimated` | boolean | 是否预估 | ❌ |
|
||||
| `customers` | string | 客户数 `'22人'` | ❌ |
|
||||
| `hours` | string | 工时 `'87.5h'` | ❌ |
|
||||
| `salary` | string | 工资 `'¥6,950'` | ❌ |
|
||||
| `callbackDone` | number | 回访完成数 | ❌ |
|
||||
| `recallDone` | number | 召回完成数 | ❌ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击任务项 | `/pages/customer-detail/customer-detail` | `?name={customerName}` |
|
||||
| 点击客户卡片 | `/pages/customer-detail/customer-detail` | `?id={customerId}` |
|
||||
| 点击服务记录 | `/pages/customer-detail/customer-detail` | `?id={customerId}` |
|
||||
| 查看更多服务记录 | `/pages/performance-records/performance-records` | `?coachId={coachId}` |
|
||||
| 问问助手 | `/pages/chat/chat` | `?coachId={coachId}` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-38**:契约 COACH-1 严重不完整。前端需要 `performance`(6 个绩效指标)、`income`(本月/上月各 4 项收入明细)、`tierNodes`(档位节点数组),契约均未定义。
|
||||
- **GAP-39**:`TopCustomer` 需要 `heartEmoji`、`score`(关系指数)、`scoreColor`、`balance`(余额)、`consume`(消费),契约仅有 `total_spend`/`visit_count`。
|
||||
- **GAP-40**:`ServiceRecord.perfHours`(折算工时)在契约中未定义。
|
||||
- **GAP-41**:`HistoryMonth`(历史月份统计)在契约中完全未定义。需要返回最近 N 个月的汇总数据(客户数、工时、工资、回访/召回完成数)。
|
||||
- **GAP-42**:`TaskItem.notes`(任务关联的备注列表,含置顶标记)在契约中未定义。契约的 `visible_tasks` 说"同 TASK-1 item 格式",但前端需要额外的 `noteCount`、`pinned`、`notes[]` 字段。
|
||||
- **GAP-43**:`AbandonedTask.reason`(放弃原因)在契约中未定义。
|
||||
- **GAP-44**:`workYears`(工龄)、`hireDate`(入职日期)在契约中未定义。
|
||||
|
||||
---
|
||||
|
||||
## 场景 10:对话页面(chat.ts + chat-history.ts)
|
||||
|
||||
### chat.ts — API 调用
|
||||
- `fetchChatMessages(customerId)` → `GET /api/xcx/chat/{chatId}/messages`
|
||||
- ⚠️ 注意:前端传入的是 `customerId`(或 `'default'`),但 API 路径参数是 `chatId`。参数语义不匹配。
|
||||
- `sendChatMessage(chatId, content)` → `POST /api/xcx/chat/{chatId}/messages`
|
||||
- 页面参数:`options.customerId`
|
||||
|
||||
### chat.ts — 前端期望字段 — ChatMessage
|
||||
| 字段 | 类型 | 说明 | 契约 CHAT-2 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 消息 ID | ✅ |
|
||||
| `role` | enum | `user/assistant` | ✅ |
|
||||
| `content` | string | 消息内容 | ✅ |
|
||||
| `timestamp` | string | 时间戳 | ⚠️ 契约用 `created_at` |
|
||||
| `referenceCard` | object? | 引用卡片 | ❌ 完全未定义 |
|
||||
| `referenceCard.type` | enum | `customer/record` | ❌ |
|
||||
| `referenceCard.title` | string | 卡片标题 | ❌ |
|
||||
| `referenceCard.summary` | string | 卡片摘要 | ❌ |
|
||||
| `referenceCard.data` | Record<string,string> | 键值对数据 | ❌ |
|
||||
|
||||
### chat.ts — SSE 流式回复
|
||||
- 前端 `simulateStreamOutput()` 模拟流式输出(逐字显示)
|
||||
- 联调时需替换为真实 SSE 连接(`POST /api/xcx/chat/stream`)
|
||||
- spec R3 已确认保留 SSE 流式端点
|
||||
|
||||
### chat.ts — 页面参数
|
||||
| 来源页面 | 传递参数 | 用途 |
|
||||
|---------|---------|------|
|
||||
| task-detail | `?customerId={taskId}` | ⚠️ 实际传的是 taskId |
|
||||
| customer-detail | 无参数 | ⚠️ 未传 customerId |
|
||||
| coach-detail | `?coachId={coachId}` | 助教 ID |
|
||||
| chat-history | `?historyId={id}` | 历史对话 ID |
|
||||
|
||||
### chat-history.ts — API 调用
|
||||
- `fetchChatHistory({ page?, pageSize? })` → `GET /api/xcx/chat/history`
|
||||
- 返回:`{ items: ChatHistoryItem[], total, hasMore }`
|
||||
|
||||
### chat-history.ts — 前端期望字段 — ChatHistoryItem
|
||||
| 字段 | 类型 | 说明 | 契约 CHAT-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 对话 ID | ✅ |
|
||||
| `title` | string | 对话标题 | ❌ 契约无 `title` |
|
||||
| `lastMessage` | string | 最后消息 | ✅ `last_message` |
|
||||
| `timestamp` | string | 时间戳 | ⚠️ 契约用 `last_time` |
|
||||
| `customerName` | string? | 关联客户名 | ✅ |
|
||||
|
||||
### chat-history.ts — 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击对话记录 | `/pages/chat/chat` | `?historyId={id}` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-45**:`ChatMessage.referenceCard`(引用卡片)在契约 CHAT-2 中完全未定义。前端需要展示客户概览卡片(含键值对数据),后端需在消息中附带结构化引用数据。
|
||||
- **GAP-46**:`ChatMessage.timestamp` vs 契约 `created_at` — 字段名不一致,前端使用 `timestamp`,契约定义 `created_at`。
|
||||
- **GAP-47**:`ChatHistoryItem.title`(对话标题)在契约 CHAT-1 中未定义。契约仅有 `customer_name`/`last_message`/`last_time`/`unread_count`。
|
||||
- **GAP-48**:`ChatHistoryItem.timestamp` vs 契约 `last_time` — 字段名不一致。
|
||||
- **GAP-49**:chat 页面接收 `customerId` 参数但 API 路径需要 `chatId`。前端需要一个"根据 customerId 查找或创建对话"的机制,或 API 支持 `customerId` 作为查询参数。
|
||||
- **GAP-50**:chat 页面还接收 `historyId`(从 chat-history 跳转)和 `coachId`(从 coach-detail 跳转),但 `loadMessages()` 仅使用 `customerId`。多种入口参数的路由逻辑未实现。
|
||||
- **GAP-51**:SSE 流式端点 `POST /api/xcx/chat/stream` 在契约中未定义(spec R3 已确认需补充)。
|
||||
|
||||
---
|
||||
|
||||
## 汇总:Gap 清单
|
||||
|
||||
### 一、架构级 Gap(影响多个页面)
|
||||
|
||||
| # | Gap | 严重度 | 影响页面 | 说明 |
|
||||
|---|-----|--------|---------|------|
|
||||
| GAP-01 | globalData.authUser 缺少 role/store_name/coach_level/avatar | 🟠 中 | task-list, performance, performance-records | 多个页面 Banner 需要这些字段,当前硬编码 |
|
||||
| GAP-04 | pin/unpin API 端点未在契约中定义 | 🔴 高 | task-list | api.ts 无对应函数,前端仅本地更新 |
|
||||
| GAP-05 | createNote 缺少 score 参数 | 🟠 中 | task-list, task-detail | 前端传 score 但 api.ts 签名无此字段 |
|
||||
| GAP-09/10 | task-detail 跳转 chat/customer-service-records 时传 taskId 而非 customerId | 🔴 高 | task-detail → chat/customer-service-records | TASK-2 响应需包含 customer_id |
|
||||
| GAP-31 | customer-detail 跳转时未传 customerId | 🔴 高 | customer-detail → chat/customer-service-records | 联调时目标页面无法获取客户 ID |
|
||||
| GAP-17 | performance 跳转 task-detail 传 customerName 而非 taskId | 🟠 中 | performance → task-detail | 参数不匹配 |
|
||||
|
||||
### 二、TASK-1 扩展 Gap(任务列表绩效卡片)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-02 | performance 字段严重不足(需 15+ 字段,契约仅 4 个) | 🔴 高 | tierNodes、basicHours、bonusHours、currentTier、nextTierHours、tierCompleted、bonusMoney、incomeTrend、incomeTrendDir、prevMonth 等 |
|
||||
| GAP-03 | enrichTask 需要 lastVisitDays/balance/aiSuggestion | 🟠 中 | 任务卡片扩展字段 |
|
||||
|
||||
### 三、PERF-1 Gap(绩效概览)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-12 | this_month_records 需要 DateGroup 分组结构 | 🔴 高 | 契约是扁平数组,前端需按日期分组 + timeRange/courseType/location |
|
||||
| GAP-13 | 收入档位数据完全未定义 | 🔴 高 | currentTier/nextTier/upgradeHoursNeeded/upgradeBonus |
|
||||
| GAP-14 | lastMonthIncome 未定义 | 🟠 中 | Banner 需展示上月收入 |
|
||||
| GAP-15 | incomeItems.desc 未定义 | 🟠 中 | 需费率×工时的拆分描述 |
|
||||
| GAP-16 | newCustomers/regularCustomers 缺少多个字段 | 🟠 中 | lastService/count/hours/income |
|
||||
| GAP-18 | 页面无月份切换功能 | 🟡 低 | F8 前端修复项 |
|
||||
|
||||
### 四、CUST-1 Gap(客户详情 — 最大 Gap 集中区)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-23 | balance/consumption60d/idealInterval/daysSinceVisit 未定义 | 🔴 高 | Banner 核心展示字段 |
|
||||
| GAP-24 | aiInsight (summary + strategies) 未定义 | 🔴 高 | AI 洞察模块 |
|
||||
| GAP-25 | coachTasks(关联助教任务列表)未定义 | 🔴 高 | 复杂数据结构,需多表聚合 |
|
||||
| GAP-26 | favoriteCoaches(最亲密助教)未定义 | 🔴 高 | 需关系指数 + 服务统计 |
|
||||
| GAP-27 | 消费记录 coaches 子数组(助教服务明细)未定义 | 🔴 高 | 含 perfHours 折算工时 |
|
||||
| GAP-28 | 消费记录拆分字段(tableFee/foodAmount/totalOrigPrice 等)未定义 | 🟠 中 | 契约仅有 amount |
|
||||
| GAP-29 | 消费记录 type 枚举不一致 | 🟠 中 | 前端 table/shop/recharge vs 契约未明确 |
|
||||
| GAP-30 | 备注列表未定义 | 🟠 中 | 客户详情需展示备注 |
|
||||
|
||||
### 五、COACH-1 Gap(助教详情 — 第二大 Gap 集中区)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-38 | performance/income/tierNodes 未定义 | 🔴 高 | 6 个绩效指标 + 本月/上月各 4 项收入 + 档位节点 |
|
||||
| GAP-39 | TopCustomer 缺少 heartEmoji/score/scoreColor/balance | 🔴 高 | 客户关系 TOP20 核心字段 |
|
||||
| GAP-40 | ServiceRecord.perfHours 未定义 | 🟠 中 | 折算工时 |
|
||||
| GAP-41 | HistoryMonth 完全未定义 | 🔴 高 | 历史月份统计(5+ 个月) |
|
||||
| GAP-42 | TaskItem.notes(含置顶标记)未定义 | 🟠 中 | 任务关联备注 |
|
||||
| GAP-43 | AbandonedTask.reason 未定义 | 🟡 低 | 放弃原因 |
|
||||
| GAP-44 | workYears/hireDate 未定义 | 🟡 低 | 助教基本信息 |
|
||||
|
||||
### 六、CHAT 模块 Gap
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-45 | ChatMessage.referenceCard 未定义 | 🔴 高 | 引用卡片(客户概览数据) |
|
||||
| GAP-46 | timestamp vs created_at 字段名不一致 | 🟠 中 | 前端用 timestamp,契约用 created_at |
|
||||
| GAP-47 | ChatHistoryItem.title 未定义 | 🟠 中 | 对话标题 |
|
||||
| GAP-48 | ChatHistoryItem.timestamp vs last_time 不一致 | 🟠 中 | 字段名不一致 |
|
||||
| GAP-49 | customerId → chatId 映射机制缺失 | 🔴 高 | 前端传 customerId 但 API 需要 chatId |
|
||||
| GAP-50 | 多入口参数路由逻辑未实现 | 🟠 中 | historyId/coachId/customerId 三种入口 |
|
||||
| GAP-51 | SSE 流式端点未在契约中定义 | 🟠 中 | spec 已确认需补充 |
|
||||
|
||||
### 七、其他 Gap
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-06 | 维客线索 tag 格式不一致(含换行符) | 🟡 低 | 需统一 |
|
||||
| GAP-07 | 维客线索 source 格式不一致 | 🟡 低 | mock 用 `'By:系统'`,类型定义用 `'manual'` |
|
||||
| GAP-08 | aiAnalysis/talkingPoints 的 ai_cache cache_type 未明确 | 🟠 中 | 需明确映射 |
|
||||
| GAP-11 | storageLevel/relationLevel 计算逻辑未明确 | 🟡 低 | 前端本地计算 |
|
||||
| GAP-19 | avatarChar/avatarColor 是否后端返回 | 🟡 低 | 建议前端自行计算 |
|
||||
| GAP-20 | courseTypeClass 枚举值前缀不统一 | 🟡 低 | `tag-basic` vs `basic` |
|
||||
| GAP-21 | 月份切换未重置分页 | 🟡 低 | F9 前端 bug |
|
||||
| GAP-22 | performance-records Banner 字段来源 | 🟡 低 | 依赖 GAP-01 |
|
||||
| GAP-32 | recordType/isEstimate 未定义 | 🟡 低 | CUST-2 扩展字段 |
|
||||
| GAP-33 | customerPhoneFull 未定义 | 🟡 低 | CUST-2 需完整手机号 |
|
||||
| GAP-34 | totalServiceCount 未定义 | 🟡 低 | CUST-2 累计次数 |
|
||||
| GAP-35 | 月份切换本地筛选需改 API 请求 | 🟡 低 | F10 前端修复 |
|
||||
| GAP-36 | notes 页无触底加载 | 🟡 低 | F11 前端修复 |
|
||||
| GAP-37 | Note.tagType 'system' 类型语义 | 🟡 低 | 需确认 |
|
||||
|
||||
---
|
||||
|
||||
### 统计
|
||||
|
||||
| 严重度 | 数量 | 说明 |
|
||||
|--------|------|------|
|
||||
| 🔴 高 | 17 | 阻塞联调,必须在后端开发前解决 |
|
||||
| 🟠 中 | 19 | 影响功能完整性,需在联调前补充 |
|
||||
| 🟡 低 | 15 | 可联调后修复或前端自行处理 |
|
||||
| **合计** | **51** | |
|
||||
|
||||
### 最高优先级修复建议
|
||||
|
||||
1. **契约大幅扩展 CUST-1**:客户详情是 Gap 最集中的页面(GAP-23~30),需新增 `balance`、`consumption60d`、`idealInterval`、`daysSinceVisit`、`aiInsight`、`coachTasks`、`favoriteCoaches`、消费记录拆分字段、备注列表。
|
||||
2. **契约大幅扩展 COACH-1**:助教详情是第二大 Gap 集中区(GAP-38~44),需新增 `performance`(6 指标)、`income`(8 项)、`tierNodes`、`TopCustomer` 扩展字段、`HistoryMonth`、`ServiceRecord.perfHours`。
|
||||
3. **契约扩展 PERF-1**:绩效概览需要 DateGroup 分组结构(GAP-12)和收入档位数据(GAP-13)。
|
||||
4. **契约扩展 TASK-1 performance**:绩效卡片需要 15+ 字段(GAP-02)。
|
||||
5. **修复 customerId/taskId 混淆**:TASK-2 响应需包含 `customer_id`(GAP-09/10),customer-detail 跳转需传参(GAP-31)。
|
||||
6. **补充 CHAT 模块**:referenceCard(GAP-45)、customerId→chatId 映射(GAP-49)、SSE 端点定义(GAP-51)。
|
||||
Reference in New Issue
Block a user