feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本

包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Neo
2026-04-06 00:03:48 +08:00
parent 70324d8542
commit 6f8f12314f
515 changed files with 76604 additions and 7456 deletions

View File

@@ -0,0 +1,244 @@
# P13小程序前端 — 联调补齐与格式统一 — miniapp-fe-polish
> 优先级P13依赖 P6~P9 前端页面 + P3 认证 + P4 核心业务 + 后端接口)
> 预估工作量:中
> 生成日期2026-03-20
---
## 背景
小程序前端页面已完成 H5 原型还原P6~P9但在 MOCK 数据排查中发现多处功能点未对接真实数据或逻辑缺失。本 SPEC 统一收敛这些遗漏项,确保每个页面在联调后数据展示完整、格式统一。
---
## 一、通用规则(跨页面)
### G1微信头像与用户信息
**现状**`fetchMe()` 接口已定义但返回空 mock`task-list` 声明了 `avatarUrl` 但未赋值;`performance`/`performance-records``avatarUrl` 字段。
**需求**
- 登录成功后,从后端获取用户信息(微信头像 URL、微信昵称、角色、门店名称
- 后端接口 `GET /api/xcx/me` 返回 `{ avatarUrl, nickName, role, storeName }`
- 头像来源:微信登录时由后端通过 `code2Session` + `getUserInfo` 获取并存储,前端不直接调用 `wx.getUserProfile`
- 所有含 banner 的页面task-list、performance、performance-records统一从全局用户信息读取 `avatarUrl`
**验收标准**
- AC-G1.1:三个 banner 页面展示微信头像,无头像时显示默认占位图
- AC-G1.2:用户昵称、角色、门店名称正确展示
### G2当月预估判断
**现状**WXML 硬编码"预估"文案TS 无当月判断逻辑。
**需求**
- 当查看的数据月份 = 当前自然月时,标题显示"我的预估收入",金额旁显示"预估"标签
- 当查看的数据月份 < 当前自然月时,标题显示"我的收入",无"预估"标签
- 判断逻辑:`isCurrentMonth = (year === nowYear && month === nowMonth)`
- 影响页面performance、performance-records、board-finance
**验收标准**
- AC-G2.1:本月数据显示"预估"标签,历史月份不显示
- AC-G2.2board-finance 本月时间筛选时,经营一览标题含"预估"字样
### G3绩效折算折前/折后)
**现状**`performance-records` 接口定义了 `hoursRaw`(折前)和 `hours`折后WXML 有条件展示逻辑,但 `totalHoursRawLabel` 始终为空。
**需求**
- "绩效折前"/"折前"指 DWS 层定义的绩效惩罚规则计算出的折算前课时
- 后端接口返回 `hours`(折后)和 `hoursRaw`(折前),当两者不同时前端展示"折前 Xh"
- 汇总统计同理:`totalHoursRaw``totalHours` 时展示"折前 Xh"
**验收标准**
- AC-G3.1:存在折算差异时,课时旁显示"折前 Xh"灰色小字
- AC-G3.2:无折算差异时不显示"折前"
### G4储值等级显示规则
**现状**`task-detail` 声明了 `storageLevel` 但无计算逻辑。
**需求**
- 根据客户储值余额balance计算等级文案
- `= 0` → "无"
- `< 200` → "少"
- `< 500` → "一般"
- `< 1500` → "多"
- `≥ 1500` → "非常多"
- 计算在前端完成(后端返回 balance 数值),工具函数放 `utils/storage-level.ts`
**验收标准**
- AC-G4.1task-detail 储值区域根据 balance 正确显示等级文案
- AC-G4.2balance 为 0 时显示"无"
---
## 二、各页面功能点
### P1task-list任务列表
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T1.1 | banner 头像 | `avatarUrl` 声明但未赋值 | 见 G1 |
| T1.2 | 比同期数据 | PerfData 有 `incomeTrend`/`incomeTrendDir`,但值为空 | 后端返回与上月同期的差值(数值,非百分比),如 `+¥1,200` / `-¥800`;前端展示数值 + ↑/↓ 箭头 |
| T1.3 | 放弃原因 | `abandonReason` 硬编码空字符串 | 从后端 task 对象的 `abandonReason` 字段获取,展示放弃时填写的备注文本 |
| T1.4 | 盖戳动画 | ✅ 已实现 | 确认:任何情况下页面加载后盖戳动画都会播放(当前仅 `tierCompleted` 时触发),需改为始终播放 |
**验收标准**
- AC-T1.2:比同期显示为数值差(如 `+¥1,200`),非百分比
- AC-T1.3:已放弃任务卡片展示放弃备注
- AC-T1.4:页面加载后盖戳动画始终播放,不依赖 `tierCompleted`
### P2performance绩效总览
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T2.1 | banner 头像 | 无 `avatarUrl` 字段 | 见 G1 |
| T2.2 | 预估/实际收入 | 硬编码"预估" | 见 G2 |
### P3performance-records绩效明细
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T3.1 | banner 头像 | 无 `avatarUrl` 字段 | 见 G1 |
| T3.2 | 预估/实际收入 | 硬编码"预估" | 见 G2 |
| T3.3 | 总笔数 | 前端 `records.length` 计算 | 后端接口返回 `totalCount` 字段,前端直接使用(分页场景下前端计数不准确) |
| T3.4 | 绩效折前 | 接口有字段但 label 始终为空 | 见 G3 |
### P4task-detail任务详情
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T4.1 | 手机号码 | `onCopyPhone``phone = ''` 硬编码 | 从 `this.data.detail` 获取客户手机号(后端 task 详情接口返回 `customerPhone` |
| T4.2 | 储值显示规则 | `storageLevel` 无计算逻辑 | 见 G4从 detail 的 balance 字段计算 |
| T4.3 | 行动建议 | 仅有"问问助手"跳转 chat | 后端 task 详情接口返回 `actionSuggestions: string[]`AI 生成的行动建议列表),前端在维客线索下方展示为卡片列表 |
| T4.4 | 备注打星 | ✅ 已实现 | star-rating 组件 + note-modal 爱心/台球双维度评分已完整 |
### P5customer-service-records客户服务记录
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T5.1 | 客户名称 | ✅ 已实现 | 从 fetchCustomerDetail 获取 |
| T5.2 | 本月服务次数 | 从 detail 取 `totalServiceCount`,类型断言 `as any` | 后端 `GET /api/xcx/customers/:id` 返回 `totalServiceCount` 字段,前端类型定义补齐 |
| T5.3 | 课程标签 | getTypeLabel 基于 includes 硬编码匹配 | 后端返回 `courseType` 枚举basic/vip/incentive/recharge/snooker/group前端直接映射不再 includes 猜测 |
### P6board-finance财务看板
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T6.1 | AI 智能洞察 | WXML 硬编码 3 行文案 | 后端接口返回 `aiInsights: Array<{ icon: string; text: string }>`,前端动态渲染 |
| T6.2 | 环比箭头 | ✅ 已实现 | compareEnabled 开关 + ↑/↓ 箭头 + 颜色区分 |
| T6.3 | 本月"预估" | 无预估标记 | 见 G2 |
### P7board-customer客户看板
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T7.1 | 爱心 icon | ✅ 已实现 | heart-icon 组件 + getHeartEmoji 映射 |
### P8customer-detail客户详情
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T8.1 | 电话 | ✅ 已实现 | onTogglePhone + onCopyPhone 完整 |
### P9board-coach助教看板
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T9.1 | 级别 Icon | ✅ 已实现 | coach-level-tag 组件 + LEVEL_CLASS 映射 |
### P10coach-detail助教详情
| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T10.1 | 任务执行数量统计 | `taskStats` 硬编码 `{ recall: 24, callback: 14 }` | 后端接口返回 `taskStats: { recall: number; callback: number }`,前端从 API 获取 |
---
## 三、数据格式统一标准
### 已有格式化工具函数(但并没有进行全部调用)
| 类型 | TS 函数 | WXS 函数 | 格式示例 | 缺省值 |
|------|---------|----------|----------|--------|
| 金额 | `formatMoney(value)` | `money(value)` | ¥12,680 / -¥368 / ¥0 | -- |
| 计数 | `formatCount(value, unit)` | `count(value, unit)` | 18笔 / 3次 / 12人 | -- |
| 百分比 | `formatPercent(value)` | `percent(value)` | 12.5% / 0% | -- |
| 课时 | `formatHours(hours)` | `hours(value)` | 86h / 72.5h / 0h | -- |
| 相对时间 | `formatRelativeTime(value)` | — | 刚刚 / 3分钟前 / 2天前 / 03-10 | -- |
| 截止日期 | `formatDeadline(deadline)` | — | 逾期3天 / 今天到期 / 还剩5天 / 03-15 | -- |
| IM 时间 | `formatIMTime(value)` | — | 14:30 / 03-10 14:30 | (空字符串) |
| 爱心 | `getHeartEmoji(score)` | — | 💖 / 🧡 / 💛 / 💙 | 💙 |
| 星级 | `scoreToHalfStar(score)` | — | 4.5 / 3.0 | 0 |
| 空值兜底 | — | `safe(val)` | (原值) | -- |
### 需要补充的格式化
| 类型 | 建议函数名 | 格式示例 | 缺省值 | 说明 |
|------|-----------|----------|--------|------|
| 日期(短) | `formatDateShort(date)` | 3月15日 / 03-15 | -- | 用于服务记录、充值日期等 |
| 日期(完整) | `formatDateFull(date)` | 2026-03-15 | -- | 用于历史数据、导出 |
| 天数 | `formatDays(days)` | 3天 / 15天 | -- | 用于"N天前到店"、逾期天数 |
| 储值等级 | `formatStorageLevel(balance)` | 无/少/一般/多/非常多 | 无 | 见 G4 |
| 同比差值 | `formatTrendValue(value)` | +¥1,200 / -¥800 | -- | 用于"比同期"展示 |
### 接口端格式对齐原则
1. 后端返回原始数值number前端负责格式化展示
2. 金额单位统一为"元"(整数),前端加 ¥ 前缀和千分位
3. 课时单位统一为"小时"number前端加 h 后缀
4. 日期统一 ISO 8601 格式(`YYYY-MM-DDTHH:mm:ss`),前端按场景格式化
5. 百分比后端返回 0-100 的 number前端加 % 后缀
6. 所有 null/undefined/0 值,前端统一展示为 `--`(通过 format 函数兜底)
---
## 四、实施优先级
### 第一批(阻塞联调)
- G1 微信头像(影响 3 个页面 banner
- T1.3 放弃原因(后端字段直通)
- T4.1 手机号码(后端字段直通)
- T3.3 总笔数(后端字段直通)
- T10.1 任务执行统计(后端字段直通)
### 第二批(业务逻辑)
- G2 当月预估判断(纯前端逻辑)
- G3 绩效折算展示(前端条件渲染)
- G4 储值等级规则(前端计算)
- T1.2 比同期数据(需后端新增字段)
- T1.4 盖戳动画始终播放(前端逻辑调整)
- T5.3 课程标签枚举化(前后端对齐)
### 第三批(增强体验)
- T4.3 行动建议(需后端 AI 接口)
- T6.1 AI 智能洞察(需后端 AI 接口)
- 格式化工具函数补充
---
## 五、涉及文件清单
### 前端
- `apps/miniprogram/miniprogram/services/api.ts`
- `apps/miniprogram/miniprogram/utils/money.ts`
- `apps/miniprogram/miniprogram/utils/time.ts`
- `apps/miniprogram/miniprogram/utils/storage-level.ts`(新建)
- `apps/miniprogram/miniprogram/pages/task-list/task-list.ts`
- `apps/miniprogram/miniprogram/pages/performance/performance.ts`
- `apps/miniprogram/miniprogram/pages/performance-records/performance-records.ts`
- `apps/miniprogram/miniprogram/pages/task-detail/task-detail.ts`
- `apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.ts`
- `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts`
- `apps/miniprogram/miniprogram/pages/coach-detail/coach-detail.ts`
### 后端(接口变更)
- `GET /api/xcx/me` — 补充 avatarUrl 字段
- `GET /api/xcx/tasks` — performance 补充 `trendValue`(同比差值)
- `GET /api/xcx/tasks/:id` — 补充 `customerPhone``actionSuggestions`
- `GET /api/xcx/customers/:id` — 类型定义补充 `totalServiceCount`
- `GET /api/xcx/performance/records` — 补充 `totalCount``hoursRaw`
- `GET /api/xcx/coaches/:id` — 补充 `taskStats`
- `GET /api/xcx/board/finance` — 补充 `aiInsights``isEstimated`