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

12 KiB
Raw Blame History

小程序多页面联调改造任务书

创建日期2026-03-27 来源:本轮对话排查 customer-service-records 页面加载问题后,用户提出的 4 页面改造需求 状态:第 1 步后端改造已完成,第 2-4 步待执行


一、背景与前提

1.1 已完成的工作(本轮对话)

  1. 权限守卫修复auth-guard.tsPAGE_ROLEScustomer-detailcustomer-service-recordscoach-detail 三个页面加入了 coach 角色,解决了 coach 进入后被 reLaunch 踢回首页的问题。

  2. ETL 连接复用fdw_queries.py_fdw_context 新增 etl_conn 可选参数,customer_service.pyget_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_recordsget_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),前端 loadMonthRecordsrawRecords.reduce 累加的是 r.duration,但后端字段名是 durationCamelModel 输出 camelCase需确认字段映射 月度时长显示 0
4 income: 0 后端到手收入计算中 salary_calc 可能没查到数据assistant_id 为 None 或费率为 0 列表收入全部显示 0
5 displayDate 格式错误 前端 generateTimeRanger.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
  • incomer.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. 修复顶部信息

  • customerNameloadCustomerInfo 调的 fetchCustomerDetail 超时问题 — 方案 1get_customer_detail 也做 ETL 连接复用;方案 2get_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 的返回值中取 customerNamecustomerPhone
  • 减少一个 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.tstask-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_infoget_member_balanceget_consumption_60dget_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. Btask-detail 折前时长)→ 小改动
  6. Cperformance 页面)→ 独立改动
  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 解包逻辑)