Files
Neo-ZQYY/apps/backend/app/ws/ai_events.py
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

81 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""AI 事件 WebSocket 推送端点。
提供:
- /ws/ai-cache/{site_id} — 缓存更新 / 失效事件
- /ws/ai-alerts/{site_id} — AI 告警事件Phase 3.1
协议:
- 客户端连接 → 服务端 accept → 订阅 EventBus → 持续 send_json 事件
- 事件格式:{"type": "cache_updated|cache_invalidated|alert_created|...", "site_id": int, "payload": {...}}
- 服务端关闭或客户端断开时清理订阅
用 site_id=-1 表示全局订阅收所有门店事件admin-web 全局监控用)。
"""
from __future__ import annotations
import logging
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from ..ai.event_bus import AIEvent, get_event_bus
logger = logging.getLogger(__name__)
ws_router = APIRouter()
@ws_router.websocket("/ws/ai-cache/{site_id}")
async def ws_ai_cache(websocket: WebSocket, site_id: int) -> None:
"""AI 缓存事件推送。
site_id=-1 表示订阅全局(收所有门店的 cache_updated / cache_invalidated
"""
await _serve_event_stream(websocket, site_id, endpoint="ai-cache")
@ws_router.websocket("/ws/ai-alerts/{site_id}")
async def ws_ai_alerts(websocket: WebSocket, site_id: int) -> None:
"""AI 告警事件推送Phase 3.1)。
site_id=-1 表示订阅全局告警。
事件 type: alert_created / alert_updated / budget_exceeded / circuit_opened。
"""
await _serve_event_stream(websocket, site_id, endpoint="ai-alerts")
async def _serve_event_stream(
websocket: WebSocket, site_id: int, endpoint: str,
) -> None:
"""共享事件流处理逻辑。"""
await websocket.accept()
# -1 映射为全局订阅None
subscribe_key: int | None = None if site_id == -1 else site_id
logger.info(
"WS %s 连接建立: site_id=%s", endpoint, subscribe_key if subscribe_key else "ALL",
)
bus = get_event_bus()
queue = await bus.subscribe(subscribe_key)
try:
while True:
event = await queue.get()
if event is None:
break
await websocket.send_json({
"type": event.type,
"site_id": event.site_id,
"payload": event.payload,
})
except WebSocketDisconnect:
logger.info("WS %s 客户端断开: site_id=%s", endpoint, subscribe_key)
except Exception:
logger.exception("WS %s 异常: site_id=%s", endpoint, subscribe_key)
finally:
await bus.unsubscribe(subscribe_key, queue)
try:
await websocket.close()
except Exception:
pass