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>
55 lines
2.3 KiB
Python
55 lines
2.3 KiB
Python
"""沙箱时光机重算引擎 (Sandbox Replay Engine)
|
|
|
|
F1-6 沙箱时光机阶段 B 启动模块。
|
|
|
|
设计意图:
|
|
P20 沙箱(`runtime_mode='sandbox' + sandbox_business_date`)的"时光机"语义
|
|
要求所有业务指标按 sandbox_business_date 重新累计/截断,而非展示 ETL
|
|
跑批的最新累计。F1-5a/F1-5b 已建立基础设施(RuntimeContext + app 视图
|
|
业务日上界),本模块统一所有"runtime-aware"业务读取路径。
|
|
|
|
完整 spec:
|
|
docs/_overview/sandbox-replay-engine-spec.md
|
|
|
|
模块结构(随 sprint 推进逐步填充):
|
|
sandbox_replay/
|
|
├── __init__.py # 本文件,re-export 主要 API
|
|
├── _decorator.py # @runtime_aware decorator
|
|
├── consumption_replay.py # 消费/到店/累计交易(P1-2/3/4/12/13)
|
|
├── balance_replay.py # 会员余额(P1-1)— sprint 2
|
|
├── assistant_metrics_replay.py # 助教课时/收入/客户(P1-5/6/7/8)— sprint 3
|
|
├── salary_replay.py # MP-2 完整 daily salary(P2-17)— sprint 3
|
|
├── adjustments_replay.py # Excel 修正截断(P2-19)— sprint 3
|
|
├── tasks_replay.py # 任务完成率(P2-18)— sprint 4
|
|
├── rs_replay.py # RS 算法重算(P2-15)— sprint 4
|
|
└── intimacy_replay.py # 客户黏性(P2-16)— sprint 4
|
|
|
|
接口契约(sprint 1):
|
|
@runtime_aware(metric='last_visit_days')
|
|
def get_last_visit_days(conn, site_id: int, member_ids: list[int]) -> dict[int, int | None]:
|
|
'''函数体内自动接收 RuntimeContext,可读 ctx.is_sandbox / ctx.business_date。'''
|
|
...
|
|
"""
|
|
|
|
from app.services.sandbox_replay._decorator import runtime_aware
|
|
from app.services.sandbox_replay.balance_replay import (
|
|
get_member_balance as get_member_balance_replay,
|
|
)
|
|
from app.services.sandbox_replay.consumption_replay import (
|
|
get_consumption_60d as get_consumption_60d_replay,
|
|
get_last_visit_days as get_last_visit_days_replay,
|
|
get_total_consume_amount as get_total_consume_amount_replay,
|
|
)
|
|
from app.services.sandbox_replay.finance_replay import (
|
|
get_total_gmv as get_total_gmv_replay,
|
|
)
|
|
|
|
__all__ = [
|
|
"runtime_aware",
|
|
"get_last_visit_days_replay",
|
|
"get_consumption_60d_replay",
|
|
"get_total_consume_amount_replay",
|
|
"get_member_balance_replay",
|
|
"get_total_gmv_replay",
|
|
]
|