Files
Neo-ZQYY/docs/_overview/wave1-findings/F1-6-tasks.md
Neo c446920c9e feat(backend): F1-6 sprint2 #5 累计 GMV 加入 sandbox_replay (门店级)
Sprint 2 收尾指标:门店级累计 GMV(与 #1-#4 会员级粒度不同),新建
sandbox_replay/finance_replay.py 模块。无原 fdw_queries.get_total_gmv
函数(0 现有调用方),不写 thin wrapper(spec §5.5 决策原则)。

数据源 dws_finance_daily_summary.gross_amount(门店日度财务汇总,daily 累计)。
SQL 模式 SUM(gross_amount) WHERE stat_date <= ctx.business_date,与 #1-#4
取最新单行不同,是多行累计 SUM。SQL 层 COALESCE(SUM(...), 0) 兜底,无数据
返回 Decimal('0')(开店前累计 GMV = 0,业务语义)。

口径 gross_amount = table_fee + goods + assistant_pd + assistant_cx,**不含
electricity_money**(与会员级 items_sum 略有差异,docstring 明确防止交叉验证)。

双口径数值验证 PASS(直接 Python,site=2790685415443269 朗朗桌球):
- 4a live(today=2026-05-05): ¥5,725,837.51
- 4b sandbox=2026-04-20: ¥5,653,063.37(差异 ¥72,774,即 4-21~4-27 七天合计)

新增防御性回归测试 test_get_total_gmv_no_member_ids_param 阻断未来误加
member_id 参数(门店级粒度强约束)。

unit test sprint1+sprint2 累计 28/28 PASS,无回归。

Sprint 2 收尾(4 项迁移 + #3 推迟 Sprint 3 等 ETL 配合)。

详见 docs/audit/changes/2026-05-06__f1_6_sprint2_total_gmv.md

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

156 lines
7.6 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.
# F1-6 沙箱时光机阶段 B 任务清单
> 创建日期:2026-05-05 (F1-5b 收尾后启动)
> 完整模块 spec:[`docs/_overview/sandbox-replay-engine-spec.md`](../sandbox-replay-engine-spec.md)
> 上游:F1-5b 全部完成(commit `5d4da0a`)
>
> **状态**:Sprint 1 已启动并完成,Sprint 2-4 待启动
## 一、阶段 B 总览
按 sandbox-replay-engine-spec 阶段 B 拆 4 个 sprint:
| Sprint | 范围 | 工作量 | 状态 |
|--------|------|------|------|
| **Sprint 1** | 框架(sandbox_replay 模块 + runtime_aware decorator) + 1 个试点指标(距上次到店天数迁移) | M ~ 4-5h | ✅ 完成(2026-05-05) |
| **Sprint 2** | 5 个会员相关 P1 指标(60d 消费 / 累计消费总额 / 累计交易笔数 / 储值卡余额 / 累计 GMV) | M ~ 4h | ✅ 完成(2026-05-06,4 项迁移 + #3 推迟 Sprint 3) |
| Sprint 3 | 5 个助教/门店 P1 + **MP-2 完整**(daily salary 含 ETL 改造)+ Sprint 2 推迟的 #3(ETL 新增 `total_open_table_count`)| L ~ 9-11h | ⏳ 待启动 |
| Sprint 4 | 5 个 P2 指标(RS 重算 / 客户黏性 / 任务完成率 / Excel 修正 / 月度新增流失) | M-L ~ 6-8h | ⏳ 待启动 |
## 二、Sprint 1 收口(2026-05-05)
### 范围
1. ✅ 建立 `apps/backend/app/services/sandbox_replay/` 模块脚手架
2. ✅ 实现 `@runtime_aware(metric=...)` decorator(自动注入 RuntimeContext)
3. ✅ 试点迁移:`get_last_visit_days`(距上次到店天数,P1-4)
- `consumption_replay.py` 新实现
- `fdw_queries.get_last_visit_days` 改 thin wrapper(兼容 75+ 现有调用)
4. ✅ Unit test 10/10 PASS
5. ✅ MCP 端到端 4a/4b 双口径 PASS
### 关键 bug 发现 + 修复
**bug**:`@trace_service` + `@runtime_aware` 嵌套时,trace_service 的 `_build_params_dict``inspect.signature(func).bind(*args, **kwargs)` 做参数 redact。`functools.wraps` 默认设置 `__wrapped__``inspect.signature` 追溯到原函数(含 keyword-only 必传 ctx 参数),导致 sig.bind 抛 `TypeError: missing a required argument: 'ctx'`
只在 FastAPI 请求 + 有活跃 TraceContext 时触发(直接 Python 调用 get_current_trace 返回 None,跳过 _build_params_dict,不复现)。
**修复**:`runtime_aware` 的 wrapper 不用 `functools.wraps`,改用手动复制 `__name__/__qualname__/__module__/__doc__`,**不设 `__wrapped__`**,让 `inspect.signature` 看到 wrapper 自身 `(*args, **kwargs)` 签名,sig.bind 永远成功。
### 双口径验证证据(关键样本)
**目标 member**: 2799207087163141(黄先生)
| 维度 | 4a live (today=2026-05-05) | 4b sandbox=2026-04-20 |
|------|---------------------------|----------------------|
| stat_date 数据 | 2026-05-01 | 2026-04-15(walkthrough 测试快照) |
| last_consume_date | 2026-04-03 | 2026-03-20 |
| daysSinceVisit 计算 | 32 = 05-05 - 04-03 | **31 = 04-20 - 03-20** |
| API 返回 | 32 ✓ | 31 ✓ |
测试快照已清理,sandbox 已切回 live。
### 走查脚本
`_DEL/walkthrough_f1_6/`:
- `step_sprint1_probe_member.py` — 找有 last_consume 的会员
- `step_sprint1_check_view.py` — 视图层 RLS 行为
- `step_sprint1_direct_call.py` — 直接调 sandbox_replay 函数
- `step_sprint1_repro_etl_conn.py` — etl_conn 复用模式
- `step_sprint1_call_service.py` — 直接调 customer_service.get_customer_detail
- `step_sprint1_4b_probe.py` — sandbox=4-20 视图行为
- `step_sprint1_4b_find_member.py` — 找 sandbox 下有数据的 member
- `step_sprint1_4b_stat_dist.py` — stat_date 分布
- `step_sprint1_4b_seed_data.py` — 插测试快照
- `step_sprint1_cleanup.py` — 清理 + 切回 live
### Sprint 1 commit
`feat(backend): F1-6 sprint1 sandbox_replay 模块脚手架 + get_last_visit_days 迁移试点`
---
## 三、Sprint 2 范围(进行中)
启动顺序按方案 A:从已有 `consumption_replay.py` 模块扩展开始,新模块靠后。
### 5 个会员相关 P1 指标
| # | 指标 | 当前实现位置 | 目标迁移到 | 复杂度 | 状态 |
|---|------|-------------|----------|------|------|
| 1 | 60 天消费 | `fdw_queries.get_consumption_60d` | `sandbox_replay/consumption_replay.py`(扩展) | S | ✅ 2026-05-06(thin wrapper)|
| 2 | 累计消费总额 | (无,新增) | `sandbox_replay/consumption_replay.py`(扩展) | S | ✅ 2026-05-06(无 wrapper,0 调用方)|
| 3 | 累计交易笔数 | DWS 现有 `total_visit_count` 实算 `COUNT(settle_type IN (1,3))` 不符开台次数语义,需 ETL 新增 `total_open_table_count` | `sandbox_replay/consumption_replay.py`(扩展) | S | ⏸️ **推迟 Sprint 3**(ETL 配合,详见 [架构演进 backlog](../architecture-evolution-backlog.md))|
| 4 | 会员储值卡余额 | `fdw_queries.get_member_balance` | `sandbox_replay/balance_replay.py`(新建) | S | ✅ 2026-05-06(thin wrapper,SCD2 时光机)|
| 5 | 累计 GMV | `dws_finance_daily_summary.gross_amount`(门店级,与现有"区间 GMV"语义不同) | `sandbox_replay/finance_replay.py`(新建) | S | ✅ 2026-05-06(无 wrapper,门店级 SUM)|
### Sprint 2 实施模式
所有指标走 `@runtime_aware` decorator + `app.v_dws_*` 视图查询。每个指标:
- 1 个新函数 to sandbox_replay/
- 1 个 thin wrapper in fdw_queries.py(兼容)
- 单元测试(本地不入仓)+ MCP 双口径走查(navigate_to + snapshot + screenshot,**含 UI 实地验证**)
- 审计 + 单独 commit
### Sprint 2 commit
- #1 60d 消费 — commit `d418621`(2026-05-06)
- #2 累计消费总额 — commit `32716bc`(2026-05-06)
- #3 累计交易笔数 — **推迟 Sprint 3**(ETL 配合新增 `total_open_table_count`,详见 [架构演进 backlog](../architecture-evolution-backlog.md) 第 3 项)
- #4 储值卡余额 — commit `7b1cfad`(2026-05-06,SCD2 时光机)
- #5 累计 GMV — `feat(backend): F1-6 sprint2 #5 累计 GMV 加入 sandbox_replay (门店级)`(待提交)
### 估算
5 指标 × 30-50min = 3-4h(#1 实际 ~ 40min)
---
## 四、Sprint 3 范围(待启动)
### 5 个助教/门店相关 P1 + MP-2 完整
| # | 指标 | 备注 |
|---|------|------|
| 6 | 累计服务客户数(助教) | dws_assistant_customer_stats daily |
| 7 | 助教等级 | dws_assistant_daily_detail.assistant_level_code |
| 8 | 月度课时(助教) | dws_assistant_daily_detail.base_hours 聚合 |
| 9 | 月度计费金额(助教) | dws_assistant_daily_detail.total_ledger_amount 聚合 |
| 10 | 月度新增/流失会员 | dws_member_*_summary daily |
| **MP-2 完整 daily salary** | board-coach 月度面板 | **需 ETL 团队配合**:新建 dws_assistant_daily_salary 表 + ETL Excel 上传 UI 改造 |
### Sprint 3 估算
5 指标 ~ 3-4h + MP-2 完整 5-6h(含 ETL 改造) = **L ~ 8-10h**
---
## 五、Sprint 4 范围(待启动)
### 5 个 P2 算法重算指标
| # | 指标 | 难点 |
|---|------|------|
| 11 | RS 关系指数 | 算法窗口期 + 衰减函数,daily 重算需重做累计 |
| 12 | 客户黏性指数 | 类似 RS,有时间衰减 |
| 13 | 任务完成率(coach_tasks) | 需补 dws daily 聚合视图 |
| 14 | Excel 修正(扣款/奖励/支出/收入) | 依赖 effective_date(F1-5b prep 已就位),后端 SQL 加截断 |
| 15 | 月度新增/流失会员(算法版) | dws_member_lifecycle 重算 |
### Sprint 4 估算
M-L ~ 6-8h
---
## 六、阶段 C(F1-7+ 远期)
详见 sandbox-replay-engine-spec.md §11.5:
- 3 个 P3 指标(门店等级 / 助教星级 / 累计 KPI)
- `biz.sandbox_audit_log` 用户行为审计表
- AI app8_consolidate prompt audit
---
## 七、关联
- 完整模块 spec:`docs/_overview/sandbox-replay-engine-spec.md`
- F1-5b 任务清单:`docs/_overview/wave1-findings/F1-5b-tasks.md`
- F1-5b MP-2 prep 审计:`docs/audit/changes/2026-05-05__wave1_f1_5b_mp2_prep.md`
- P20 SPEC §11.4 + §11.5(F1-6 待办登记):`docs/prd/specs/P20-runtime-context-sandbox.md`
- Sprint 1 审计:`docs/audit/changes/2026-05-05__f1_6_sprint1_sandbox_replay_kickoff.md`