Files
Neo-ZQYY/docs/specs/miniapp-data-perf-optimization/tasks.md
Neo 2a7a5d68aa feat: 2026-04-15~04-20 累积变更基线 — 多主线合流
主线 1: rns1-customer-coach-api + 04-miniapp-core-business 后端实施
  - 新增 GET /xcx/coaches/{id}/banner 轻量接口
  - performance/records 加 coach_id 参数 + view_board_coach 权限分流
  - coach/customer/performance/board/task 服务层重构
  - fdw_queries 结算单粒度聚合 + consumption_summary 视图统一
  - task_generator 回访宽限 72h + UPSERT 替代策略 + Step 5 保底清理
  - recall_detector settle_type=3 双重限制 + 门店级 resolved

主线 2: 小程序权限分流 + 新增 coach-service-records 管理者视角业绩明细页
  - perf-progress 共享模块去重 task-list/coach-detail 动画逻辑
  - isScattered 散客标记端到端
  - foodDetail/phoneFull/creator* 字段透传

主线 3: P19 指数回测框架 Phase 1+2
  - 3 个指数表 stat_date 日快照模式
  - 新增 DWS_INDEX_BACKFILL / DWS_TASK_SIMULATION 工具任务
  - task_engine 升级 HTTP 实时 + 推演回测双模式

主线 4: Core 维度层启用
  - 新增 CORE_DIM_SYNC 任务(DWD → core 4 维度表)
  - 修复 app 视图空查询问题

主线 5: member_project_tag 改为 LAST_30_VISITS 消费次数窗口

主线 6: 2 个迁移 SQL 已执行(stat_date + member_project_tag 新窗口)
  - schema 基线与 DDL 快照同步

主线 7: 开发机路径迁移 C:\NeoZQYY → C:\Project\NeoZQYY(约 95% 改动量)

附带: 新建运维脚本(churned_customer_report / simulate_historical_tasks /
      backfill_index_snapshots)+ tools/task-analysis/ 任务分析工具

合计 157 文件。未包含中间产物(tmp/ .playwright-mcp/ inspect-* excel/sheet 分析 txt)。
审计记录见下一个 commit。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 06:32:07 +08:00

