258 lines
11 KiB
Python
258 lines
11 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from datetime import datetime, timedelta
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
TRADE_STATUS_MAP = {
|
|||
|
|
"WAIT_BUYER_PAY": "等待买家付款",
|
|||
|
|
"WAIT_SELLER_SEND_GOODS": "等待卖家发货",
|
|||
|
|
"WAIT_BUYER_CONFIRM_GOODS": "等待买家确认收货",
|
|||
|
|
"TRADE_FINISHED": "交易完成",
|
|||
|
|
"TRADE_CLOSED": "交易关闭",
|
|||
|
|
"TRADE_CLOSED_BY_TAOBAO": "交易被淘宝关闭",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TaobaoTools:
|
|||
|
|
def __init__(self, client):
|
|||
|
|
self._client = client
|
|||
|
|
|
|||
|
|
async def query_order(self, order_id: str, session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
data = await self._client.get_trade_fullinfo(int(order_id), session)
|
|||
|
|
trade = data.get("trade_fullinfo_get_response", {}).get("trade", {})
|
|||
|
|
|
|||
|
|
status = TRADE_STATUS_MAP.get(trade.get("status", ""), trade.get("status", "未知"))
|
|||
|
|
payment = trade.get("payment", "未知")
|
|||
|
|
buyer_nick = trade.get("buyer_nick", "")
|
|||
|
|
orders = trade.get("orders", {}).get("order", [])
|
|||
|
|
|
|||
|
|
lines = [
|
|||
|
|
f"📦 订单 {order_id}",
|
|||
|
|
f"状态: {status}",
|
|||
|
|
f"金额: ¥{payment}",
|
|||
|
|
f"买家: {buyer_nick}",
|
|||
|
|
]
|
|||
|
|
if orders:
|
|||
|
|
lines.append("商品:")
|
|||
|
|
for o in orders if isinstance(orders, list) else [orders]:
|
|||
|
|
lines.append(f" - {o.get('title', '未知')} ×{o.get('num', 1)} ¥{o.get('price', 0)}")
|
|||
|
|
return "\n".join(lines)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query order failed: %s", order_id)
|
|||
|
|
return f"查询订单 {order_id} 失败: {e}"
|
|||
|
|
|
|||
|
|
async def query_logistics(self, order_id: str, session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
data = await self._client.get_logistics_trace(int(order_id), session)
|
|||
|
|
resp = data.get("logistics_trace_search_response", {})
|
|||
|
|
traces = resp.get("trace_list", resp.get("traces", []))
|
|||
|
|
|
|||
|
|
if not traces:
|
|||
|
|
return f"📦 订单 {order_id} 暂无物流信息"
|
|||
|
|
|
|||
|
|
lines = [f"📦 订单 {order_id} 物流轨迹:"]
|
|||
|
|
for t in (traces if isinstance(traces, list) else [traces])[:10]:
|
|||
|
|
lines.append(f" [{t.get('time', '')}] {t.get('status_desc', t.get('desc', ''))}")
|
|||
|
|
return "\n".join(lines)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query logistics failed: %s", order_id)
|
|||
|
|
return f"查询物流 {order_id} 失败: {e}"
|
|||
|
|
|
|||
|
|
async def query_item(self, item_id: str, session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
data = await self._client.get_item(int(item_id), session)
|
|||
|
|
item = data.get("item_get_response", {}).get("item", {})
|
|||
|
|
|
|||
|
|
title = item.get("title", "未知商品")
|
|||
|
|
price = item.get("price", "")
|
|||
|
|
desc = item.get("desc", "")[:200]
|
|||
|
|
|
|||
|
|
lines = [f"🛒 {title}"]
|
|||
|
|
if price:
|
|||
|
|
lines.append(f"价格: ¥{price}")
|
|||
|
|
if desc:
|
|||
|
|
lines.append(f"描述: {desc}")
|
|||
|
|
return "\n".join(lines)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query item failed: %s", item_id)
|
|||
|
|
return f"查询商品 {item_id} 失败: {e}"
|
|||
|
|
|
|||
|
|
async def query_refund(self, refund_id: str, session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
data = await self._client.qianniu_refund_get(refund_id, session)
|
|||
|
|
refund = data.get("qianniu_refund_get_response", {})
|
|||
|
|
|
|||
|
|
status = refund.get("status", "未知")
|
|||
|
|
amount = refund.get("refund_amount", refund.get("amount", "未知"))
|
|||
|
|
reason = refund.get("refund_reason", refund.get("reason", "未知"))
|
|||
|
|
|
|||
|
|
return f"🔙 退款 {refund_id}\n状态: {status}\n退款金额: ¥{amount}\n申请原因: {reason}"
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query refund failed: %s", refund_id)
|
|||
|
|
return f"查询退款 {refund_id} 失败: {e}"
|
|||
|
|
|
|||
|
|
async def query_buyer_tags(self, buyer_nick: str, session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
data = await self._client.qianniu_buyer_tag_get(buyer_nick, session)
|
|||
|
|
tags = data.get("qianniu_buyer_tag_get_response", {}).get("tags", [])
|
|||
|
|
|
|||
|
|
if not tags:
|
|||
|
|
return f"买家 {buyer_nick} 暂无标签"
|
|||
|
|
|
|||
|
|
tag_names = [t.get("tag_name", str(t)) for t in tags] if isinstance(tags, list) else [str(tags)]
|
|||
|
|
return f"买家 {buyer_nick} 标签: {', '.join(tag_names)}"
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query buyer tags failed: %s", buyer_nick)
|
|||
|
|
return f"查询买家标签失败: {e}"
|
|||
|
|
|
|||
|
|
async def transfer_human(self, buyer_nick: str, reason: str = "", session: str | None = None) -> str:
|
|||
|
|
try:
|
|||
|
|
if not hasattr(self._client, "qianniu_cloudkefu_forward"):
|
|||
|
|
return "转人工功能暂不可用(千牛客服转发 API 未接入)"
|
|||
|
|
|
|||
|
|
await self._client.qianniu_cloudkefu_forward(
|
|||
|
|
buyer_nick=buyer_nick,
|
|||
|
|
to_nick="", # 不指定分配到哪个客服,由分流系统自动分配
|
|||
|
|
session=session,
|
|||
|
|
)
|
|||
|
|
return f"已将买家 {buyer_nick} 转接人工客服" + (f",原因: {reason}" if reason else "")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Transfer to human failed: %s", buyer_nick)
|
|||
|
|
return f"转人工失败: {e}"
|
|||
|
|
|
|||
|
|
async def query_chat_history(self, buyer_nick: str, session: str | None = None) -> str:
|
|||
|
|
end = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
begin = (datetime.now() - timedelta(hours=24)).strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
try:
|
|||
|
|
data = await self._client.get_chatlogs(buyer_nick, begin, end, session)
|
|||
|
|
logs_data = data.get("openim_chatlogs_get_response", {}).get("chatlogs", {})
|
|||
|
|
logs = logs_data.get("chatlog", [])
|
|||
|
|
|
|||
|
|
if not logs:
|
|||
|
|
return "无近24小时聊天记录"
|
|||
|
|
|
|||
|
|
lines = ["近24小时聊天记录:"]
|
|||
|
|
for log in (logs if isinstance(logs, list) else [logs])[:10]:
|
|||
|
|
role = "买家" if log.get("role") == "buyer" else "客服"
|
|||
|
|
msg = str(log.get("message", ""))[:100]
|
|||
|
|
lines.append(f" [{log.get('time', '')}] {role}: {msg}")
|
|||
|
|
return "\n".join(lines)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Query chat history failed: %s", buyer_nick)
|
|||
|
|
return f"查询聊天记录失败: {e}"
|
|||
|
|
|
|||
|
|
def get_agent_tools(self) -> list[dict]:
|
|||
|
|
return [
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_order",
|
|||
|
|
"description": "查询淘宝订单详情,获取订单状态、金额、买家、商品列表等信息",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"order_id": {"type": "string", "description": "淘宝订单号(tid)"},
|
|||
|
|
},
|
|||
|
|
"required": ["order_id"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_order,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_logistics",
|
|||
|
|
"description": "查询淘宝订单物流轨迹",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"order_id": {"type": "string", "description": "淘宝订单号(tid)"},
|
|||
|
|
},
|
|||
|
|
"required": ["order_id"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_logistics,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_item",
|
|||
|
|
"description": "查询淘宝商品详情,包括标题、价格、描述等",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"item_id": {"type": "string", "description": "淘宝商品ID(num_iid)"},
|
|||
|
|
},
|
|||
|
|
"required": ["item_id"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_item,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_refund",
|
|||
|
|
"description": "查询淘宝退款/售后状态和详情",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"refund_id": {"type": "string", "description": "退款单号(refund_id)"},
|
|||
|
|
},
|
|||
|
|
"required": ["refund_id"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_refund,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_buyer_tags",
|
|||
|
|
"description": "查询买家标签(VIP/黑名单等)",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"buyer_nick": {"type": "string", "description": "买家淘宝昵称"},
|
|||
|
|
},
|
|||
|
|
"required": ["buyer_nick"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_buyer_tags,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_transfer_human",
|
|||
|
|
"description": "将当前会话转接人工客服",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"buyer_nick": {"type": "string", "description": "买家淘宝昵称"},
|
|||
|
|
"reason": {
|
|||
|
|
"type": "string",
|
|||
|
|
"description": "转接原因(可选)",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
"required": ["buyer_nick"],
|
|||
|
|
},
|
|||
|
|
"handler": self.transfer_human,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "taobao_query_chat_history",
|
|||
|
|
"description": "查询与指定买家的近24小时聊天记录",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"buyer_nick": {"type": "string", "description": "买家淘宝昵称"},
|
|||
|
|
},
|
|||
|
|
"required": ["buyer_nick"],
|
|||
|
|
},
|
|||
|
|
"handler": self.query_chat_history,
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
async def execute_agent_tool(self, tool_name: str, params: dict, context: dict) -> dict:
|
|||
|
|
tool_map = {
|
|||
|
|
"taobao_query_order": self.query_order,
|
|||
|
|
"taobao_query_logistics": self.query_logistics,
|
|||
|
|
"taobao_query_item": self.query_item,
|
|||
|
|
"taobao_query_refund": self.query_refund,
|
|||
|
|
"taobao_query_buyer_tags": self.query_buyer_tags,
|
|||
|
|
"taobao_transfer_human": self.transfer_human,
|
|||
|
|
"taobao_query_chat_history": self.query_chat_history,
|
|||
|
|
}
|
|||
|
|
handler = tool_map.get(tool_name)
|
|||
|
|
if handler is None:
|
|||
|
|
return {"success": False, "error": f"Unknown tool: {tool_name}"}
|
|||
|
|
try:
|
|||
|
|
session = context.get("session") or context.get("access_token")
|
|||
|
|
result = await handler(**params, session=session)
|
|||
|
|
return {"success": True, "result": result}
|
|||
|
|
except Exception as e:
|
|||
|
|
return {"success": False, "error": str(e)}
|