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:
Neo
2026-05-04 02:30:19 +08:00
parent 2010034840
commit caf179a5da
130 changed files with 14543 additions and 2717 deletions

View File

@@ -24,6 +24,7 @@
- 非 all 行现金流/卡消费/充值字段 = 0
- hall 行 = 各具体区域之和(历史兼容)
- all 行 = 各具体区域之和(收入/优惠),现金流/充值/卡消费来自 dws_finance_daily_summary
无台桌订单和补时长等 all-only 台区只合入 all不合入具体区域
- settle_type IN (1, 3) 过滤
- discount_gift_card 使用赠送卡消费金额口径
@@ -107,6 +108,9 @@ _COUNT_FIELDS = {"order_count", "member_order_count"}
_ZERO = Decimal("0")
# 已知不属于看板 7 个具体区域、但应合入 all 的物理台区。
_ALL_ONLY_AREA_NAMES = {"补时长", "虚拟台"}
class FinanceAreaDailyTask(FinanceBaseTask):
"""
@@ -177,6 +181,7 @@ class FinanceAreaDailyTask(FinanceBaseTask):
sql = f"""
SELECT
{biz_expr} AS stat_date,
sh.table_id AS table_id,
dt.site_table_area_name AS area_name,
sh.settle_type,
-- 收入
@@ -378,8 +383,9 @@ def transform_area_daily(
)
# 收集所有涉及的日期
all_dates: set[date] = set()
# 未知区域名称计数(汇总后一次性输出,避免逐行 warning 产生大量日志噪音)
# 未知/无具体区域计数(汇总后一次性输出,避免逐行日志噪音)
_unknown_area_counts: Dict[str, int] = defaultdict(int)
_all_only_area_counts: Dict[str, int] = defaultdict(int)
for row in settlement_rows:
sd = row.get("stat_date")
@@ -393,10 +399,15 @@ def transform_area_daily(
continue
area_name = row.get("area_name")
table_id = row.get("table_id")
area_code = resolve_area_code(area_name)
if area_code is None:
_unknown_area_counts[str(area_name)] += 1
unmatched_label = _format_unmatched_area_label(area_name, table_id)
if _is_all_only_area(area_name, table_id):
_all_only_area_counts[unmatched_label] += 1
else:
_unknown_area_counts[unmatched_label] += 1
# 提取金额
table_fee = safe_decimal_fn(row.get("table_fee_amount", 0))
@@ -479,11 +490,20 @@ def transform_area_daily(
for k, v in fields.items():
bucket[k] = bucket[k] + v
# 汇总输出未知区域名称(避免逐行 warning 刷屏)
# 汇总输出 all-only 区域(无台桌订单、补时长等),这些记录合入 all 属正常口径。
if _all_only_area_counts:
summary = ", ".join(f"'{k}': {v}" for k, v in _all_only_area_counts.items())
logger.info(
"DWS_FINANCE_AREA_DAILY: 共 %d 条结算单无具体区域(已计入 all不计入任何具体区域: %s",
sum(_all_only_area_counts.values()),
summary,
)
# 汇总输出真正未知区域名称(避免逐行 warning 刷屏)
if _unknown_area_counts:
summary = ", ".join(f"'{k}': {v}" for k, v in _unknown_area_counts.items())
logger.warning(
"DWS_FINANCE_AREA_DAILY: 共 %d 条结算单区域未匹配(已计入 all 但不计入任何具体区域): %s",
"DWS_FINANCE_AREA_DAILY: 共 %d 条结算单区域未匹配(已计入 all 但不计入任何具体区域,请检查 dim_table/AREA_LABEL_MAP: %s",
sum(_unknown_area_counts.values()),
summary,
)
@@ -618,4 +638,42 @@ def _safe_decimal(value: Any, default: Decimal = _ZERO) -> Decimal:
return default
def _is_all_only_area(area_name: Any, table_id: Any) -> bool:
"""判断结算单是否属于无具体区域但应合入 all 的正常口径。
CHANGE 2026-05-02 | 扩大豁免规则,避免噪音 WARNING
- "补时长" / "虚拟台" 的带数字/空格变体(如 "补时长2""虚拟台 1")也算 all-only。
- 维表 site_table_area_name 为空NULL但有 table_id 的脏数据,归入 all-only INFO
因为这通常是 dim_table SCD2 缺区域名而非真正映射缺口;金额仍合入 all 不丢失。
真正的「未知非空区域名」(如新店自定义命名未在 AREA_LABEL_MAP 中)才进 WARNING。
"""
if area_name is None:
# 无 table_id本来就没台桌正常 all-only
# 有 table_id维表区域名缺失作为 dim_table 数据问题,仍归 all-only 但保留可观测性INFO 行会带 'table_id=… None' 标签)
return True
if not isinstance(area_name, str):
return False
name = area_name.strip()
if not name:
return True
if name in _ALL_ONLY_AREA_NAMES:
return True
# 形如 "补时长2"、"补时长 3"、"虚拟台4" 等编号变体
for prefix in _ALL_ONLY_AREA_NAMES:
if name.startswith(prefix):
tail = name[len(prefix):].strip()
if not tail or tail.isdigit():
return True
return False
def _format_unmatched_area_label(area_name: Any, table_id: Any) -> str:
"""格式化未匹配区域日志标签,区分无台桌订单和维表缺口。"""
if area_name is None and not table_id:
return "无台桌"
if area_name is None:
return f"table_id={table_id}: None"
return str(area_name)
__all__ = ["FinanceAreaDailyTask", "transform_area_daily"]

View File

@@ -42,7 +42,10 @@ load_dotenv(_REPO_ROOT / ".env", override=False)
logger = logging.getLogger(__name__)
_TIMEOUT = (5, 30)
# CHANGE 2026-05-02 | 旧值 (5, 30) 在 recall_completion_check / task_generator 这种长任务下
# 经常 30s 读超时(实际处理 ~33s 以上)。临时止血提到 600s 与 flow_runner 对齐;
# 长期方案是后端 /api/internal/run-job 改异步入队(参见 docs/database/changes/2026-05-02__sandbox_complete_refactor.md 已知未覆盖)
_TIMEOUT = (10, 600)
# HTTP 模式<E6A8A1><E5BC8F><EFBFBD>按顺序执行的后端任务
_JOB_SEQUENCE = [