包含多个会话的累积代码变更: - backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔 - admin-web: ETL 状态页、任务管理、调度配置、登录优化 - miniprogram: 看板页面、聊天集成、UI 组件、导航更新 - etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强 - tenant-admin: 项目初始化 - db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8) - packages/shared: 枚举和工具函数更新 - tools: 数据库工具、报表生成、健康检查 - docs: PRD/架构/部署/合约文档更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
AI 触发工具 — DWS 任务完成后通知后端 AI 模块。
|
||
|
||
通过 POST /api/internal/ai/trigger 发送事件,
|
||
后端异步执行 AI 调用链(App2 预生成、消费事件链等)。
|
||
|
||
失败时仅记录日志,不中断 ETL 流程。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import os
|
||
from typing import Any
|
||
|
||
import requests
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 超时设置(秒):连接 5s,读取 10s
|
||
_TIMEOUT = (5, 10)
|
||
|
||
|
||
def trigger_ai_event(
|
||
event_type: str,
|
||
site_id: int,
|
||
member_id: int | None = None,
|
||
payload: dict[str, Any] | None = None,
|
||
connector_type: str = "feiqiu",
|
||
) -> bool:
|
||
"""向后端 AI 模块发送触发事件。
|
||
|
||
Args:
|
||
event_type: 事件类型(dws_completed / consumption / note_created / task_assigned)
|
||
site_id: 门店 ID
|
||
member_id: 会员 ID(可选)
|
||
payload: 附加数据(可选)
|
||
connector_type: 连接器类型,默认 feiqiu
|
||
|
||
Returns:
|
||
True 表示发送成功,False 表示失败(已记录日志)
|
||
"""
|
||
backend_url = os.environ.get("BACKEND_API_URL", "").rstrip("/")
|
||
token = os.environ.get("INTERNAL_API_TOKEN", "")
|
||
|
||
if not backend_url:
|
||
logger.warning("AI 触发跳过:BACKEND_API_URL 未配置")
|
||
return False
|
||
if not token:
|
||
logger.warning("AI 触发跳过:INTERNAL_API_TOKEN 未配置")
|
||
return False
|
||
|
||
url = f"{backend_url}/api/internal/ai/trigger"
|
||
headers = {
|
||
"Authorization": f"Internal-Token {token}",
|
||
"Content-Type": "application/json",
|
||
}
|
||
body = {
|
||
"event_type": event_type,
|
||
"connector_type": connector_type,
|
||
"site_id": site_id,
|
||
"member_id": member_id,
|
||
"payload": payload or {},
|
||
}
|
||
|
||
try:
|
||
resp = requests.post(url, json=body, headers=headers, timeout=_TIMEOUT)
|
||
if resp.status_code == 200:
|
||
data = resp.json()
|
||
logger.info(
|
||
"AI 触发成功:event=%s site=%s job_id=%s",
|
||
event_type,
|
||
site_id,
|
||
data.get("trigger_job_id"),
|
||
)
|
||
return True
|
||
else:
|
||
logger.warning(
|
||
"AI 触发失败:event=%s site=%s status=%s body=%s",
|
||
event_type,
|
||
site_id,
|
||
resp.status_code,
|
||
resp.text[:200],
|
||
)
|
||
return False
|
||
except requests.RequestException as exc:
|
||
logger.warning(
|
||
"AI 触发异常:event=%s site=%s error=%s",
|
||
event_type,
|
||
site_id,
|
||
exc,
|
||
)
|
||
return False
|