补齐成交跟进:按线路推成交通知、收下未成交原因、协商中按工作日再问。
成交走询价消息服务;陆运/海运私聊下一句能记下原因;协商中回执写明顺延三个工作日和下次 9:00。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
协商中到期再问:Worker HTTP 槽轮询,回到当初那个会话。
|
||||
|
||||
本文件职责:认领主账到期单、发成交跟进。不在回调线程跑。
|
||||
禁止:群解散后改发私聊;占 LibreOffice 槽;把六态写进 Redis。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from agent.config import Settings, get_settings
|
||||
from agent.policy import inquiry_copy as copy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def poll_due_negotiate(*, ledger: Any = None, settings: Optional[Settings] = None) -> int:
|
||||
"""
|
||||
认领到期协商中工单并再问一次。
|
||||
|
||||
返回发出条数。主账已清空 next_followup_at,本轮失败也不再自动补发。
|
||||
"""
|
||||
cfg = settings or get_settings()
|
||||
book = ledger
|
||||
if book is None:
|
||||
if str(getattr(cfg, "ledger_backend", "http") or "http").lower() == "memory":
|
||||
return 0
|
||||
from agent.ledger.http_ledger import HttpLedger
|
||||
|
||||
book = HttpLedger()
|
||||
claimer = getattr(book, "claim_due_negotiate", None)
|
||||
if not callable(claimer):
|
||||
return 0
|
||||
rows = claimer() or []
|
||||
sent = 0
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
try:
|
||||
if _reask_one(row, settings=cfg):
|
||||
sent += 1
|
||||
except Exception:
|
||||
logger.exception("协商再问失败 wo=%s", row.get("work_order_no") or row.get("workOrderNo"))
|
||||
return sent
|
||||
|
||||
|
||||
def _reask_one(row: dict[str, Any], *, settings: Settings) -> bool:
|
||||
no = str(row.get("work_order_no") or row.get("workOrderNo") or "").strip()
|
||||
if not no:
|
||||
return False
|
||||
chat_id = str(row.get("collab_chat_id") or row.get("collabChatId") or "").strip()
|
||||
sales = str(row.get("sales_wecom_id") or row.get("salesWecomId") or "").strip()
|
||||
name = str(row.get("sales_name") or row.get("salesName") or "").strip() or "销售"
|
||||
facts = dict(row.get("facts") or {})
|
||||
quote = dict(row.get("quote") or {})
|
||||
line = str(row.get("business_line") or row.get("businessLine") or "").strip().upper()
|
||||
if chat_id:
|
||||
ok = _send_group_reask(
|
||||
chat_id=chat_id,
|
||||
work_order_no=no,
|
||||
sales_name=name,
|
||||
facts=facts,
|
||||
quote=quote,
|
||||
business_line=line,
|
||||
settings=settings,
|
||||
)
|
||||
if not ok:
|
||||
logger.warning("协商再问原群失败且不补发私聊 wo=%s chat=%s", no, chat_id)
|
||||
return ok
|
||||
if not sales:
|
||||
logger.warning("协商再问无销售 userid wo=%s", no)
|
||||
return False
|
||||
from agent.channel.queue import get_message_store
|
||||
|
||||
text = copy.deal_card(work_order_no=no, quote=quote, facts=facts)
|
||||
payload = copy.deal_wecom_payload(work_order_no=no, quote=quote, facts=facts)
|
||||
ok, info = get_message_store().enqueue_outbound(
|
||||
touser=sales,
|
||||
content=text,
|
||||
dedupe_key=f"deal-reask:{no}",
|
||||
payload=payload,
|
||||
)
|
||||
if not ok:
|
||||
logger.warning("协商再问私聊入队失败 wo=%s info=%s", no, info)
|
||||
return bool(ok)
|
||||
|
||||
|
||||
def _send_group_reask(
|
||||
*,
|
||||
chat_id: str,
|
||||
work_order_no: str,
|
||||
sales_name: str,
|
||||
facts: dict[str, str],
|
||||
quote: dict[str, Any],
|
||||
business_line: str,
|
||||
settings: Settings,
|
||||
) -> bool:
|
||||
text = copy.group_deal_followup(sales_name=sales_name, work_order_no=work_order_no)
|
||||
if business_line == "AIR":
|
||||
from agent.channel.aibot.reply import AibotReplyClient
|
||||
|
||||
out = AibotReplyClient().send_group(chat_id=chat_id, content=text)
|
||||
return bool(out.get("ok"))
|
||||
from agent.channel.outbox.sender import WeComAppClient
|
||||
|
||||
client = WeComAppClient(
|
||||
corp_id=settings.wecom_corp_id,
|
||||
secret=settings.wecom_secret,
|
||||
agent_id=int(settings.wecom_agent_id or "0") or 0,
|
||||
)
|
||||
out = client.send_group(chat_id=chat_id, content=text)
|
||||
if not out.get("ok"):
|
||||
return False
|
||||
carder = getattr(client, "send_group_card", None)
|
||||
if callable(carder):
|
||||
payload = copy.deal_wecom_payload(work_order_no=work_order_no, quote=quote, facts=facts)
|
||||
carder(chat_id=chat_id, template_card=payload.get("template_card") or {})
|
||||
return True
|
||||
Reference in New Issue
Block a user