Files
Neo-ZQYY/docs/database/changes/2026-05-02__sandbox_complete_refactor.md
Neo caf179a5da feat: 2026-04-15~05-02 累积变更基线 — AI 重构 + Runtime Context + DWS 修复
涵盖(每条对应已存的审计记录):
- AI 模块拆分:apps/backend/app/ai/apps -> prompts/(8 个 APP + app2a 派生)
  audit: 2026-04-20__ai-module-complete.md
- admin-web AI 管理套件:AIDashboard / AIOperations / AIRunLogs / AITriggers / TriggerManager
  audit: 2026-04-21__admin-web-ai-management-suite.md
- App2 财务洞察 prompt v3 -> v5.1 + 小程序 AI 接入(chat / board-finance)
  audit: 2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md
- App2 prewarm 全过滤器 + AI 触发器 cron reschedule
  audit: 2026-04-21__app2-finance-prewarm-all-filters.md
  migration: 20260420_ai_trigger_jobs_and_app2_prewarm.sql / 20260421_app2_prewarm_cron_reschedule.sql
- AppType 联合类型对齐 + adminAiAppTypes.test.ts
  audit: 2026-04-30__admin_web_ai_app_type_alignment.md
- DashScope tokens_used 提取修复
  audit: 2026-04-30__backend_dashscope_tokens_used_extraction.md
- App3 线索完整详情 prompt
  audit: 2026-05-01__backend_app3_full_detail_prompt.md
- Runtime Context 沙箱(5-1~5-2 主线):
  - 后端 schema/service + admin_runtime_context / xcx_runtime_clock 两个 router
  - admin-web RuntimeContext.tsx + miniprogram runtime-clock.ts
  - migration: 20260501__runtime_context_sandbox.sql
  - tools/db/verify_admin_web_sandbox.py + verify_sandbox_end_to_end.py
  - database/changes: 7 份 sandbox_* 验证报告
- 飞球 DWS 修复:finance_area_daily 区域汇总 + task_engine 调整
  + RLS 视图业务日上界(migration 20260502 + scripts/ops/gen_rls_business_date_migration.py)

合规:
- .gitignore 启用 tmp/ 排除
- 不入仓:apps/etl/connectors/feiqiu/.env(API_TOKEN secret,本地修改保留)

待验证清单:
- docs/audit/changes/2026-05-04__cumulative_baseline_pending_verification.md
  每个主题的功能完整性 / 上线验证几乎都未收口,按优先级 P0~P3 逐一处理
2026-05-04 02:30:19 +08:00

9.7 KiB
Raw Blame History

2026-05-02 沙箱「不看未来」彻底改造A+B+C 全做)

目标

让 sandbox 真正模拟"设定历史日 sandbox_date 当时所有数据状态"—— 后台读取层、AI prompts、小程序业务看板/绩效/客户/任务页全部按 business_date 截断, 不再读取 sandbox_date 之后的真实生产数据。

