diff --git a/backend/app/api/lead.py b/backend/app/api/lead.py index b87d3332..f2ae88ca 100644 --- a/backend/app/api/lead.py +++ b/backend/app/api/lead.py @@ -1,16 +1,17 @@ """AI获客 - 客户咨询接口""" from fastapi import APIRouter, HTTPException from pydantic import BaseModel -import json, os, datetime, subprocess +import json, os, datetime, urllib.request router = APIRouter(prefix="/api/cma/lead", tags=["AI获客"]) -# ai-cs 实盘分流脚本(跨 profile 调用) -# 必须以 ai-cs 身份 + 其 HERMES_HOME/HOME 运行:脚本内用 `hermes send` 复用该 profile 的企微凭据; -# cma-backend.service 的 Environment 为空,不显式传入会解析到非 ai-cs 的 profile(root 默认 profile)。 -AI_CS_USER = "ai-cs" -AI_CS_HOME = "/var/lib/wecom-ai-cs/profile" -AI_CS_ROUTING_SCRIPT = AI_CS_HOME + "/scripts/routing-live.py" +# 客户咨询通知唯一通道:系统总线中继 8660 → 任总 DM。 +# 2026-10-22 起取代旧的 ai-cs routing-live.py(该 profile/user 已下线,runuser 必然失败且异常被吞, +# 导致线索静默丢失通知)。中继类别按契约取 fallback(无会话兜底),company 类被中继直接拒绝。 +BUS_RELAY_URL = "http://127.0.0.1:8660/bus/send" +BUS_KEY_FILE = "/srv/hermes/shared/.secrets/bus_relay.key" +BOSS_USERID = "200653946" # 任富海 +NOTIFY_LOG = "/root/leads/notify.log" class LeadRequest(BaseModel): name: str @@ -18,22 +19,54 @@ class LeadRequest(BaseModel): requirement: str = "" source: str = "sxbh.ltd" -def notify_boss(name, phone, requirement): - """额外推给任总(经 ai-cs 实盘分流脚本;失败不影响主流程)""" +def _log_notify(ok, detail): + """通知结果落痕(旧实现 except: pass 静默吞异常,导致线索漏通知无人知晓)""" try: - subprocess.run( - ["runuser", "-u", AI_CS_USER, "--", - "env", f"HOME={AI_CS_HOME}", f"HERMES_HOME={AI_CS_HOME}", - f"USER={AI_CS_USER}", f"LOGNAME={AI_CS_USER}", - "/usr/bin/python3", AI_CS_ROUTING_SCRIPT, - requirement or "(未填需求)", - f"{name} {phone}", - "website:sxbh.ltd"], - timeout=30, capture_output=True, - ) + os.makedirs(os.path.dirname(NOTIFY_LOG), exist_ok=True) + with open(NOTIFY_LOG, "a", encoding="utf-8") as f: + f.write("%s %s %s\n" % (datetime.datetime.now().isoformat(timespec="seconds"), + "OK" if ok else "FAIL", detail)) except Exception: pass + +def notify_boss(name, phone, requirement): + """客户咨询通知:经系统总线中继 8660 推任总 DM。失败不阻断主流程,但必须留痕。""" + now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") + content = ( + "## 新客户咨询(sxbh.ltd 官网)\n" + f"> 姓名:{name}\n" + f"> 电话:{phone}\n" + f"> 需求:{requirement or '(未填)'}\n" + f"> 时间:{now}\n\n" + "承诺 30 分钟内联系。" + ) + try: + with open(BUS_KEY_FILE, encoding="utf-8") as f: + key = f.read().strip() + if not key: + raise RuntimeError("bus key empty") + payload = json.dumps({ + "touser": BOSS_USERID, + "msgtype": "markdown", + "content": content, + "notice_class": "fallback", + }, ensure_ascii=False).encode("utf-8") + req = urllib.request.Request( + BUS_RELAY_URL, data=payload, + headers={"Content-Type": "application/json", + "X-API-Key": key, + "X-Notice-Class": "fallback"}, + method="POST", + ) + with urllib.request.urlopen(req, timeout=10) as resp: + body = resp.read().decode("utf-8", "ignore")[:300] + _log_notify(True, body) + return True + except Exception as e: + _log_notify(False, "%s: %s" % (type(e).__name__, e)) + return False + @router.post("") def create_lead(data: LeadRequest): """接收客户咨询并保存"""