机器人显示名与站点标题可配;多段联运列表投影;协同群监听对账;工单附件落存;模板与工单后台补强
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
主账 HTTP 账本:建单 / 查价 / 报价 / 六态只打 8180。
|
||||
主账 HTTP 账本:建单 / 查价 / 报价 / 六态只打 8080。
|
||||
|
||||
本文件职责:把 MemoryLedger 同名方法接到 /inquiry/agent/**;查价走真 TMS(主账出站)。
|
||||
禁止:直连 TMS / MySQL;把 Jeecg 包装失败当成有价。
|
||||
@@ -236,13 +236,15 @@ class HttpLedger:
|
||||
quote: Optional[dict[str, Any]] = None,
|
||||
format: str = "xlsx",
|
||||
sales_wecom_id: str = "",
|
||||
persist_attachment: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
打主账按后台报价模板填 Excel。
|
||||
|
||||
失败(无关键词且无默认)原样带回销售话术。Java 不转 PDF。
|
||||
persist_attachment:True 才写主账附件表(确认落库);激活发群默认 False。
|
||||
"""
|
||||
from agent.quote_templates.match import FAIL_MESSAGE
|
||||
from agent.quote_templates.match import FAIL_MESSAGE, normalize_biz, pick_quote_template
|
||||
from agent.schema.quote_adjust import stamp_prices_for_sheet
|
||||
|
||||
# 出单前把当前费用写入线路表、海运分段、空运舱位。否则报价单仍是旧价。
|
||||
@@ -260,8 +262,9 @@ class HttpLedger:
|
||||
"facts": facts,
|
||||
"quote": quote or {},
|
||||
"salesWecomId": sales_wecom_id,
|
||||
"persistAttachment": bool(persist_attachment),
|
||||
},
|
||||
idem=f"quote-render:{work_order_no}:{format}",
|
||||
idem=f"quote-render:{work_order_no}:{format}:{int(bool(persist_attachment))}",
|
||||
)
|
||||
finally:
|
||||
self._client.timeout_seconds = prev_timeout
|
||||
@@ -270,6 +273,7 @@ class HttpLedger:
|
||||
return {
|
||||
"ok": False,
|
||||
"error": out.get("error") or "NO_TEMPLATE",
|
||||
"failStage": out.get("failStage"),
|
||||
"message": out.get("message") or FAIL_MESSAGE,
|
||||
}
|
||||
from agent.quote_templates.filename import filename_from_render
|
||||
@@ -465,12 +469,16 @@ class HttpLedger:
|
||||
flight_no: str = "",
|
||||
) -> dict[str, Any]:
|
||||
"""锁舱出站。航班号和航司一并带上,主账缺了这两项不能当锁舱失败。"""
|
||||
from agent.policy.bot_name import strip_bot_mention
|
||||
|
||||
# 群入站常带「@智价通AIQ …」;出站备注只留业务原文,禁止把 @机器人 带给 TMS。
|
||||
clean_instruction = strip_bot_mention(instruction_text or "")
|
||||
body: dict[str, Any] = {
|
||||
"workOrderNo": work_order_no,
|
||||
"optionNo": option_no,
|
||||
"operatorUserId": sender_id,
|
||||
"operatorName": operator_name,
|
||||
"instructionText": instruction_text,
|
||||
"instructionText": clean_instruction,
|
||||
}
|
||||
if (airline or "").strip():
|
||||
body["airline"] = airline.strip().upper()
|
||||
@@ -491,6 +499,9 @@ class HttpLedger:
|
||||
operator_name: str = "",
|
||||
instruction_text: str = "",
|
||||
) -> dict[str, Any]:
|
||||
from agent.policy.bot_name import strip_bot_mention
|
||||
|
||||
clean_instruction = strip_bot_mention(instruction_text or "")
|
||||
return self._post(
|
||||
"/inquiry/agent/tms/releaseCabin",
|
||||
{
|
||||
@@ -498,7 +509,7 @@ class HttpLedger:
|
||||
"lockId": lock_id,
|
||||
"operatorUserId": sender_id,
|
||||
"operatorName": operator_name,
|
||||
"instructionText": instruction_text,
|
||||
"instructionText": clean_instruction,
|
||||
},
|
||||
idem=f"air-release:{work_order_no}:{lock_id}",
|
||||
)
|
||||
@@ -510,6 +521,38 @@ class HttpLedger:
|
||||
return None
|
||||
return out
|
||||
|
||||
def list_open_collab(self) -> list[dict[str, Any]] | None:
|
||||
"""
|
||||
列出仍绑着协同群的工单(含已结束协同)。
|
||||
|
||||
Worker 对账监听用。
|
||||
返回 None = 接口失败/形态不对(调用方禁止据此做孤儿清群);
|
||||
返回 [] = 确实没有绑群工单。
|
||||
"""
|
||||
try:
|
||||
out = self._post("/inquiry/agent/ticket/listOpenCollab", {})
|
||||
except Exception:
|
||||
logger.exception("listOpenCollab 调用失败")
|
||||
return None
|
||||
if not isinstance(out, dict):
|
||||
logger.error("listOpenCollab 返回非对象")
|
||||
return None
|
||||
# 主账失败常见 success=false / code!=200,不能当成「零条活群」。
|
||||
if out.get("success") is False:
|
||||
logger.error("listOpenCollab 业务失败 code=%s", out.get("code") or out.get("status"))
|
||||
return None
|
||||
rows = out.get("tickets")
|
||||
if rows is None and "result" in out:
|
||||
result = out.get("result")
|
||||
if isinstance(result, dict):
|
||||
rows = result.get("tickets")
|
||||
elif isinstance(result, list):
|
||||
rows = result
|
||||
if not isinstance(rows, list):
|
||||
logger.error("listOpenCollab 无 tickets 列表字段 keys=%s", sorted(out.keys())[:20])
|
||||
return None
|
||||
return [r for r in rows if isinstance(r, dict)]
|
||||
|
||||
def end_collab_group(self, *, work_order_no: str) -> dict[str, Any]:
|
||||
"""协同结束,这单不能再拉群。不改六态。"""
|
||||
return self._post(
|
||||
@@ -539,6 +582,34 @@ class HttpLedger:
|
||||
},
|
||||
)
|
||||
|
||||
def attach_file_bytes(
|
||||
self,
|
||||
*,
|
||||
work_order_no: str,
|
||||
file_name: str,
|
||||
file_bytes: bytes,
|
||||
content_type: str = "",
|
||||
sender_id: str = "",
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
报价附件字节写入主账(上传对象存储 + inq_attachment)。
|
||||
|
||||
用于 PDF 转换完成后落库;Excel 出单由主账 render 内登记。
|
||||
"""
|
||||
import base64
|
||||
|
||||
return self._post(
|
||||
"/inquiry/agent/ticket/attachFileBytes",
|
||||
{
|
||||
"workOrderNo": work_order_no,
|
||||
"fileName": file_name,
|
||||
"fileBase64": base64.b64encode(file_bytes or b"").decode("ascii"),
|
||||
"contentType": content_type or "application/octet-stream",
|
||||
"senderId": sender_id,
|
||||
},
|
||||
idem=f"attach-bytes:{work_order_no}:{file_name}:{len(file_bytes or b'')}",
|
||||
)
|
||||
|
||||
def upsert_product_quote(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user