Files
Neo-ZQYY/docs/_overview/sandbox-replay-engine-spec.md
Neo 1e803e23cd feat(db,docs): F1-5b MP-2 prep + 沙箱时光机模块 spec (W1)
MP-2 经 4 轮调研 + Neo 反馈,采纳方案 C(推迟到 F1-6 沙箱时光机阶段 B):
- 第 1 轮原方案 D(双口径) → 第 2 轮 D'(单口径)
- 第 3 轮 Neo 架构纠正:不读 DWD,走 Core/DWS/app
- 第 4 轮 DWS 视图靠谱性审计:dws_assistant_daily_detail 是计费明细
  (ledger_amount),不是助教工资(gross_salary 需等级时薪 + 抽成
  + 罚分),且缺 effective_hours / work_days
- 结论:MP-2 真正实施需要新建 dws_assistant_daily_salary 表(ETL
  改造),跟其他 14 个 P1 指标一起做更高效 → 推迟到 F1-6

本次 Wave B 只做 prep:DB schema + 模块 spec + tasks.md 状态调整。

DB 迁移(zqyy_app):
- db/zqyy_app/migrations/20260505__add_effective_date_for_excel_adjustments.sql
- 3 张 Excel 暂存表(全空,Neo 确认尚无 Excel 上传)ADD COLUMN
  effective_date DATE NOT NULL(无 DEFAULT,强制未来 Excel 上传必须带):
  * biz.salary_adjustments(助教薪资扣款/奖励)
  * biz.stg_finance_expense(月度支出)
  * biz.stg_platform_income(平台结算收入)
- 3 个复合索引 (site_id, effective_date) 支持后续 daily 截断查询
- biz.stg_recharge_commission 已有 recharge_date,无需改造

测试库执行 + 5/5 校验 PASS:
- 字段存在(NOT NULL DATE 无 default)
- 复合索引存在 + 列序正确
- 字段注释含 'F1-5b MP-2 prep'
- INSERT 不带 effective_date 触发 NotNullViolation

docs/database/ 同步:
- docs/database/changes/2026-05-05__add_effective_date_for_excel_adjustments.md
  完整变更说明 + 兼容性 + 回滚 + 5 条校验 SQL + 正式库执行说明

沙箱时光机模块 spec(主干任务排期登记):
- docs/_overview/sandbox-replay-engine-spec.md
- 22 个相关指标分 P1/P2/P3 优先级:
  * P1 14 项(daily 视图已有,后端切换)
  * P2 5 项(算法重算,含 MP-2 完整 daily salary)
  * P3 3 项(状态算法 + sandbox_audit_log 用户行为)
- 4 阶段实施路径:
  * 阶段 0(本次 prep)
  * 阶段 A(F1-5a/b 已完成)
  * 阶段 B(F1-6,2-3 周)— MP-2 真正实施在此
  * 阶段 C(F1-7+,1-2 周)
- sandbox_replay 模块结构 + runtime_aware decorator 接口契约
- 性能 + 测试 + 前置依赖清单

F1-5b-tasks.md 状态调整:
- §4.3 顺序 15:MP-2 从"待开始/C4" → "延期 F1-6"
- §6 进度表 MP-2 行同步标"延期 F1-6 + 改方向说明"
- 关联到 mp2_prep.md 审计

业务影响:
- board-coach sandbox 行为暂遗留(F1-6 解决)
- 旧 Excel 模板上传将因 NOT NULL 失败,需 F1-6 同期 ETL UI 改造 +
  操作员培训
- 跨页面已 audit:board-finance / customer-records / coach-service-records
  / customer-service-records 等已合规(F1-5b A1/A3 + MP-1/3/5 收益)

审计:docs/audit/changes/2026-05-05__wave1_f1_5b_mp2_prep.md

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

9.0 KiB

沙箱时光机引擎 (Sandbox Replay Engine) — 模块 Spec

版本:v1.0 · 创建日期:2026-05-05 决策来源:F1-5b Wave B MP-2 调研收尾(Neo 同意分阶段实现,加入主干任务排期) 主要落地依赖:F1-5a runtime_context 框架 + F1-5b 业务日上界裁剪基础设施

状态:Spec 阶段(未启动实施)

一、设计意图

P20 沙箱(runtime_mode='sandbox' + sandbox_business_date)的核心承诺是 "设定某个历史日期 X 后,系统所有数据/统计/AI 生成内容呈现的状态都是 X 那天的样子",即"时光机"语义。

