Files
Neo-ZQYY/docs/miniprogram-dev/api-audit/task-detail.md

286 lines
14 KiB
Markdown
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.
# task-detail 页面数据来源排查
> 排查日期2026-03-18
> 页面路径pages/task-detail/task-detail
## 概览
| 分类 | 数量 | 说明 |
|------|------|------|
| Mock 数据 | 12 个字段 | 来自 `mock-data.ts``mockTaskDetails` + 页面内联 mock 备注 |
| 硬编码数据 | 18 个字段/数组 | 维客线索、话术参考、服务记录、服务汇总、手机号、储值等级等 |
| 已对接 API | 0 个接口 | 当前无任何真实 API 调用 |
| 前端计算/派生 | 7 个字段 | 关系等级、Banner 背景、AI 配色、时间标签等 |
| 路由参数 | 1 个 | `id`(任务 ID |
| WXML 硬编码文案 | 6 处 | 建议文案、section 标题等 |
---
## 一、Mock 数据
### 1.1 来自 mock-data.ts`mockTaskDetails`
页面通过 `import { mockTaskDetails } from '../../utils/mock-data'` 引入,在 `loadData()` 中使用 `setTimeout` 模拟异步加载。
| 字段 | 类型 | 来源路径 | 联调替换 API |
|------|------|----------|-------------|
| `detail`(整体) | `TaskDetail` | `mockTaskDetails.find(t => t.id === id) \|\| mockTaskDetails[0]` | `GET /api/tasks/{id}` |
| `detail.id` | `string` | TaskDetail.id | 同上 |
| `detail.customerName` | `string` | TaskDetail.customerName | 同上 |
| `detail.customerAvatar` | `string` | TaskDetail.customerAvatar | 同上 |
| `detail.taskType` | `TaskType` | TaskDetail.taskType`'callback' \| 'priority_recall' \| 'relationship' \| 'high_priority'` | 同上 |
| `detail.taskTypeLabel` | `string` | TaskDetail.taskTypeLabel | 同上 |
| `detail.heartScore` | `number` | TaskDetail.heartScore | 同上 |
| `detail.status` | `'pending' \| 'completed' \| 'abandoned'` | TaskDetail.status | 同上 |
| `detail.aiAnalysis.summary` | `string` | TaskDetail.aiAnalysis.summary | `GET /api/tasks/{id}/ai-analysis` |
| `detail.aiAnalysis.suggestions` | `string[]` | TaskDetail.aiAnalysis.suggestions | 同上 |
| `detail.hobbies` | `string[]` | TaskDetail.hobbies | `GET /api/tasks/{id}` |
| `detail.deadline` | `string` | TaskDetail.deadline | 同上 |
**mock-data.ts 中 TaskDetail 扩展字段(已定义但页面未直接渲染):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `lastVisitDate` | `string?` | 最后到店日期,页面未使用 |
| `lastSpendAmount` | `number?` | 最后消费金额,页面未使用 |
| `callbackReason` | `string?` | 回访原因,页面未使用 |
| `daysAbsent` | `number?` | 缺席天数,页面未使用 |
| `spendTrend` | `'up' \| 'down' \| 'flat'?` | 消费趋势,页面未使用 |
| `churnRisk` | `'high' \| 'medium' \| 'low'?` | 流失风险,页面未使用 |
| `preferences` | `string[]?` | 偏好,页面未使用 |
| `consumptionHabits` | `string?` | 消费习惯,页面未使用 |
| `socialPreference` | `string?` | 社交偏好,页面未使用 |
### 1.2 页面内联 Mock 备注
`loadData()` 方法内部硬编码了 5 条 mock 备注(`mockNotes` 局部变量L107-L113覆盖了 `mockTaskDetails` 中的 `notes` 字段:
| 字段 | 当前值 | 联调替换 API |
|------|--------|-------------|
| `mockNotes[0]` | `'已通过微信联系王先生...'`score=10 | `GET /api/tasks/{id}/notes` |
| `mockNotes[1]` | `'王先生最近出差较多...'`score=7.5 | 同上 |
| `mockNotes[2]` | `'上次到店时推荐了会员续费活动...'`score=6 | 同上 |
| `mockNotes[3]` | `'客户对今天的服务非常满意...'`score=9.5 | 同上 |
| `mockNotes[4]` | `'完成高优先召回任务...'`score=8 | 同上 |
---
## 二、硬编码数据
### 2.1 维客线索(`retentionClues`
**风险:高** — 8 条完整的维客线索数据直接写死在 `data` 对象中L68-L77应从 API 获取。
| 索引 | tag | text摘要 | source | 所在行 | 应改为 |
|------|-----|-------------|--------|--------|--------|
| 0 | 客户基础 | `'生日 3月15日 · VIP会员 · 注册2年'` | By:系统 | L69 | `GET /api/tasks/{id}/retention-clues` |
| 1 | 消费习惯 | `'常来夜场 · 月均4-5次'` | By:系统 | L70 | 同上 |
| 2 | 消费习惯 | `'高客单价'`(含 desc | By:系统 | L71 | 同上 |
| 3 | 玩法偏好 | `'偏爱中式八球 · 斯诺克进阶中...'`(含 desc | By:系统 | L72 | 同上 |
| 4 | 重要反馈 | `'上次提到想练斯诺克走位'`(含 desc | By:小燕 | L73 | 同上 |
| 5 | 社交偏好 | `'喜欢带朋友来玩 · 社交型客户'`(含 desc | By:系统 | L74 | 同上 |
| 6 | 消费习惯 | `'酒水消费占比高 · 偏好高端酒水'`(含 desc | By:系统 | L75 | 同上 |
| 7 | 重要反馈 | `'上次提到想办生日派对'`(含 desc | By:Lucy | L76 | 同上 |
> 注意:`mock-data.ts` 中已定义 `mockRetentionClues` 和 `RetentionClue` 接口,但页面未使用,而是自行硬编码了不同结构的数据。联调时应统一数据结构。
### 2.2 话术参考(`talkingPoints`
**风险:高** — 5 条话术直接写死在 `data` 对象中L80-L86应由 AI 生成或从 API 获取。
| 索引 | 内容摘要 | 所在行 | 应改为 |
|------|---------|--------|--------|
| 0 | `'王哥您好,好久不见!最近店里新到了...'` | L81 | `GET /api/tasks/{id}/talking-points` 或 AI 生成接口 |
| 1 | `'王哥,最近忙吗?这周末我们有个...'` | L82 | 同上 |
| 2 | `'王哥好呀,上次您提到想练练...'` | L83 | 同上 |
| 3 | `'王哥,好久没见您了,您的老位置...'` | L84 | 同上 |
| 4 | `'王哥您好,我们这个月推出了...'` | L85 | 同上 |
### 2.3 近期服务记录(`serviceRecords`
**风险:高** — 4 条服务记录直接写死在 `data` 对象中L91-L96应从 API 获取。
| 索引 | table | type | income | date | 所在行 | 应改为 |
|------|-------|------|--------|------|--------|--------|
| 0 | A12号台 | 基础课 | ¥200 | 2月7日 21:30 | L92 | `GET /api/tasks/{id}/service-records` |
| 1 | 3号台 | 基础课 | ¥160 | 2月1日 20:30 | L93 | 同上 |
| 2 | VIP1号房 | 包厢课 | ¥150 | 1月28日 19:00 | L94 | 同上 |
| 3 | (空) | 充值 | ¥80 | 1月15日 10:00 | L95 | 同上 |
### 2.4 服务汇总(`serviceSummary`
**风险:高** — 汇总数据直接写死L90应由后端计算或前端根据服务记录列表聚合。
| 字段 | 当前值 | 所在行 | 应改为 |
|------|--------|--------|--------|
| `totalHours` | `6.0` | L90 | 后端返回或前端聚合 `serviceRecords` |
| `totalIncome` | `510` | L90 | 同上 |
| `avgIncome` | `170` | L90 | 同上 |
### 2.5 手机号
**风险:高** — 手机号硬编码在两处。
| 位置 | 当前值 | 所在行 | 应改为 |
|------|--------|--------|--------|
| WXML 中 `phone` 显示 | `'138****5678'`(脱敏)/ `'13812345678'`(明文) | wxml L38 | 从 `detail` 对象获取API 返回脱敏版 |
| `onCopyPhone()` 方法 | `'13812345678'` | ts L175 | 调用 API 获取明文手机号 `GET /api/customers/{id}/phone` |
### 2.6 储值等级
**风险:中** — 直接写死在 `data` 中。
| 字段 | 当前值 | 所在行 | 应改为 |
|------|--------|--------|--------|
| `storageLevel` | `'非常多'` | ts L101 | 从 `detail` 或客户信息 API 获取 |
### 2.7 关系等级初始值
**风险:低** — 初始值会被 `updateRelationshipDisplay()` 覆盖,但仍属于硬编码。
| 字段 | 当前值 | 所在行 | 说明 |
|------|--------|--------|------|
| `relationLevel` | `'excellent'` | ts L104 | 初始值,`loadData` 后被覆盖 |
| `relationLevelText` | `'很好'` | ts L105 | 同上 |
| `relationColor` | `'#e91e63'` | ts L106 | 同上 |
### 2.8 任务建议文案
**风险:中** — WXML 中硬编码了建议引导文案。
| 位置 | 当前值 | 所在行 | 应改为 |
|------|--------|--------|--------|
| `suggestion-intro` | `'该客户已有 15 天未到店,存在流失风险。建议通过微信联系:'` | wxml L82 | 从 `detail.aiAnalysis` 获取或后端生成 |
---
## 三、已对接 API
**当前无任何真实 API 调用。**
`loadData()` 使用 `setTimeout` + `mockTaskDetails` 模拟异步请求L128-L155。所有数据操作放弃任务、保存备注、删除备注均为本地 `setData` 操作,未与后端通信。
涉及的伪操作:
| 操作 | 当前实现 | 需对接 API |
|------|---------|-----------|
| 加载任务详情 | `mockTaskDetails.find()` | `GET /api/tasks/{id}` |
| 放弃任务 | `setData({ 'detail.status': 'abandoned' })` | `POST /api/tasks/{id}/abandon` |
| 取消放弃 | `setData({ 'detail.status': 'pending' })` | `POST /api/tasks/{id}/cancel-abandon` |
| 保存备注 | `setData({ sortedNotes: [...] })` | `POST /api/tasks/{id}/notes` |
| 删除备注 | `filter` 本地数组 | `DELETE /api/tasks/{id}/notes/{noteId}` |
---
## 四、前端计算/派生数据
| 字段 | 计算逻辑 | 所在方法 | 依赖数据 |
|------|---------|---------|---------|
| `relationLevel` | `heartScore` 分段映射:>8.5→excellent, ≥6→good, ≥3.5→normal, <3.5→poor | `updateRelationshipDisplay()` L157 | `detail.heartScore` |
| `relationLevelText` | 同上分段映射为中文:很好/良好/一般/待发展 | 同上 | 同上 |
| `relationColor` | 同上分段映射为颜色值 | 同上 | 同上 |
| `bannerBgSvg` | 根据 `detail.taskType` 映射 SVG 路径 | `loadData()` L133-L142 | `detail.taskType` |
| `sortedNotes` | `mockNotes` → 附加 `timeLabel``formatRelativeTime`)→ `sortByTimestamp` 排序 | `loadData()` L116-L118 | mock 备注数据 |
| `aiColor` | 随机从 6 色中选取 | `onLoad()` L124 | 无(随机) |
| `copiedIndex` | 复制话术后设为当前索引2 秒后重置为 -1 | `onCopySpeech()` L189 | 用户交互 |
| `pageState` | 加载状态机loading → normal/empty/error | `loadData()` | 数据加载结果 |
| `phoneVisible` | 手机号显示/隐藏切换 | `onTogglePhone()` L173 | 用户交互 |
| `noteModalVisible` | 备注弹窗显隐 | `onAddNote()`/`onNoteConfirm()`/`onNoteCancel()` | 用户交互 |
| `abandonModalVisible` | 放弃弹窗显隐 | `onAbandon()`/`onAbandonConfirm()`/`onAbandonCancel()` | 用户交互 |
| `formatServiceDate()` 返回值 | ISO 日期 → `M月D日 HH:mm` 中文短格式 | 页面顶部函数 L55 | 服务记录日期字符串 |
---
## 五、路由参数
| 参数 | 来源 | 用途 | 所在行 |
|------|------|------|--------|
| `options.id` | `onLoad(options)` | 任务 ID用于查找 mock 数据;缺失时回退空字符串 | ts L123 |
---
## 六、WXML 中的硬编码文案
| 文案 | 位置 | 类型 | 说明 |
|------|------|------|------|
| `'该客户已有 15 天未到店,存在流失风险。建议通过微信联系:'` | wxml L82 | 业务数据 | 应从 AI 分析结果获取 |
| `'💡 建议执行'` | wxml L79 | UI 文案 | 可保留 |
| `'💬 话术参考'` | wxml L89 | UI 文案 | 可保留 |
| `'60天内服务记录'` | wxml L121 | UI 文案 | "60天"为业务规则,可保留但需确认是否动态 |
| `'未找到任务信息'` / `'加载失败'` / `'加载中...'` | wxml L7-L15 | 状态文案 | 可保留 |
| `'暂无备注'` | wxml L117 | 空态文案 | 可保留 |
---
## 七、引用的工具函数
| 函数 | 来源文件 | 用途 |
|------|---------|------|
| `mockTaskDetails` | `utils/mock-data.ts` | Mock 任务详情数据 |
| `TaskDetail`, `Note` | `utils/mock-data.ts` | 类型定义 |
| `sortByTimestamp` | `utils/sort.ts` | 备注按时间降序排序 |
| `formatRelativeTime` | `utils/time.ts` | 备注时间 → 相对时间文案 |
| `formatMoney` | `utils/money.ts` | 金额格式化(已 import 但页面 TS 中未直接调用WXML 使用 WXS 版) |
| `fmt.toFixed` | `utils/format.wxs` | WXML 中数字保留小数 |
| `fmt.money` | `utils/format.wxs` | WXML 中金额格式化 |
| `fmt.hours` | `utils/format.wxs` | WXML 中课时格式化 |
---
## 八、引用的自定义组件
| 组件 | 路径 | 用途 |
|------|------|------|
| `note-modal` | `/components/note-modal/note-modal` | 备注弹窗 |
| `abandon-modal` | `/components/abandon-modal/abandon-modal` | 放弃任务弹窗 |
| `star-rating` | `/components/star-rating/star-rating` | 星级评分展示 |
| `heart-icon` | `/components/heart-icon/heart-icon` | 爱心图标(关系等级) |
| `ai-inline-icon` | `/components/ai-inline-icon/ai-inline-icon` | AI 内联图标 |
| `ai-title-badge` | `/components/ai-title-badge/ai-title-badge` | AI 标题徽章 |
| `clue-card` | `/components/clue-card/clue-card` | 维客线索卡片 |
| `service-record-card` | `/components/service-record-card/service-record-card` | 服务记录卡片 |
| `dev-fab` | `/components/dev-fab/dev-fab` | 开发调试浮动按钮JSON 注册但 WXML 未使用) |
---
## 九、调试面板数据
页面包含一个调试面板(`showDebugPanel`),用于开发阶段切换任务类型和关系数值。联调前应移除或隐藏。
| 字段 | 用途 | 所在行 |
|------|------|--------|
| `showDebugPanel` | 调试面板显隐 | ts L110 |
| `debugTaskType` | 调试用任务类型 | ts L111 |
| `debugHeartScore` | 调试用关系数值 | ts L112 |
| `debugShowExpandBtn` | 调试用展开按钮开关 | ts L113 |
---
## 十、联调 TODO
### 高优先级(数据完全依赖 Mock/硬编码)
- [ ] 替换 `mockTaskDetails``GET /api/tasks/{id}` 真实 API 调用
- [ ] 替换页面内联 mock 备注为 `GET /api/tasks/{id}/notes` API
- [ ] 替换硬编码 `retentionClues`8 条维客线索)为 `GET /api/tasks/{id}/retention-clues` API
- [ ] 替换硬编码 `talkingPoints`5 条话术)为 `GET /api/tasks/{id}/talking-points` 或 AI 生成接口
- [ ] 替换硬编码 `serviceRecords`4 条服务记录)为 `GET /api/tasks/{id}/service-records?days=60` API
- [ ] 替换硬编码 `serviceSummary` 为后端返回或前端聚合计算
- [ ] 替换硬编码手机号 `'13812345678'` / `'138****5678'` 为 API 返回的客户手机号
- [ ] 替换硬编码 `storageLevel: '非常多'` 为客户信息 API 返回值
### 中优先级(操作类接口)
- [ ] `onAbandonConfirm` 对接 `POST /api/tasks/{id}/abandon`
- [ ] `cancelAbandon` 对接 `POST /api/tasks/{id}/cancel-abandon`
- [ ] `onNoteConfirm` 对接 `POST /api/tasks/{id}/notes`
- [ ] `onDeleteNote` 对接 `DELETE /api/tasks/{id}/notes/{noteId}`
### 低优先级(优化项)
- [ ] WXML 中 `suggestion-intro` 硬编码文案改为从 AI 分析结果动态获取
- [ ] 移除或条件隐藏调试面板(`showDebugPanel` 相关代码)
- [ ] 统一维客线索数据结构:页面硬编码的 `RetentionClue` 接口与 `mock-data.ts` 中定义的不一致
- [ ] `formatMoney` 已 import 但 TS 中未使用WXML 用 WXS 版),可清理无用 import
- [ ] `dev-fab` 组件已在 JSON 注册但 WXML 未引用,可清理