123 lines
4.2 KiB
Python
123 lines
4.2 KiB
Python
"""
|
|
协商中到期再问: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, business_line=line)
|
|
payload = copy.deal_wecom_payload(work_order_no=no, quote=quote, facts=facts, business_line=line)
|
|
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, business_line=business_line
|
|
)
|
|
carder(chat_id=chat_id, template_card=payload.get("template_card") or {})
|
|
return True
|