端的归类(重要更正 2026-05-02

  • 小程序 才是业务看板(board-finance / board-customer / board-coach)和绩效/客户/任务页面所在, 是沙箱「不看未来」的主要受益方。
  • admin-web 是开发/运维向,不展示业务看板;沙箱在它这边主要表现为 RuntimeContext 开关、 AIDashboard / AIOperations / AIRunLogs / TaskManager / TriggerManager 等管理页能看到 sandbox 实例下的 AI 调用、任务写入与触发记录是隔离的(但 AI 计费/调度时间仍按真实系统时间,不受沙箱影响)。
  • tenant-admin 几乎不涉及业务数据展示,本轮基本不在沙箱范围。

总览:三层方案

范围 方法 状态
A 文档/UI admin-web、BD_Manual 顶部 Alert + 路线章节,提示"读取层修复进行中"
B 应用层 backend service / AI prompts & fetchers / fdw_queries / 小程序 时间锚替换为 RuntimeContext.business_date / business_nowSQL 补上界
C 数据层 etl_feiqiu app schema RLS 视图 引入 GUC app.current_business_date + app.business_date_now() 函数 + 关键视图 WHERE 上界

关键改动

A 层

  • apps/admin-web/src/pages/RuntimeContext.tsx — 顶部 Alert 增加"读取层修复进行中"+ plan 链接。
  • docs/database/BD_Manual_runtime_context_sandbox.md — 第 7 节新增"读取层不看未来路线"。

B 层 (后端)

  • apps/backend/app/services/runtime_context.py 新增 helpers
    • as_runtime_year_month_param(site_id) -> 'YYYY-MM'
    • as_runtime_business_now_str(site_id, fmt) -> str
    • business_date_upper_bound_sql(site_id, column, alias, cast) 返回 SQL 片段
    • apply_runtime_session_vars(conn, ctx | site_id) 设置 GUCC 层基础)
  • AI promptsapp3/4/5/6/7 的 current_time 改用 as_runtime_business_now_str,不再 datetime.now()
  • AI data_fetchers
    • member_data._query_consumption_records / _query_visit_info 接受 ref_date,所有窗口加业务日上界。
    • assistant_data._fetch_assistant_info_sync / _fetch_service_history_sync 用业务日。
    • page_context._text_board_finance/customer/coach/customer_service_records 全部上界化。
    • 所有直连 ETL 库的 cursor 在 SET LOCAL app.current_site_id 之后再下发 app.current_business_date,供 RLS 视图 GUC 读取。
  • service
    • board_service._batch_coach_details 接受 ref_date60 天消费窗口按业务日截。
    • chat_service._get_consumption_30d / _get_visit_count_30d 业务日 30 天窗口。
    • coach_service.get_coach_detail / _build_history_months 用业务日年月。
    • customer_service 60 天助教统计上界化。
    • task_generator 转移子流程的 now 改用 business_now。
    • task_manager.batch_query_for_task_list / build_performance_summary / 任务详情 60 天窗口全部业务日。
    • tenant_users.py SCD2 配置cfg_assistant_level_price用业务日。
  • fdw_queries(关键修复):
    • _fdw_context 进入事务后下发 app.current_business_date + app.current_runtime_mode GUC。
    • 客户看板「最近到店」bug 修复get_last_visit_days / batch_query_for_task_listlast_visit 计算)改为 ETL last_consume_date + business_date - last_consume_date 实时计算,不再依赖 ETL 预计算的 days_since_last,沙箱场景与 ETL 跑批延迟下都能正确显示"距上次到店 N 天"。
    • get_customer_board_recent / get_customer_board_recharge / get_customer_board_freq60 / get_customer_board_recall / _get_weekly_visits_batch / get_coach_60d_stats / batch_query_for_task_list 60 天窗口 / SCD2 配置等全部用业务日。

B 层 (小程序)

  • apps/backend/app/routers/xcx_runtime_clock.py 新增端点 GET /api/xcx/runtime/clock,返回 mode/business_date/business_year/business_month/business_year_month/business_now/is_sandbox/sandbox_date。
  • apps/miniprogram/miniprogram/services/api.ts 增加 fetchRuntimeClock
  • apps/miniprogram/miniprogram/utils/runtime-clock.ts(新增)—— 60s 缓存 + 失败降级到本地时间。
  • 关键页面切换为业务时钟:
    • pages/performance/performance.ts —— G2 当月预估判断
    • pages/performance-records/performance-records.ts —— onLoad / loadData / switchMonth
    • pages/task-list/task-list.ts —— 月度判断
    • pages/customer-records/customer-records.ts —— onLoad
    • pages/customer-service-records/customer-service-records.ts —— onLoad

C 层 (RLS 视图)

  • 新增迁移 db/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql
    • 注册 STABLE SQL 函数 app.business_date_now():从 GUC app.current_business_date 读取业务日,未设置时回退 CURRENT_DATE
    • 21 个视图重写 WHERE<日期列> <= app.business_date_now()
      • 财务事实 6 个:v_dws_finance_area_daily / daily_summary / discount_detail / expense_summary / income_structure / recharge_summary
      • 助教汇总 5 个:v_assistant_daily / v_dws_assistant_daily_detail / monthly_summary / salary_calc / finance_analysis
      • 客户事实 3 个:v_dws_member_consumption_summary / visit_detail / winback_index
      • DWD 事实 5 个:v_dwd_settlement_head / assistant_service_log / recharge_order / store_goods_sale / table_fee_log
      • SCD2 配置 2 个:v_cfg_assistant_level_price / performance_tier
    • 列签名通过 pg_get_viewdef 实时从测试库读取,确保 CREATE OR REPLACE VIEW 不会因列签名漂移而失败。
  • 生成脚本:scripts/ops/gen_rls_business_date_migration.py(可重复执行)。
  • DDL 同步:docs/database/ddl/etl_feiqiu__app.sqldb/etl_feiqiu/schemas/app.sql 已同步。

