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

@@ -0,0 +1,137 @@
"""AI references 工具模块。
为 AI 输出ai_cache.result_json / ai_messages.reference_card
注入数据来源引用元数据,便于前端渲染可点击引用卡片。
- App2~8通过 dispatcher._write_cache 统一注入到 result['_references']
- App1通过 xcx_chat 在 assistant 消息写入时调用 build_app1_reference 生成单卡片
"""
from __future__ import annotations
from typing import Any
def build_app_references(app_type: str, context: dict) -> list[dict]:
"""为 App2~8 构建 references 列表,供前端消息卡片渲染。
引用结构:
{
"type": "member" | "task" | "assistant" | "finance",
"id": int | str,
"label": "卡片上的文字",
"link": "/pages/xxx/xxx?param=val"(小程序页面路径),
"source_page": 小程序页面 contextType
}
Args:
app_type: 应用名称
context: 传给 build_prompt 的上下文(含 site_id / member_id 等)
Returns:
refs 数组。无有效上下文时返回空数组。
"""
refs: list[dict] = []
site_id = context.get("site_id")
member_id = context.get("member_id")
assistant_id = context.get("assistant_id")
time_dimension = context.get("time_dimension")
if member_id is not None:
refs.append({
"type": "member",
"id": member_id,
"label": f"客户 #{member_id}",
"link": f"/pages/customer-detail/customer-detail?customerId={member_id}",
"source_page": "customer-detail",
})
if assistant_id is not None:
refs.append({
"type": "assistant",
"id": assistant_id,
"label": f"助教 #{assistant_id}",
"link": f"/pages/coach-detail/coach-detail?coachId={assistant_id}",
"source_page": "coach-detail",
})
if app_type == "app2_finance" and time_dimension:
refs.append({
"type": "finance",
"id": time_dimension,
"label": f"财务看板:{_label_for_dimension(time_dimension)}",
"link": f"/pages/board-finance/board-finance?timeDimension={time_dimension}",
"source_page": "board-finance",
})
# 保留 site_id 作为兜底上下文(不单独成卡,但用于前端场景判断)
if site_id is not None and refs:
for r in refs:
r.setdefault("site_id", site_id)
return refs
def attach_references(app_type: str, result: dict | None, context: dict) -> dict | None:
"""向 AI 输出 result 追加 _references 字段(非破坏性)。
- result 为 None 时原样返回(调用失败不注入)
- result 为 dict 时追加 _references 字段;如果 result 已含 _references保留原值
"""
if result is None or not isinstance(result, dict):
return result
if "_references" in result:
return result
refs = build_app_references(app_type, context)
if refs:
result["_references"] = refs
return result
def build_app1_reference_card(source_page: str | None, context_id: int | str | None) -> dict | None:
"""为 App1chatassistant 消息构建单个 reference_card。
兼容前端 chat.wxml 已有的 {type, title, summary, data, dataList} 渲染结构,
额外携带 link 字段供前端点击跳转详情页。
当用户在特定页面customer-detail / coach-detail / task-detail发起对话时
自动附加对应跳转卡片。普通浮窗对话source_page='general')返回 None。
与 chat_service.build_reference_card 不同:本函数不查 DB仅按 source_page 构造链接。
"""
if not source_page or not context_id:
return None
mapping: dict[str, tuple[str, str, str]] = {
"customer-detail": ("customer", "客户", "customerId"),
"coach-detail": ("assistant", "助教", "coachId"),
"task-detail": ("task", "任务", "taskId"),
}
entry = mapping.get(source_page)
if entry is None:
return None
ref_type, label_prefix, param = entry
return {
"type": ref_type,
"title": f"{label_prefix} #{context_id}",
"summary": f"点击查看{label_prefix}详情",
"data": {},
"link": f"/pages/{source_page}/{source_page}?{param}={context_id}",
"source_page": source_page,
}
def _label_for_dimension(dimension: str) -> str:
"""8 个财务维度 → 中文标签。"""
mapping = {
"this_month": "本月",
"last_month": "上月",
"this_week": "本周",
"last_week": "上周",
"this_quarter": "本季度",
"last_quarter": "上季度",
"last_3_months": "近三个月",
"last_6_months": "近六个月",
}
return mapping.get(dimension, dimension)