当前 F1-5a/F1-5b 已建立基础设施,但业务读取层只覆盖了"daily 累计型"指标(财务流水 / 服务记录),未覆盖"月度结算型 / 状态算法型 / Excel 修正型"指标。沙箱设到 4-20 时,部分页面仍能"看见未来"或显示已知最终结果,违反时光机语义。

本模块旨在统一所有业务读取按 sandbox_business_date 重算,达成完整时光机体验。

二、当前已就位的基础设施(继承)

已做 Wave / Commit
RuntimeContext 框架 site_runtime_context 表 + apply_runtime_session_vars() F1-5a 421e193
数据写入隔离 ai_run_logs / coach_tasks / 各业务写入表带 runtime_mode + sandbox_instance_id F1-5a + F1-5b A1 af02446
app 视图业务日上界裁剪 39 个 app.v_* 视图加 WHERE stat_date <= business_date_now()(2026-05-02 迁移) 2026-05-02 ETL 迁移
后端读取(daily 累计型指标) board-finance / customer-detail / customer-records / coach-service-records 等 F1-5b MP-1/3/5
后端读取(权限路由) manager 角色权限隔离 F1-5b BE-1
admin-web sandbox 透出 UI-1/2/3/4/5 全套 runtime 字段 + 提示条 + 全局徽章 F1-5b Wave A/B
Excel 修正表 schema 准备 salary_adjustments / stg_finance_expense / stg_platform_income 加 effective_date NOT NULL F1-5b MP-2 prep(本次)

三、缺失能力清单(本模块覆盖范围)

22 个指标按复杂度 + 优先级分:

P1 — Daily 视图已有,后端 service 切换即可(14 项,S 复杂度)

# 指标 当前数据源 目标 daily 视图 重算逻辑
1 会员储值卡余额 dws_member_balance_snapshot(状态) dws_member_balance_change daily 累计 SUM(收入-支出) WHERE date<=B
2 60 天消费 dws_member_consumption_summary dws_member_consumption_daily SUM 60 day window 终点为 B
3 累计消费总额 同上 同上 SUM all WHERE date<=B
4 距上次到店天数 状态字段 dwd 消费记录 B - MAX(visit_date WHERE<=B)
5 累计服务客户数(助教) dws_assistant_customer_stats daily 累计 COUNT DISTINCT member WHERE date<=B
6 助教等级 dws_assistant_daily_detail.assistant_level_code 同表 SELECT WHERE stat_date=B
7 月度课时(助教) dws_assistant_daily_detail.base_hours 等 同表月度聚合 SUM WHERE month_of(B) AND stat_date<=B
8 月度计费金额(助教) dws_assistant_daily_detail.total_ledger_amount 同上 SUM WHERE month_of(B) AND stat_date<=B
9 门店月度财务 dws_finance_daily_summary 同表(已实现 MP-1) ✓ 已完成
10 月度新增会员 dws_member_*_summary dws_member_daily COUNT WHERE join_date<=B AND month_of(B)
11 月度流失会员 同上 同上 COUNT WHERE last_visit<B-30 AND month_of(B)
12 累计交易笔数 dws_order_summary daily COUNT WHERE date<=B
13 累计 GMV dws_finance_daily_summary.gross_amount 同上 SUM WHERE date<=B
14 AI 缓存命中率 sandbox_instance_id 隔离 F1-5a 已覆盖 ✓ 已完成

P2 — 算法重算(5 项,M 复杂度)

# 指标 复杂度 难点
15 关系指数 RS M RS 涉及窗口期 + 衰减函数,daily 重算需要重做累计逻辑
16 客户黏性指数 M 类似 RS,有时间衰减
17 助教月薪(完整 daily salary 含罚分) M-L MP-2 真正实施在此,需要新建 dws_assistant_daily_salary 表
18 任务完成率(coach_tasks 累计) M 需 coach_tasks 加 dws 层 daily 聚合视图
19 Excel 修正(扣款/奖励/支出/收入) M 依赖 effective_date(F1-5b prep 已做),后端 SQL 加截断

P3 — 状态算法依赖累计(3 项,L 复杂度)

# 指标 难点
20 门店等级评级 评级算法依赖累计 KPI 反推,需要重新建模
21 助教星级 类似门店等级,涉及多月 KPI 综合
22 用户操作行为日志 完全未覆盖(F1-5a 仅写入隔离,没有"用户行为审计表");需新建 sandbox_audit_log

四、实施分阶段(主干任务排期)