302 lines
15 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.
# 实施计划:小程序数据获取与展示效率优化
## 概述
按"数据正确性优先于性能"原则,先拍基线快照、再做 DDL 变更、然后后端优化、前端优化,最后收尾验证。后端使用 PythonFastAPI + pytest/hypothesis前端使用 TypeScript微信小程序 + fast-check数据库使用 PostgreSQL DDL。
## 任务
- [ ] 1. 数据基线快照(优化前)
- [x] 1.1 创建基线快照测试脚本 `tests/test_perf_optimization_baseline.py`
- 使用小燕真实数据(`assistant_id: 2964673443302213``site_id: 2790685415443269`
-`get_coach_detail``get_task_list_v2``get_task_detail` 各执行一次
- 将完整返回值序列化为 JSON 存储到 `tests/fixtures/perf_optimization/`
- 实现逐字段 diff 对比函数,排除 `updated_at``data_updated_at` 等时间戳字段
- diff 覆盖到手金额、RS 指数、会员余额、服务记录条数/排序、酒水明细、TOP 客户列表、`aiAnalysis``talkingPoints`
- _Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 10.4_
- [x] 1.2 执行基线快照采集
- 运行 `pytest tests/test_perf_optimization_baseline.py -v` 生成基线 JSON 文件
- 确认三个快照文件已生成并纳入版本控制
- _Requirements: 9.1, 9.6_
- [ ] 2. 数据库 DDL 变更
- [ ] 2.1 创建迁移脚本 `db/zqyy_app/migrations/YYYY-MM-DD__coach_tasks_updated_at.sql`
- `ALTER TABLE biz.coach_tasks ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT now()`
- 创建触发器函数 `biz.coach_tasks_set_updated_at()` 和触发器 `trg_coach_tasks_updated_at`
- 触发器在 `BEFORE UPDATE` 时自动设置 `NEW.updated_at = now()`
- _Requirements: 6.4, 6.5_
- [ ] 2.2 编写属性测试updated_at 自动更新
- **Property 9: 任务更新后 updated_at 自动变化**
- 使用 hypothesis 生成随机任务状态变更
- 验证 UPDATE 后 `updated_at` 严格大于更新前的值
- **验证: 需求 6.4**
- [ ] 3. 检查点 — DDL 变更验证
- 执行迁移脚本到测试库,验证新字段和触发器已正确创建
- 确保 Property 9 测试通过ask the user if questions arise.
- [ ] 4. 后端优化Performance 页 N+1 查询消除
- [ ] 4.1 新增 `fdw_queries.get_relation_index_batch()` 函数
- 接受 `conn, site_id, assistant_id, member_ids` 参数
- 返回 `dict[int, float]`member_id → rs_display 映射)
- `member_ids` 为空时直接返回空字典,不执行 SQL
- 使用 `WHERE assistant_id = %s AND member_id = ANY(%s)` 查询
- 复用已有 `_fdw_context` 连接管理
- _Requirements: 1.2, 1.3_
- [ ] 4.2 编写属性测试:批量 RS 查询与逐条查询等价性
- **Property 1: 批量 RS 查询与逐条查询等价性**
- 使用 hypothesis `st.lists(st.integers(min_value=1))` 生成 member_ids
- 验证 `get_relation_index_batch` 返回与逐条 `get_relation_index` 结果完全一致
- **验证: 需求 1.4**
- [ ] 4.3 修改 `coach_service._build_top_customers()` 使用批量查询
-`for mid in member_ids` 循环替换为单次 `get_relation_index_batch` 调用
- 返回数据结构不变
- _Requirements: 1.1, 1.5_
- [ ] 4.4 修改 `coach_service.get_coach_detail()` 添加 `data_updated_at` 字段
- `top_customers` 列表中每个客户新增 `data_updated_at`(取查询时 `now()`
- _Requirements: 6.2_
- [ ] 4.5 编写单元测试:`member_ids` 为空返回空字典
- 验证边界条件:空列表不执行 SQL直接返回 `{}`
- _Requirements: 1.3_
- [ ] 5. 后端优化Task-List 页服务端过滤
- [ ] 5.1 验证并完善 `task_manager.get_task_list_v2()` SQL 层过滤
- 确认 SQL WHERE 子句已包含 `relationship_building` 任务的 RS 范围过滤
- 验证 `total` 计数与实际过滤后结果集一致
- 返回每个任务项新增 `updated_at` 字段
- _Requirements: 2.1, 2.2, 2.4, 2.5, 6.1_
- [ ] 5.2 编写属性测试RS 范围过滤
- **Property 2: relationship_building 任务 RS 范围过滤**
- 生成随机 RS 值和 rs_min/rs_max 范围
- 验证返回的 `relationship_building` 任务 RS 指数在 `(rs_min, rs_max)` 范围内
- **验证: 需求 2.2**
- [ ] 5.3 编写属性测试:分页 total 一致性
- **Property 3: 分页 total 与实际结果集一致性**
- 生成随机 page/page_size 参数
- 验证 `total` 等于遍历所有页累计的 `items` 总数
- **验证: 需求 2.4**
- [ ] 5.4 编写单元测试RS 排除列表查询失败降级
- 验证查询失败时降级为不排除任何任务
- _Requirements: 2.3_
- [ ] 6. 后端优化Task-Detail 页 ETL 查询合并
- [ ] 6.1 新增 `fdw_queries.batch_query_for_task_detail()` 函数
- 接受 `conn, site_id, assistant_id, member_id` 参数
- 单连接批量查询 `member_info``balance``rs_score`
- 每个子查询用 try/except 包裹,失败返回默认值
- 复用 `_fdw_context` 单连接模式
- _Requirements: 5.1, 5.2, 5.3_
- [ ] 6.2 编写属性测试Task-Detail 批量查询等价性
- **Property 8: Task-Detail 批量查询与独立查询等价性**
- 生成随机 member_id
- 验证 `batch_query_for_task_detail` 返回与三个独立查询结果一致
- **验证: 需求 5.4**
- [ ] 6.3 修改 `task_manager.get_task_detail()` 使用批量查询
- 将 3 个独立 ETL 查询替换为 `batch_query_for_task_detail` 单次调用
- 返回数据结构不变,新增 `updated_at` 字段
- _Requirements: 5.1, 6.3_
- [ ] 6.4 编写属性测试:接口返回包含版本时间戳
- **Property 4: 接口返回包含版本时间戳**
- 验证 `get_task_list_v2``get_task_detail``get_coach_detail` 返回数据包含有效 ISO 8601 时间戳
- **验证: 需求 3.2, 6.1, 6.2, 6.3**
- [ ] 6.5 编写单元测试:子查询独立降级
- 验证某子查询失败时返回默认值,不影响其他子查询
- _Requirements: 5.3_
- [ ] 7. 后端优化:酒水聚合 CTE 预聚合(低优先级)
- [ ] 7.1 修改 `fdw_queries.get_service_records_for_task()` 添加 CTE 分支
- 服务记录 > 50 条时使用 CTE 预聚合替代 `LEFT JOIN LATERAL`
- ≤ 50 条时保持现有方式
- _Requirements: 8.1, 8.3_
- [ ] 7.2 编写属性测试CTE 预聚合与 LEFT JOIN LATERAL 等价性
- **Property 12: CTE 预聚合与 LEFT JOIN LATERAL 等价性**
- 生成随机服务记录集合(> 50 条)
- 验证两种方式返回的 `drinks` 字段内容完全一致
- **验证: 需求 8.2**
- [ ] 8. 后端回归验证
- [ ] 8.1 运行基线快照 diff 对比
- 使用相同参数再次调用三个核心接口
- 逐字段 diff 对比优化前后返回值
- 确认 `aiAnalysis``talkingPoints` 字段未被改变
- _Requirements: 9.2, 9.3, 9.4, 10.1, 10.2, 10.3, 10.4_
- [ ] 9. 检查点 — 后端优化完成验证
- 运行 Monorepo 属性测试:`cd C:\Project\NeoZQYY && pytest tests/ -v`
- 运行后端单元测试:`cd apps/backend && pytest tests/ -v`
- 确保所有属性测试Property 1-4, 8-9, 12和单元测试全部通过
- 确保基线快照 diff 无数据偏差
- ask the user if questions arise.
- [ ] 10. 前端优化CacheStore 缓存模块
- [ ] 10.1 创建 `utils/cache-store.ts` 缓存模块
- 实现 `CacheEntry<T>` 接口(`data``updatedAt``cachedAt`
- 实现 `CacheStore` 类:`get()``set()``clear()``setTaskPreload()``getTaskPreload()`
- 缓存 key 格式:`{dataType}:{id}`
- 所有读写操作用 try/catch 包裹,异常时静默降级
- _Requirements: 3.1, 3.3, 3.4, 3.5, 3.6_
- [ ] 10.2 编写属性测试:缓存版本校验行为
- **Property 5: 缓存版本校验行为**
- 使用 fast-check `fc.string()` 生成 updatedAt 时间戳
- 验证缓存命中/过期判定逻辑
- **验证: 需求 3.3, 3.4**
- [ ] 10.3 编写属性测试:缓存 clear 后为空
- **Property 6: 缓存 clear 后为空**
- 使用 fast-check `fc.dictionary()` 生成随机缓存条目
- 验证 `clear()` 后所有 `get()` 返回 `null`
- **验证: 需求 3.6**
- [ ] 10.4 编写属性测试:预加载数据 round-trip
- **Property 7: 预加载数据 round-trip**
- 使用 fast-check `fc.record()` 生成 TaskPreloadData
- 验证 `setTaskPreload``getTaskPreload` 数据完全一致
- **验证: 需求 4.1**
- [ ] 10.5 编写单元测试CacheStore 异常降级
- 验证缓存读写异常时静默降级为网络请求
- _Requirements: 3.5_
- [ ] 11. 前端优化:跳转传参与骨架渲染
- [ ] 11.1 修改 Task-List 页跳转传参
- 点击任务卡片时调用 `cacheStore.setTaskPreload()` 写入预加载数据
- 传递字段:`customer_name``heart_score``balance``task_type``task_type_label`
- _Requirements: 4.1_
- [ ] 11.2 修改 Task-Detail 页骨架渲染
- `onLoad` 时先从 `cacheStore.getTaskPreload()` 读取预加载数据
- 有缓存:立即渲染骨架内容,同时异步请求完整详情数据
- 完整数据返回后覆盖骨架内容,实现无缝过渡
- 无缓存:回退为当前全量加载模式
- _Requirements: 4.2, 4.3, 4.4_
- [ ] 11.3 修改 Performance 页跳转传参
- 点击客户卡片跳转 Task-Detail 时,将 `customer_name``heart_score` 写入 `cacheStore`
- _Requirements: 4.5_
- [ ] 11.4 集成 `onHide` 和下拉刷新清空缓存
- 在 App 级 `onHide` 回调中调用 `cacheStore.clear()`
- 在支持下拉刷新的页面 `onPullDownRefresh` 中调用 `cacheStore.clear()`
- _Requirements: 3.6_
- [ ] 12. 前端优化:折前/折后小时数条件显示
- [ ] 12.1 实现折前/折后条件显示逻辑
- `duration_raw` 不为 null 且 `duration_raw ≠ duration` 时显示"折前 XX.Xh"标注
- `duration_raw` 为 null 或等于 `duration` 时仅显示折后小时数
- 格式化为一位小数(如"折前 2.5h"
- _Requirements: 7.1, 7.2, 7.3, 7.4_
- [ ] 12.2 编写属性测试:折前/折后条件显示逻辑
- **Property 10: 折前/折后条件显示逻辑**
- 使用 fast-check `fc.float()` 生成 duration 和 duration_raw
- 验证显示/隐藏"折前"标注的条件判断
- **验证: 需求 7.1, 7.2, 7.3**
- [ ] 12.3 编写属性测试:折前小时数格式化
- **Property 11: 折前小时数格式化**
- 使用 fast-check `fc.float({min: 0, max: 1000})`
- 验证输出恰好一位小数且 `parseFloat(formatted) ≈ round(hours, 1)`
- **验证: 需求 7.4**
- [ ] 13. 检查点 — 前端优化完成验证
- 运行前端测试:`cd apps/miniprogram && npm test`
- 确保所有属性测试Property 5-7, 10-11和单元测试全部通过
- ask the user if questions arise.
- [ ] 14. 前后端联调与集成验证
- [ ] 14.1 启动后端服务,验证各端点完整请求-响应链路
- 使用真实 FDW 连接验证 SQL 查询正确性
- 验证 JSON 响应结构与 Schema 定义一致camelCase 序列化)
- 验证 `updated_at` / `data_updated_at` 字段在响应中正确返回
- _Requirements: 6.1, 6.2, 6.3_
- [ ] 14.2 前端联调验证
- 确认前端页面能正确调用优化后的 API 并渲染数据
- 验证缓存命中/过期场景下前端行为正确
- 验证空数据/降级场景下前端不崩溃
- _Requirements: 3.3, 3.4, 3.5, 4.2, 4.3, 4.4_
- [ ] 15. 数据库变更审计与 DDL 合并
- [ ] 15.1 审计本次实现中对数据库的所有改动
- 检查新增字段(`updated_at`)、新增触发器(`trg_coach_tasks_updated_at`
- 确认无遗漏的 DDL 变更
- _Requirements: 6.4, 6.5_
- [ ] 15.2 执行迁移脚本到测试库
- 验证新字段和触发器已正确创建(使用 BD 手册中的验证 SQL
- _Requirements: 6.4_
- [ ] 15.3 合并到主 DDL 基线文件
- 业务库 → `docs/database/ddl/zqyy_app__biz.sql`
- _Requirements: 6.5_
- [ ] 15.4 编写回滚脚本(逆序 DROP TRIGGER / DROP FUNCTION / DROP COLUMN
- _Requirements: 6.5_
- [ ] 16. BD 手册更新
- [ ] 16.1 更新 BD 手册 `docs/database/BD_Manual_coach_tasks.md`
- 新增 `updated_at` 字段明细、触发器说明
- 包含:字段明细、约束与索引、验证 SQL≥3 条)、兼容性影响、回滚策略
- 记录变更原因和影响范围
- _Requirements: 6.4, 6.5_
- [ ] 17. 文档同步更新
- [ ] 17.1 更新后端 API 参考文档
-`apps/backend/docs/API-REFERENCE.md` 更新接口返回结构变更(`updated_at``data_updated_at` 字段)
- 更新 `apps/backend/README.md` 相关模块摘要
- _Requirements: 6.1, 6.2, 6.3_
- [ ] 17.2 更新小程序接口契约文档
-`docs/miniprogram-dev/API-contract.md` 更新接口返回结构
- _Requirements: 3.2, 6.1, 6.2, 6.3_
- [ ] 17.3 更新文档地图
-`docs/DOCUMENTATION-MAP.md` 新增本次变更相关条目
- _Requirements: 6.5_
- [ ] 18. 最终检查点 — 全量验证
- 运行 Monorepo 属性测试:`cd C:\Project\NeoZQYY && pytest tests/ -v`
- 运行后端单元测试:`cd apps/backend && pytest tests/ -v`
- 运行前端测试:`cd apps/miniprogram && npm test`
- 确保所有属性测试Property 1-12和单元测试全部通过
- 确保基线快照 diff 无数据偏差
- 确保 DDL 迁移已合并到主基线
- 确保 BD 手册已同步更新
- 确保 API 文档、后端 README、文档地图均已更新
- 确保前端页面连接真实后端运行正常
- ask the user if questions arise.
## 备注
- 标记 `*` 的子任务为可选(属性测试/单元测试),可跳过以加速 MVP
- 每个任务引用了具体的需求编号以确保可追溯性
- 属性测试验证通用正确性属性Property 1-12单元测试验证具体边界条件
- 检查点任务确保增量验证,避免问题累积
- 需求 10AI 分析数据校对预留)通过基线快照覆盖(任务 1.1 和 8.1),无需独立实施任务
## 硬性规定:属性测试执行规范
1. **分组执行**:每次只运行单个属性测试函数(`-k "test_property_N"`),禁止一次性跑全部
2. **后台 CLI**:使用 `controlPwshProcess` 启动测试,`getProcessOutput` 轮询结果(间隔 ≥ 15 秒)
3. **确认完成再继续**:必须在输出中看到明确的 `passed`/`failed`/`error` 后才可进入下一步,禁止"启动测试后立即继续编码"
4. **超时保护**`executePwsh``timeout` 设为预估时间 3 倍(最少 120 秒hypothesis `max_examples=100`
5. **详见**`.kiro/steering/testing-env.md` 属性测试执行规范章节
## 硬性规定:前后端联调期间的效率优化框架
当用户在联调过程中提及"效率优化"或"本页已完成,进行效率优化"时,所有调试和改动必须在本 spec 的框架下进行:
1. **数据正确性优先**:任何优化改动前先确认基线快照存在(任务 1改动后跑 diff 验证(任务 8
2. **AI 数据不动**`aiAnalysis``talkingPoints` 的查询逻辑和返回结构保持不变(需求 10后期单独校对
3. **联调中的样式/交互调整**:属于联调范畴,不受效率优化 spec 约束,但改动不得破坏优化后的数据链路
4. **联调中发现的数据问题**:如果是优化引入的偏差,回退到基线快照对比定位;如果是原有问题,单独记录不混入本 spec