验证

测试库迁移结果

test site_id = 2790685415443269
live: max(stat_date)=2026-04-27, count=2439
sandbox(=2025-09-01):
  max(stat_date) finance_area_daily = 2025-09-01, count=432
  max(visit_date) member_visit       = 2025-09-01
  max(create_time::date) settlement  = 2025-09-01
RESULT: PASS

live 模式行为不变sandbox 模式下所有事实视图严格不返回 sandbox_date 之后的数据。

静态检查

  • 后端 99 个改动文件 AST 解析全部通过。
  • 前端 admin-web、小程序关键页面 lint 无新增错误。

兼容性 / 回滚

  • live 模式下 GUC 不设置 → app.business_date_now() 回退 CURRENT_DATE,行为完全等同于改造前。
  • 回滚:DROP FUNCTION app.business_date_now() CASCADE;(视图会一并被 DROP然后重新执行 db/etl_feiqiu/schemas/app.sql 即可恢复 live 行为。
  • B 层 / 小程序的时间锚替换全部走 RuntimeContextfail-soft 降级 live不影响生产链路。

已知未覆盖

  • page_context.py 中 7 处直连 ETL 的查询,已加 SQL 上界B 层),但部分位置依赖 GUCC 层)即可,未单独传 ref_date。
  • 写入时间戳(created_atupdated_atfinished_at、调度 last_run_at、ai_run_logs 写入)保持系统真实时间,不应被沙箱影响(这是审计/运行时元数据),保留现状。
  • 小程序 chat / customer-detail 页面用于"展示当前操作时间"的 new Date() 保留(与会话/操作记录关联)。
  • AI 调度的预算计算、限流仍按真实系统时间。
  • DIM SCD2 维度v_dim_assistant / v_dim_member / v_dim_member_card_account / v_dim_staff / v_dim_staff_ex / v_dim_table保留 scd2_is_current=1 当前快照语义,未按 sandbox_date 重建历史维度行;如需"sandbox 当时维度状态"另行评估。

2026-05-02 后续追加

B-2 / C 层补强

  • 18 个非关键视图补业务日上界(详见 gen_rls_business_date_migration.pyVIEWS_WITH_BD):覆盖 v_cfg_bonus_rules / v_cfg_index_parameters 两个配置维度,及 16 个 DWS 业务事实/汇总(如 v_dws_assistant_customer_statsv_dws_member_assistant_intimacyv_dws_finance_board_cachev_finance_daily 等)。总计 39 个 RLS 视图带业务日上界
  • 端到端验证:tools/db/verify_sandbox_end_to_end.py 一键跑 live + sandbox(2025-09-01) 对比,输出 2026-05-02__sandbox_e2e_verify_report.md。本轮结果 31/31 PASS。
    • 注意:脚本里测的 get_customer_board_recent / recharge / freq60 / recallfdw_queries 函数,实际服务的是小程序 board-customer,不是 admin-web。验证脚本同时覆盖 RLS 视图层21+18=39 个视图),与端无关。

log 警告止血(独立于沙箱)

  • apps/etl/connectors/feiqiu/tasks/dws/finance_area_daily.py: 拓宽 _is_all_only_area,把 补时长N/虚拟台N 编号变体、area_name=None & table_id 不空 都归入 INFO不再 WARNING消除噪音。
  • apps/etl/connectors/feiqiu/tasks/dws/task_engine.py: ETL → backend HTTP _TIMEOUT(5, 30)(10, 600),与 flow_runner 对齐,止血 30s 读超时。根因(同步长任务+30s timeout已记录长期方案是 /api/internal/run-job 改异步入队,待后续 PR。

相关文件清单

  • 主迁移:db/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql
  • 生成器:scripts/ops/gen_rls_business_date_migration.py
  • 端到端验证:tools/db/verify_sandbox_end_to_end.py
  • 验证报告:docs/database/changes/2026-05-02__sandbox_e2e_verify_report.md
  • 文档:本文件 + docs/database/BD_Manual_runtime_context_sandbox.md