""" 意图:询价卡片按钮(复制工单号 / 拉产品进群 / 跳过协同 / 采用 / 不采用 / 成交)。 本文件职责:校验当前会话允许的动作后交给流程执行。 禁止:不校验 wait/允许集合就改主账;禁止一个函数处理未声明的新按钮。 """ from __future__ import annotations import logging from typing import Any from agent.channel.queue import get_message_store from agent.policy import inquiry_copy as copy from agent.policy.air_text_flow import get_air_text_flow from agent.policy.land_text_flow import get_land_text_flow from agent.policy.sea_text_flow import get_sea_text_flow logger = logging.getLogger(__name__) def _pick_card_flow(sender_id: str, payload: dict | None): """点卡:海运/陆运书签能找回工单就走对应流程,否则空运。""" sea = get_sea_text_flow() sess = sea.session_for_action(sender_id, payload) if sess and (sess.business_line or "").upper() == "SEA": return sea land = get_land_text_flow() land_sess = land.session_for_action(sender_id, payload) if land_sess and (land_sess.business_line or "").upper() == "LAND": return land return get_air_text_flow() def handle_card_action( *, sender_id: str, thread_id: str, wait_version: int, action: str, payload: dict[str, Any] | None = None, flow=None, store=None, ) -> str: """ 处理一次卡片动作。 wait_version:会话书签版本;错版本丢弃,不重放副作用。 payload 须带 task_id / event_key,才能找回点卡当时的工单。 """ _ = thread_id if not (sender_id or "").strip(): return "reject_no_sender" engine = flow or _pick_card_flow(sender_id, payload) sess = engine.session_for_action(sender_id, payload) if sess and wait_version and sess.wait_version != wait_version: logger.info("card waitVersion 不匹配 expect=%s got=%s", sess.wait_version, wait_version) return "card_discard" msg_store = store or get_message_store() def reply(text: str, extra: dict | None = None) -> str: # 销售询价正文不加 [test];[test] 只给运维告警 body = text extra = dict(extra or {}) wo = "" if sess is not None: wo = (sess.work_order_no or "").strip() if not wo: wo = copy.work_order_from_card_meta(payload) code = str((payload or {}).get("response_code") or extra.get("response_code") or "")[:24] ok, info = msg_store.enqueue_outbound( touser=sender_id, content=body, dedupe_key=( f"card:{sender_id}:{wo}:{code}:" f"{sess.wait_version if sess else 0}:{action}:{body[:20]}" ), payload=extra, ) if not ok: logger.warning("card 出站被去重 sender=%s wo=%s info=%s", sender_id, wo or "-", info) return "" return info return engine.on_button( sender_id=sender_id, action=action, reply=reply, card_meta=payload )