Files
Neo-ZQYY/docs/prd/specs/miniapp-page-refinement-2026-03-27.md
Neo 6f8f12314f 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>
2026-04-06 00:03:48 +08:00

239 lines
12 KiB
Markdown
Raw Permalink 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.
# 小程序多页面联调改造任务书
> 创建日期2026-03-27
> 来源:本轮对话排查 customer-service-records 页面加载问题后,用户提出的 4 页面改造需求
> 状态:第 1 步后端改造已完成,第 2-4 步待执行
---
## 一、背景与前提
### 1.1 已完成的工作(本轮对话)
1. **权限守卫修复**`auth-guard.ts``PAGE_ROLES``customer-detail``customer-service-records``coach-detail` 三个页面加入了 `coach` 角色,解决了 coach 进入后被 `reLaunch` 踢回首页的问题。
2. **ETL 连接复用**`fdw_queries.py``_fdw_context` 新增 `etl_conn` 可选参数,`customer_service.py``get_customer_records` 中只创建一个 ETL 连接传给所有子查询,连接开销从 ~10s 降到 ~2.6s。
3. **后端 assistant_id 过滤**
- `customer_service.py` 新增 `_get_assistant_id()``auth.user_assistant_binding` 获取 assistant_id
- `get_customer_records` 签名新增 `user_id` 参数,内部获取 assistant_id 后传给所有 fdw_queries 调用
- `fdw_queries.get_customer_service_records``get_total_service_count` 加入可选 `assistant_id` 过滤(`site_assistant_id = %s`
- `_get_month_aggregation` 同步加入 `assistant_id` 过滤
- 路由层 `xcx_customers.py` 传入 `user.user_id`
4. **后端到手收入计算**
- `get_customer_records` 中查询 `v_dws_assistant_salary_calc` 获取当月费率
- 基础课到手 = hours × (base_course_price - base_deduction)
- 激励课到手 = hours × bonus_course_price × (1 - bonus_deduction_ratio)
- 充值提成到手 = ledger_amount直接取
- Schema 新增 `month_income: float` 字段
5. **BACKLOG 更新**`docs/roadmap/BACKLOG.md` 新增 AI BudgetTracker 和 admin_db_health UnicodeDecodeError 两条待办。
6. **联调规范更新**`docs/guides/FRONTEND-BACKEND-INTEGRATION.md``.kiro/steering/frontend-backend-integration.md` 新增 ETL 连接复用规则。
### 1.2 已识别但未修复的数据问题
通过开发者工具读取页面 data 发现以下问题:
| # | 问题 | 原因 | 影响 |
|---|------|------|------|
| 1 | `customerName` 为空 | `loadCustomerInfo` 调的 `get_customer_detail` 还没做 ETL 连接复用优化,可能超时 | 顶部不显示客户名 |
| 2 | `totalServiceCount: 0` | 后端 `get_total_service_count` 返回 0需排查 assistant_id 是否正确传入 | 顶部"服务X次"显示 0 |
| 3 | `monthHours: "0.0h"` | 后端返回 `duration` 是小时数(如 1.1),前端 `loadMonthRecords``rawRecords.reduce` 累加的是 `r.duration`,但后端字段名是 `duration`CamelModel 输出 camelCase需确认字段映射 | 月度时长显示 0 |
| 4 | `income: 0` | 后端到手收入计算中 `salary_calc` 可能没查到数据assistant_id 为 None 或费率为 0 | 列表收入全部显示 0 |
| 5 | `displayDate` 格式错误 | 前端 `generateTimeRange``r.duration`(小时数)当分钟数处理,导致 `19:1.116...` | 时间显示乱码 |
| 6 | `table` 显示 ID | 后端返回 `table_id`(数字 ID前端直接显示应该显示台桌名称 | 台桌号显示为长数字 |
| 7 | `durationHours: 0` | 前端 `(r.duration \|\| 0) / 60` 把小时数再除以 60 变成了接近 0 的值 | 列表时长显示 0 |
### 1.3 开发环境
- Windows + PowerShell
- 后端:`start-admin.bat` 启动uvicorn --reload 端口 8000
- 前端:微信开发者工具(自动化端口 9420
- 数据库ETL 库PG_DSN+ 业务库APP_DB_DSNETL 连接耗时 ~2.6s
- 可用调试工具weixin-devtools MCP power连接 `ws://127.0.0.1:9420`
### 1.4 关键文件位置
| 文件 | 说明 |
|------|------|
| `apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.ts` | 前端页面逻辑 |
| `apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.wxml` | 前端页面模板 |
| `apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.wxss` | 前端页面样式 |
| `apps/miniprogram/miniprogram/services/api.ts` | 前端 API 调用层 |
| `apps/miniprogram/miniprogram/utils/request.ts` | 前端请求封装(含 ResponseWrapper 解包) |
| `apps/miniprogram/miniprogram/utils/auth-guard.ts` | 权限守卫 |
| `apps/miniprogram/miniprogram/pages/task-detail/task-detail.ts` | 任务详情页参考60天服务记录列表格式 |
| `apps/miniprogram/miniprogram/pages/task-detail/task-detail.wxml` | 任务详情页模板参考service-record-card |
| `apps/miniprogram/miniprogram/pages/performance/performance.ts` | 绩效页 |
| `apps/miniprogram/miniprogram/pages/performance/performance.wxml` | 绩效页模板 |
| `apps/miniprogram/miniprogram/pages/task-list/task-list.ts` | 任务列表页 |
| `apps/miniprogram/miniprogram/pages/task-list/task-list.wxml` | 任务列表页模板 |
| `apps/backend/app/services/customer_service.py` | 后端客户服务 |
| `apps/backend/app/services/fdw_queries.py` | 后端 FDW 查询 |
| `apps/backend/app/routers/xcx_customers.py` | 后端客户路由 |
| `apps/backend/app/schemas/xcx_customers.py` | 后端响应 Schema |
| `apps/backend/app/routers/xcx_auth.py` | 后端认证路由(/api/xcx/me |
| `apps/backend/app/auth/dependencies.py` | JWT 解析CurrentUser |
### 1.5 关键数据口径
- **到手收入**:基础课 = hours × (base_course_price - base_deduction);激励课 = hours × bonus_course_price × (1 - bonus_deduction_ratio);充值提成 = ledger_amount
- **费率来源**`v_dws_assistant_salary_calc`,按 `assistant_id + salary_month` 查询
- **DWD 层助教字段**`site_assistant_id`(与 `auth.user_assistant_binding.assistant_id` 相同)
- **废单排除**`is_delete = 0`
- **会员信息**:通过 `member_id` JOIN `v_dim_member (scd2_is_current=1)`
- **CamelModel**:后端 Pydantic schema 自动将 snake_case 转 camelCase 输出
- **ResponseWrapper**:后端中间件将 2xx JSON 包装为 `{ code: 0, data: <原始body> }`,前端 `request.ts` 自动解包
---
## 二、待执行任务
### 任务 Acustomer-service-records 前端改造(优先级最高)
#### A1. 修复数据转换逻辑
前端 `loadMonthRecords` 中的字段映射需要对齐后端 CamelModel 输出:
```
后端返回camelCase 前端当前读取 问题
─────────────────────────────────────────────────
duration (float, 小时) r.duration / 60 多除了一次 60
durationRaw (float, 小时) 未使用 应直接用
income (float, 到手元) r.amount 字段名不匹配
timeRange (string|null) 未使用 后端已返回,前端用 generateTimeRange 自己算
courseType (string) r.courseType ✓ 正确
table (string|null) r.table 后端返回 table_id 数字,需要改为台桌名
recordType (string) 前端自己判断 后端已返回,应直接用
isEstimate (bool) r.isEstimate ✓ 正确
drinks (string|null) r.drinks 后端当前返回 null后续需补齐
```
修改要点:
- `durationHours` 直接用 `r.duration`(已是小时数),不再 `/60`
- `durationRaw` 直接用 `r.durationRaw`
- `income``r.income`(后端已计算到手)
- `displayDate` 优先用后端返回的 `r.timeRange`,无值时才用 `generateTimeRange`
- `generateTimeRange` 的参数是小时数不是分钟数,需修正或移除
- `table` 字段:后端当前返回 `table_id`(数字),需要后端改为返回台桌名称,或前端做映射
#### A2. 修复月度统计
- `monthHours` 累加逻辑:`rawRecords.reduce((sum, r) => sum + (r.duration || 0), 0)` — 后端返回的 `duration` 已是小时数,不需要再 `/60`
- 新增 `monthIncome`:从后端返回的 `monthIncome` 字段读取并展示
#### A3. 修复顶部信息
- `customerName``loadCustomerInfo` 调的 `fetchCustomerDetail` 超时问题 — 方案 1`get_customer_detail` 也做 ETL 连接复用;方案 2`get_customer_records` 的返回值中直接取 `customerName`(后端已返回)
- `totalServiceCount`:从 `get_customer_records` 返回值中取(后端已返回 `totalServiceCount`
- 手机号查看/复制交互:对齐 task-detail 页面的实现
- 首字母头像:使用通用颜色规则(`nameToAvatarColor`
#### A4. 列表记录格式对齐 task-detail
- 对齐 task-detail 的"60天内服务记录"列表的 `service-record-card` 组件格式
- 确认 `service-record-card` 组件是否已存在,还是内联在 task-detail 的 WXML 中
- 到手收入显示:`¥XXX`
- 课程类型标签:基础课/激励课/充值
- 折前/折后时长:`durationRaw !== durationHours` 时才显示折前
#### A5. 加速加载
- `loadCustomerInfo` 可以去掉(改为从 `loadMonthRecords` 的返回值中取 `customerName``customerPhone`
- 减少一个 API 请求 = 减少一次 ETL 连接开销
---
### 任务 Btask-detail 折前时长条件显示
文件:`apps/miniprogram/miniprogram/pages/task-detail/task-detail.wxml`
- 找到服务记录列表中显示"折前X.Xh"的位置
- 加条件判断:`wx:if="{{item.durationRaw && item.durationRaw !== item.durationHours}}"`
- 折前和折后相等时隐藏折前标签
---
### 任务 Cperformance 页面改造
文件:`apps/miniprogram/miniprogram/pages/performance/performance.ts` + `.wxml`
#### C1. 未知客户点击拦截
- 找到客户卡片点击跳转逻辑
- 判断 `memberId <= 0`(散客/未知客户)时,`wx.showToast({ title: '未知客户不提供查看客户任务详情', icon: 'none' })` 并 return
- 非散客正常跳转
#### C2. 角色标签对齐 task-list
- 查看 task-list 页面的角色标签展示方式WXML + WXSS
- 对齐 performance 页面顶部的角色标签样式
---
### 任务 D头像修复performance + task-list
文件:`performance.ts``task-list.ts`、对应 `.wxml`
- 后端 `/api/xcx/me` 已返回 `avatarUrl` 字段apply 时上传的头像)
- 前端 `fetchMe()` 返回的 `data.avatarUrl` 需要绑定到页面 data
- WXML 中头像 `<image>``src` 应优先用 `avatarUrl`,无值时 fallback 到默认占位图或首字母头像
- 确认 task-list 和 performance 两个页面的 banner 区域头像都使用此逻辑
---
### 任务 E后端补充优化可选
#### E1. get_customer_detail ETL 连接复用
-`get_customer_records` 相同的模式:创建一个 ETL 连接传给所有 fdw_queries 调用
- 涉及的子查询:`get_member_info``get_member_balance``get_consumption_60d``get_last_visit_days``_build_consumption_records``_build_coach_tasks``_build_favorite_coaches`
#### E2. 台桌名称返回
- 当前 `get_customer_service_records` 返回 `site_table_id`(数字 ID
- 需要 JOIN `v_dim_table`(或类似维度表)获取台桌名称
- 或者前端做映射(不推荐,增加前端复杂度)
#### E3. 到手收入排查
- 确认 `_get_assistant_id` 返回值是否正确(可能 `user_assistant_binding` 中没有对应记录)
- 确认 `get_salary_calc` 是否能查到当月数据(可能该月还没有 salary_calc 记录)
- 费率为 0 时的兜底处理
---
## 三、执行顺序建议
1. **E3**(排查到手收入为 0 的原因)→ 确认后端数据正确性
2. **A1 + A2 + A3**(前端数据转换修复)→ 页面能正确展示数据
3. **A4**(列表格式对齐 task-detail→ UI 一致性
4. **A5**(加速加载)→ 去掉多余 API 请求
5. **B**task-detail 折前时长)→ 小改动
6. **C**performance 页面)→ 独立改动
7. **D**(头像修复)→ 独立改动
8. **E1 + E2**(后端补充优化)→ 可选
---
## 四、验证方式
每步完成后:
1. 微信开发者工具编译Ctrl+B
2. 操作路径:任务列表 → 轩哥 → 任务详情 → 查看全部服务记录
3. 用 weixin-devtools MCP power 的 `evaluate_script` 读取页面 data 验证字段值
4.`list_console_messages` 检查是否有错误
5. 后端改动通过 uvicorn `--reload` 自动加载,看后端窗口有无报错
---
## 五、参考文档
- `docs/guides/FRONTEND-BACKEND-INTEGRATION.md` — 联调规范字段命名、到手收入口径、ETL 连接复用等)
- `.kiro/steering/frontend-backend-integration.md` — 联调规范摘要
- `.kiro/steering/feiqiu-data-rules.md` — 飞球数据规范索引
- `apps/backend/app/schemas/xcx_customers.py` — 后端响应 SchemaCamelModel 自动转 camelCase
- `apps/miniprogram/miniprogram/utils/request.ts` — 前端请求封装ResponseWrapper 解包逻辑)