Files
cma-management/backend/scripts/bot_bridge_push.py
T
Hermes CI Fix 32fe3e4d50 feat: bot-bridge数据通道 — POST /kpi-result回填KPI+触发预警+MPM自动回填脚本
- 新增 POST /api/cma/bot-bridge/kpi-result: (entity_id,kpi_code)查KPI,
  写入KPIValue(source_type=bot, data_status=verified), 复用run_alert_check
  触发预警(自动联动行动计划), 返回new_alerts; KPI不存在/缺字段返回错误
- 修复verify引擎_get_kpi_current_value排序: 按calculated_at取最新值,
  bot回填值可被auto-verify读到 (修复2026H1字符串排序遮蔽月值问题)
- 新增scripts/bot_bridge_push.py: 财务Bot MPM结果自动回填(MPM字段→KPI
  映射), 支持--mpm/--mpm-file/单KPI直推/--ping
2026-08-10 23:56:14 +08:00

139 lines
5.1 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.
"""
bot-bridge 推送脚本 — 财务Bot分析结果自动回填CMA KPI值 + 触发预警
数据通道: 财务Bot MPM结果 → POST /api/cma/bot-bridge/kpi-result → KPIValue写入
→ run_alert_check 触发预警 → 联动行动计划
用法:
# 1) 推送MPM分析结果(自动映射MPM字段→KPI编码,逐项调kpi-result
python3 bot_bridge_push.py --entity-id 1 --period 2026-08 \
--mpm '{"gross_margin_adjusted": 22.5, "channel_rebate_rate": 78.3}'
# 2) 从JSON文件读取MPM结果
python3 bot_bridge_push.py --entity-id 1 --period 2026-08 --mpm-file mpm_result.json
# 3) 单KPI直接回填
python3 bot_bridge_push.py --entity-id 1 --period 2026-08 \
--kpi-code C_REBATE_RATE --value 78.3 --source finance-bot
# 4) 测试连通性
python3 bot_bridge_push.py --ping
环境变量:
CMA_API=http://127.0.0.1:8010/api/cma/bot-bridge (默认)
CMA_BRIDGE_TOKEN=cma-bot-bridge-2026 (默认, 与migrate_bot_bridge_v2.sql一致)
"""
import argparse
import json
import os
import sys
from datetime import datetime
from typing import Optional
import httpx
CMA_API = os.getenv("CMA_API", "http://127.0.0.1:8010/api/cma/bot-bridge")
BRIDGE_TOKEN = os.getenv("CMA_BRIDGE_TOKEN", "cma-bot-bridge-2026")
# MPM字段 → KPI编码映射(与 bot_bridge_v2.MPM_TO_KPI_MAP 保持一致)
MPM_TO_KPI_MAP = {
"gross_margin_adjusted": "F_GROSS_MARGIN",
"net_profit_adjusted": "F_NET_PROFIT",
"channel_rebate_rate": "C_REBATE_RATE",
"mgmt_expense_ratio": "F_COST_RATIO",
}
_HEADERS = {"X-BRIDGE-TOKEN": BRIDGE_TOKEN, "Content-Type": "application/json"}
def push_kpi_result(entity_id: int, period: str, kpi_code: str, value,
source: str = "finance-bot", remark: Optional[str] = None) -> dict:
"""调用 POST /api/cma/bot-bridge/kpi-result"""
payload = {
"entity_id": entity_id,
"kpi_code": kpi_code,
"period": period,
"value": value,
"source": source,
}
if remark:
payload["remark"] = remark
resp = httpx.post(f"{CMA_API}/kpi-result", json=payload,
headers=_HEADERS, timeout=30)
if resp.status_code >= 400:
try:
detail = resp.json().get("detail", resp.text)
except Exception:
detail = resp.text
return {"kpi_code": kpi_code, "success": False, "error": detail}
return {"kpi_code": kpi_code, "success": True, "data": resp.json()}
def push_mpm_results(entity_id: int, period: str, results: dict,
source: str = "finance-bot") -> dict:
"""MPM结果自动回填:映射字段→KPI编码,逐项推送"""
summary = {"kpi_pushed": 0, "kpi_failed": 0, "alerts": [], "items": []}
for field, kpi_code in MPM_TO_KPI_MAP.items():
if not kpi_code or results.get(field) is None:
continue
result = push_kpi_result(
entity_id, period, kpi_code, results[field], source,
remark=f"MPM自动回填[{field}] {datetime.now().strftime('%Y-%m-%d %H:%M')}",
)
summary["items"].append(result)
if result["success"]:
summary["kpi_pushed"] += 1
summary["alerts"].extend(result["data"].get("new_alerts", []))
else:
summary["kpi_failed"] += 1
return summary
def main():
parser = argparse.ArgumentParser(description="财务Bot分析结果回填CMA")
parser.add_argument("--entity-id", type=int, default=1, help="企业实体ID")
parser.add_argument("--period", default=datetime.now().strftime("%Y-%m"), help="期间 YYYY-MM")
parser.add_argument("--mpm", type=json.loads, help="MPM结果JSON (内联)")
parser.add_argument("--mpm-file", help="MPM结果JSON文件路径")
parser.add_argument("--kpi-code", help="单KPI回填: KPI编码")
parser.add_argument("--value", type=float, help="单KPI回填: 数值")
parser.add_argument("--source", default="finance-bot", help="来源Bot标识")
parser.add_argument("--ping", action="store_true", help="连通性测试")
args = parser.parse_args()
if args.ping:
try:
resp = httpx.get(f"{CMA_API}/../bot/ping", headers=_HEADERS, timeout=10)
print(f"✅ bot-bridge API连通 (HTTP {resp.status_code})")
return 0
except Exception as e:
print(f"❌ 连接失败: {e}")
return 1
if args.mpm_file:
with open(args.mpm_file, encoding="utf-8") as f:
mpm = json.load(f)
else:
mpm = args.mpm
if mpm:
summary = push_mpm_results(args.entity_id, args.period, mpm, args.source)
print(json.dumps(summary, ensure_ascii=False, indent=2))
return 0 if summary["kpi_failed"] == 0 else 1
if args.kpi_code:
if args.value is None:
print("❌ 单KPI回填需要 --value")
return 1
result = push_kpi_result(args.entity_id, args.period, args.kpi_code,
args.value, args.source)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0 if result["success"] else 1
parser.print_help()
return 1
if __name__ == "__main__":
sys.exit(main())