阶段 内容 工作量 时机
阶段 0(F1-5b prep,本次) Excel 修正表加 effective_date schema + 沙箱时光机 spec 文档 1.5h 本次 F1-5b Wave B 完成
阶段 A(F1-5b 已完成) RuntimeContext 框架 + app 视图 business_date 上界 + daily 累计型指标 9 项 F1-5a + F1-5b 已完成
阶段 B(F1-6) 14 个 P1 指标 service 层切换 daily 累计 + 5 个 P2 指标(含 MP-2 完整 daily salary) 2-3 周 F1-6
阶段 C(F1-7+) 3 个 P3 指标 + sandbox_audit_log 用户行为审计 1-2 周 F1-7 长期

五、阶段 B 实施模式建议

5.1 sandbox_replay 模块结构

apps/backend/app/services/sandbox_replay/
├── __init__.py                # runtime_aware decorator
├── balance_replay.py          # 会员余额(P1-1)
├── consumption_replay.py      # 消费累计(P1-2/3/4/12/13)
├── assistant_metrics_replay.py # 助教课时/收入/客户(P1-5/6/7/8)
├── member_lifecycle_replay.py  # 月度新增/流失(P1-10/11)
├── salary_replay.py            # MP-2 完整 daily salary(P2-17,需新 dws 表)
├── adjustments_replay.py       # Excel 修正截断(P2-19)
├── tasks_replay.py             # 任务完成率(P2-18)
├── rs_replay.py                # RS 算法重算(P2-15)
└── intimacy_replay.py          # 客户黏性(P2-16)

5.2 接口契约(runtime_aware decorator)

@runtime_aware(metric='member_balance')
def get_member_balance(site_id: int, member_id: int) -> Decimal:
    """根据 RuntimeContext 自动选 live / sandbox 路径。"""
    # 实现内会判断:
    # - if runtime_ctx.is_sandbox: 调 balance_replay.partial(business_date)
    # - else: 调 dws_query.live_balance()
    pass

5.3 测试模式

每个 replay 模块配套 unit test(BE-3 / T3 模式):

  • mock get_runtime_context 返回 live / sandbox 两路
  • 断言 SQL 包含 daily 累计 + business_date 截断
  • 部分 integration test 用真实测试库验证数据一致性

5.4 性能考虑

  • daily 累计 SQL 比 monthly snapshot 慢(SUM 多行 vs 单行查)
  • 建议:sandbox 模式下接受性能折衷;live 模式仍走原 dws 月度路径
  • 必要时可做 in-memory cache(business_date 不变时缓存命中)

六、阶段 B 前置依赖清单

依赖 来源 状态
RuntimeContext + business_date F1-5a ✓ 已完成
app 视图 daily 上界裁剪 2026-05-02 迁移 ✓ 已完成
Excel 修正表 effective_date 字段 F1-5b MP-2 prep(本次) ✓ 本次完成
ETL Excel 上传 UI 支持 effective_date 列 F1-6 阶段 B 内 待做
dws_assistant_daily_salary 表/视图 F1-6 阶段 B 内(MP-2 真正实施) 待做
dws_member_daily 系列视图(部分缺失) F1-6 阶段 B 内 待做

七、阶段 C 远期目标

sandbox_audit_log(用户行为审计)

业务目标:沙箱模式下用户的所有操作都记录,切回 live 后可追溯,支持"沙箱演练复盘"场景。

表设计草案:

CREATE TABLE biz.sandbox_audit_log (
    id BIGSERIAL PRIMARY KEY,
    site_id BIGINT NOT NULL,
    sandbox_instance_id VARCHAR(100) NOT NULL,
    user_id BIGINT NOT NULL,
    action_type VARCHAR(50) NOT NULL,  -- 'view' / 'trigger_ai' / 'modify' / ...
    page_path VARCHAR(200),
    payload JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

写入入口:统一 middleware 或 decorator 拦截 sandbox 模式请求。

八、关联

  • F1-5a 主体审计:docs/audit/changes/2026-05-05__wave1_f1_5a_sandbox_batch_run.md
  • F1-5a 走查报告:docs/audit/changes/2026-05-05__wave1_f1_5a_backend_walkthrough.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:docs/prd/specs/P20-runtime-context-sandbox.md
  • 业务日上界 ETL 迁移:db/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql

九、决策路径(Owner Approval)

  • 2026-05-05 Neo 决策:

    "沙箱'全数据时光机'模块可行性 — 同意,分阶段实现,往主干任务排期中增加。"

  • 本 spec 状态:已 commit,等待 F1-6 启动。F1-6 启动时本 spec 作为阶段 B 实施依据。