From 4cad2b73c47c28ae23c47886774983807d021b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A1=B9=E7=9B=AEBot?= Date: Thu, 22 Oct 2026 16:47:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(lead):=20=E5=AE=A2=E6=88=B7=E5=92=A8?= =?UTF-8?q?=E8=AF=A2=E9=80=9A=E7=9F=A5=E6=94=B9=E8=B5=B0=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=80=BB=E7=BA=BF8660=E2=86=92=E4=BB=BB=E6=80=BBDM=EF=BC=8C?= =?UTF-8?q?=E6=9B=BF=E6=8E=89=E5=B7=B2=E4=B8=8B=E7=BA=BF=E7=9A=84=20ai-cs?= =?UTF-8?q?=20=E9=80=9A=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ai-cs 用户/profile 已不存在,旧 runuser 调用必然失败且被 except: pass 吞掉 - 线索只落 /root/leads/leads.json,任总侧零通知(线上活缺陷) - 新通道:POST 127.0.0.1:8660/bus/send,notice_class=fallback,收件人不变(200653946) - 新增 /root/leads/notify.log 落痕,杜绝静默失败 --- backend/app/api/lead.py | 71 ++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 19 deletions(-) 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): """接收客户咨询并保存"""