feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations

This commit is contained in:
Neo
2026-03-20 01:43:48 +08:00
parent 075caf067f
commit 79f9a0e1da
437 changed files with 118603 additions and 976 deletions

View File

@@ -0,0 +1,221 @@
# customer-detail 页面数据来源排查
> 排查日期2026-03-18
> 页面路径pages/customer-detail/customer-detail
## 概览
| 分类 | 数量 | 说明 |
|------|------|------|
| A. Mock 数据 | 1 处 import + 1 处页面内联 | `mockCustomerDetail` 已 import 但未使用;`mockRecords` 内联定义 |
| B. 硬编码数据 | 7 个数据块 | `detail``aiInsight``clues``coachTasks``favoriteCoaches``sortedNotes``consumptionRecords` |
| C. 已对接 API | 0 | 页面无任何 `request()` 调用 |
| D. 前端计算/派生 | 3 处 | `aiColor` 随机、`phoneVisible` 切换、`pageState` 状态机 |
| E. 路由参数 | 0 | `onLoad(options)` 未读取任何参数 |
| F. WXML 硬编码文案 | 14 处 | 标题、标签、按钮文字等 |
---
## 一、Mock 数据
### 1.1 `mockCustomerDetail`(已 import 未使用)
| 项目 | 说明 |
|------|------|
| 来源 | `import { mockCustomerDetail } from "../../utils/mock-data"` |
| 状态 | **已 import 但页面内未引用**,属于死代码 |
| mock-data.ts 中的字段 | `id`, `name`, `avatar`, `tags`, `heartScore`, `phone`, `spiIndex`, `consumptionRecords[]` |
| 联调动作 | 删除此 import页面 `detail` 对象改为从 API 获取 |
### 1.2 `mockRecords`(页面内联 mock
| 项目 | 说明 |
|------|------|
| 来源 | 页面顶部 `const mockRecords: ConsumptionRecord[]` 硬编码 3 条记录 |
| 赋值位置 | `data.consumptionRecords: mockRecords` |
| 字段清单 | 每条记录含:`id`, `type`, `date`, `tableName`, `startTime`, `endTime`, `duration`, `tableFee`, `tableOrigPrice`, `coaches[]``name`, `level`, `levelColor`, `courseType`, `hours`, `perfHours`, `fee`, `foodAmount`, `foodOrigPrice`, `totalAmount`, `totalOrigPrice`, `payMethod`, `rechargeAmount` |
| 联调 API | `GET /api/xcx/customer/{member_id}/consumption-records`(待开发) |
| 风险 | 🔴 高 — 金额字段涉及 `tableFee`/`totalAmount` 等财务数据,需严格对齐 DWD-DOC 口径 |
---
## 二、硬编码数据
### 2.1 `detail` 对象(客户基础信息)
| 字段 | 硬编码值 | 应对接来源 | 风险 |
|------|----------|-----------|------|
| `id` | `"cust_001"` | API 返回 `member_id` | 🔴 高 |
| `name` | `"王先生"` | API `dim_member.nickname` | 🔴 高 |
| `avatarChar` | `"王"` | 前端从 `name` 截取首字 | 🟡 中 |
| `phone` | `"13812345678"` | API `dim_member.mobile`(注意 DQ-6需通过 `member_id` JOIN `dim_member` | 🔴 高 |
| `balance` | `"8,600"` | API 储值余额(`balance_pay` | 🔴 高 |
| `consumption60d` | `"2,800"` | API 近60天消费汇总 | 🔴 高 |
| `idealInterval` | `"7天"` | API / AI 计算 | 🟡 中 |
| `daysSinceVisit` | `"12天"` | API 最后到店距今天数 | 🔴 高 |
**联调 API**`GET /api/xcx/customer/{member_id}/profile`(待开发)
### 2.2 `aiInsight` 对象AI 智能洞察)
| 字段 | 硬编码值 | 应对接来源 | 风险 |
|------|----------|-----------|------|
| `summary` | 长文本("高价值 VIP 客户…" | AI 缓存 API `GET /api/xcx/ai-cache?cache_type=customer_insight&target_id={member_id}` | 🔴 高 |
| `strategies[]` | 3 条策略,含 `color` + `text` | 同上AI 缓存返回 | 🔴 高 |
**联调 API**`GET /api/xcx/ai-cache`(已有路由 `xcx_ai_cache.py`,需确认 `cache_type` 枚举是否包含 `customer_insight`
### 2.3 `clues` 数组(维客线索)
共 7 条硬编码线索:
| 索引 | category | text 摘要 | source | 应对接来源 | 风险 |
|------|----------|----------|--------|-----------|------|
| 0 | 客户基础 | 🎂 生日 3月15日 · VIP会员 · 注册2年 | 系统 | API 维客线索 | 🔴 高 |
| 1 | 消费习惯 | 🌙 常来夜场 · 月均4-5次 | 系统 | API 维客线索 | 🔴 高 |
| 2 | 消费习惯 | 💰 高客单价(含 detail | 系统 | API 维客线索 | 🔴 高 |
| 3 | 玩法偏好 | 🎱 偏爱中式 · 斯诺克进阶中 | 系统 | API 维客线索 | 🔴 高 |
| 4 | 促销接受 | 🍷 爱点酒水套餐(含 detail | 系统 | API 维客线索 | 🔴 高 |
| 5 | 社交关系 | 👥 常带朋友(含 detail | 系统 | API 维客线索 | 🔴 高 |
| 6 | 重要反馈 | ⚠️ 想练斯诺克走位… | 小燕 | API 维客线索 | 🔴 高 |
**联调 API**`GET /api/retention-clue/{member_id}?site_id=X`(已有路由 `member_retention_clue.py`
### 2.4 `coachTasks` 数组(助教任务分配)
共 4 条硬编码任务卡片:
| 索引 | name | taskType | 字段清单 | 风险 |
|------|------|----------|---------|------|
| 0 | 小燕 | 高优先召回 | `level`, `levelColor`, `taskColor`, `bgClass`, `status`, `lastService`, `metrics[]`近60天次数/总时长/次均时长) | 🔴 高 |
| 1 | 泡芙 | 优先召回 | 同上 + `status: "pinned"` | 🔴 高 |
| 2 | Amy | 关系构建 | 同上 | 🔴 高 |
| 3 | Lucy | 客户回访 | 同上 + `status: "abandoned"` | 🔴 高 |
**联调 API**`GET /api/xcx/customer/{member_id}/coach-tasks`(待开发;后端 `biz.coach_tasks` 表已存在)
### 2.5 `favoriteCoaches` 数组(最喜欢的助教)
共 2 条硬编码:
| 索引 | name | 字段清单 | 风险 |
|------|------|---------|------|
| 0 | 小燕 | `emoji`, `relationIndex: "9.2"`, `indexColor`, `bgClass`, `stats[]`(基础/激励/上课/充值) | 🔴 高 |
| 1 | 泡芙 | 同上,`relationIndex: "7.8"` | 🔴 高 |
**联调 API**`GET /api/xcx/customer/{member_id}/favorite-coaches`(待开发;需 DWS 层助教-客户关系聚合)
### 2.6 `sortedNotes` 数组(备注记录)
共 3 条硬编码备注:
| 索引 | tagLabel | createdAt | content 摘要 | 风险 |
|------|----------|-----------|-------------|------|
| 0 | 管理员 | 2026-03-05 14:30 | 对斯诺克课程感兴趣… | 🔴 高 |
| 1 | 小燕 | 2026-02-20 16:45 | 客户反馈服务态度很好… | 🔴 高 |
| 2 | 管理员 | 2026-02-10 10:00 | 储值活动当天即充值… | 🔴 高 |
**联调 API**`GET /api/xcx/notes?target_type=member&target_id={member_id}`(已有路由 `xcx_notes.py`
### 2.7 其他硬编码值
| 位置 | 字段 | 硬编码值 | 说明 | 风险 |
|------|------|----------|------|------|
| `data` | `pageState` | `"loading"` | 初始状态,正常 | 🟢 低 |
| `data` | `phoneVisible` | `false` | UI 状态,正常 | 🟢 低 |
| `data` | `loadingMore` | `false` | UI 状态,正常 | 🟢 低 |
| `data` | `noteModalVisible` | `false` | UI 状态,正常 | 🟢 低 |
| `loadDetail()` | `pageState` | 直接设为 `"normal"` | ⚠️ 无 API 调用,直接跳过 loading | 🔴 高 |
| WXML | 手机号掩码 | `'138****5678'` | 硬编码掩码,应从 `detail.phone` 动态生成 | 🟡 中 |
---
## 三、已对接 API
| API | 状态 |
|-----|------|
| (无) | 页面当前 **零 API 调用**`loadDetail()` 为空壳 |
---
## 四、前端计算/派生数据
| 字段 | 计算逻辑 | 说明 |
|------|----------|------|
| `aiColor` | `onLoad` 中随机从 6 色数组取值 | 用于 AI 洞察卡片配色,纯 UI 装饰 |
| `phoneVisible` | `onTogglePhone()` 切换布尔值 | 控制手机号显示/隐藏 |
| `pageState` | `loadDetail()` 中设为 `"normal"` | 应改为loading → API 成功 normal / 失败 error / 空 empty |
---
## 五、路由参数
| 参数 | 状态 | 说明 |
|------|------|------|
| `options.id` / `options.memberId` | ❌ 未读取 | `onLoad(options)` 接收了 `options` 但未使用任何参数 |
**联调时必须**:从 `options` 中读取 `memberId`(或 `id`),作为所有 API 请求的入参。
---
## 六、WXML 中的硬编码文案
| 位置 | 文案 | 是否需要动态化 | 说明 |
|------|------|---------------|------|
| 导航栏 | `"客户详情"` | ❌ 保持 | JSON 配置 `navigationBarTitleText` |
| loading 态 | `"加载中..."` | ❌ 保持 | 通用文案 |
| empty 态 | `"未找到客户信息"` | ❌ 保持 | 通用文案 |
| error 态 | `"加载失败"` | ❌ 保持 | 通用文案 |
| error 按钮 | `"点击重试"` | ❌ 保持 | 通用文案 |
| banner 统计标签 | `"储值余额"` / `"60天消费"` / `"理想间隔"` / `"距今到店"` | ❌ 保持 | 固定标签 |
| AI 卡片标题 | `"AI 智能洞察"` | ❌ 保持 | 固定标题 |
| 策略标题 | `"当前推荐策略"` | ❌ 保持 | 固定标题 |
| 维客线索标题 | `"维客线索"` | ❌ 保持 | 固定标题 |
| 助教任务标题 | `"助教任务分配"` / `"当前进行中"` | ❌ 保持 | 固定标题 |
| 最喜欢助教标题 | `"最喜欢的助教"` / `"近60天"` | ❌ 保持 | 固定标题 |
| 消费记录 | `"消费记录"` / `"商城订单"` / `"总金额"` / `"🍷 食品酒水"` | ❌ 保持 | 固定标签 |
| 底部按钮 | `"问问助手"` / `"备注"` | ❌ 保持 | 固定文案 |
| 空态提示 | `"暂无消费记录"` / `"暂无备注"` | ❌ 保持 | 通用文案 |
---
## 七、联调 TODO
### 优先级 P0页面核心数据阻塞上线
| # | 数据块 | 当前状态 | 需要的 API | 后端现状 |
|---|--------|---------|-----------|---------|
| 1 | 客户基础信息 `detail` | 全部硬编码 | `GET /api/xcx/customer/{member_id}/profile` | ❌ 待开发 |
| 2 | 消费记录 `consumptionRecords` | 内联 mock | `GET /api/xcx/customer/{member_id}/consumption-records` | ❌ 待开发 |
| 3 | 路由参数 `memberId` | 未读取 | 从 `onLoad(options)` 读取 | — |
| 4 | `loadDetail()` 空壳 | 直接设 normal | 改为真实 API 调用 + 状态管理 | — |
### 优先级 P1重要业务数据
| # | 数据块 | 当前状态 | 需要的 API | 后端现状 |
|---|--------|---------|-----------|---------|
| 5 | 维客线索 `clues` | 全部硬编码 | `GET /api/retention-clue/{member_id}` | ✅ 已有路由 |
| 6 | 备注记录 `sortedNotes` | 全部硬编码 | `GET /api/xcx/notes?target_type=member&target_id={member_id}` | ✅ 已有路由 |
| 7 | AI 智能洞察 `aiInsight` | 全部硬编码 | `GET /api/xcx/ai-cache?cache_type=customer_insight&target_id={member_id}` | ⚠️ 路由已有,需确认 cache_type |
### 优先级 P2增强功能
| # | 数据块 | 当前状态 | 需要的 API | 后端现状 |
|---|--------|---------|-----------|---------|
| 8 | 助教任务 `coachTasks` | 全部硬编码 | `GET /api/xcx/customer/{member_id}/coach-tasks` | ❌ 待开发(`biz.coach_tasks` 表已有) |
| 9 | 最喜欢助教 `favoriteCoaches` | 全部硬编码 | `GET /api/xcx/customer/{member_id}/favorite-coaches` | ❌ 待开发(需 DWS 聚合) |
### 前端清理项
| # | 项目 | 说明 |
|---|------|------|
| 10 | 删除 `import { mockCustomerDetail }` | 死代码import 后未使用 |
| 11 | 删除 `const mockRecords` 内联 mock | 替换为 API 数据 |
| 12 | 手机号掩码硬编码 `'138****5678'` | 改为从 `detail.phone` 动态生成掩码 |
| 13 | `ConsumptionRecord` interface | 页面内重复定义,应移至 `utils/types.ts` 或复用 `mock-data.ts` 中的类型 |
### 注意事项
- **DQ-6 会员手机号断档**`settlement_head.member_phone` 自 2025-12 起全为 NULL后端 API 必须通过 `member_id` JOIN `dim_member.mobile` 获取
- **金额口径**:消费记录中的金额字段需严格对齐 DWD-DOC 标杆文档,禁止直接使用 `consume_money`
- **助教费用拆分**:消费记录中的 `coaches[].fee` 需区分陪打(`assistant_pd_money`)和超休(`assistant_cx_money`