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 逐一处理
This commit is contained in:
136
docs/database/changes/2026-05-02__sandbox_complete_refactor.md
Normal file
136
docs/database/changes/2026-05-02__sandbox_complete_refactor.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 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_now,SQL 补上界 | ✅ |
|
||||
| **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)` 设置 GUC(C 层基础)
|
||||
- AI prompts:app3/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_date,60 天消费窗口按业务日截。
|
||||
- `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_list`(last_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.sql`、`db/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 层 / 小程序的时间锚替换全部走 RuntimeContext(fail-soft 降级 live),不影响生产链路。
|
||||
|
||||
## 已知未覆盖
|
||||
|
||||
- **page_context.py** 中 7 处直连 ETL 的查询,已加 SQL 上界(B 层),但部分位置依赖 GUC(C 层)即可,未单独传 ref_date。
|
||||
- 写入时间戳(`created_at`、`updated_at`、`finished_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.py` 的 `VIEWS_WITH_BD`):覆盖 `v_cfg_bonus_rules` / `v_cfg_index_parameters` 两个配置维度,及 16 个 DWS 业务事实/汇总(如 `v_dws_assistant_customer_stats`、`v_dws_member_assistant_intimacy`、`v_dws_finance_board_cache`、`v_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 / recall` 是 `fdw_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`
|
||||
Reference in New Issue
Block a user