主要修改: 1. 订单数据解析修复 (agent/agents/order.py) - 修复 Mall API 返回数据的嵌套结构解析 - 更新字段映射:orderId→order_id, orderProduct→items, statusText→status_text - 支持多种商品图片字段:image, pic, thumb, productImg - 添加详细的调试日志 2. 物流查询修复 (mcp_servers/order_mcp/server.py) - 修复物流接口返回数据结构解析 (data[].trackingCode→tracking_number) - 添加 print() 日志用于调试 - 支持多种字段名映射 3. Chatwoot 集成优化 (agent/integrations/chatwoot.py) - 添加 json 模块导入 - 完善订单卡片和表单展示功能 4. API 请求头优化 (mcp_servers/shared/mall_client.py) - 更新 User-Agent 和 Accept 头 - 修正 Origin 和 Referer 大小写 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
185 lines
5.6 KiB
Python
185 lines
5.6 KiB
Python
"""
|
||
Mall API Client for MCP Servers
|
||
用于调用商城 API,包括订单查询等接口
|
||
"""
|
||
from typing import Any, Optional
|
||
import httpx
|
||
from pydantic_settings import BaseSettings
|
||
from pydantic import ConfigDict
|
||
|
||
|
||
class MallSettings(BaseSettings):
|
||
"""Mall API configuration"""
|
||
mall_api_url: Optional[str] = None
|
||
mall_api_token: Optional[str] = None
|
||
mall_tenant_id: str = "2"
|
||
mall_currency_code: str = "EUR"
|
||
mall_language_id: str = "1"
|
||
mall_source: str = "us.qa1.gaia888.com"
|
||
|
||
model_config = ConfigDict(
|
||
env_file=".env",
|
||
env_file_encoding="utf-8",
|
||
extra="ignore"
|
||
)
|
||
|
||
|
||
settings = MallSettings()
|
||
|
||
|
||
class MallClient:
|
||
"""Async client for Mall API"""
|
||
|
||
def __init__(
|
||
self,
|
||
api_url: Optional[str] = None,
|
||
api_token: Optional[str] = None,
|
||
tenant_id: Optional[str] = None,
|
||
currency_code: Optional[str] = None,
|
||
language_id: Optional[str] = None,
|
||
source: Optional[str] = None
|
||
):
|
||
self.api_url = (api_url or settings.mall_api_url or "").rstrip("/")
|
||
self.api_token = api_token or settings.mall_api_token or ""
|
||
self.tenant_id = tenant_id or settings.mall_tenant_id
|
||
self.currency_code = currency_code or settings.mall_currency_code
|
||
self.language_id = language_id or settings.mall_language_id
|
||
self.source = source or settings.mall_source
|
||
self._client: Optional[httpx.AsyncClient] = None
|
||
|
||
async def _get_client(self) -> httpx.AsyncClient:
|
||
"""Get or create HTTP client with default headers"""
|
||
if self._client is None:
|
||
self._client = httpx.AsyncClient(
|
||
base_url=self.api_url,
|
||
headers={
|
||
"Authorization": f"Bearer {self.api_token}",
|
||
"Content-Type": "application/json",
|
||
"Accept": "application/json, text/plain, */*",
|
||
"Device-Type": "pc",
|
||
"tenant-Id": self.tenant_id,
|
||
"currency-code": self.currency_code,
|
||
"language-id": self.language_id,
|
||
"source": self.source,
|
||
"Origin": "https://www.qa1.gaia888.com",
|
||
"Referer": "https://www.qa1.gaia888.com/",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
||
"DNT": "1",
|
||
},
|
||
timeout=30.0
|
||
)
|
||
return self._client
|
||
|
||
async def close(self):
|
||
"""Close HTTP client"""
|
||
if self._client:
|
||
await self._client.aclose()
|
||
self._client = None
|
||
|
||
async def request(
|
||
self,
|
||
method: str,
|
||
endpoint: str,
|
||
params: Optional[dict[str, Any]] = None,
|
||
json: Optional[dict[str, Any]] = None,
|
||
headers: Optional[dict[str, str]] = None
|
||
) -> dict[str, Any]:
|
||
"""Make API request and handle response
|
||
|
||
Args:
|
||
method: HTTP method
|
||
endpoint: API endpoint (e.g., "/mall/api/order/show")
|
||
params: Query parameters
|
||
json: JSON body
|
||
headers: Additional headers
|
||
|
||
Returns:
|
||
Response data
|
||
"""
|
||
client = await self._get_client()
|
||
|
||
# Merge additional headers
|
||
request_headers = {}
|
||
if headers:
|
||
request_headers.update(headers)
|
||
|
||
response = await client.request(
|
||
method=method,
|
||
url=endpoint,
|
||
params=params,
|
||
json=json,
|
||
headers=request_headers
|
||
)
|
||
response.raise_for_status()
|
||
|
||
data = response.json()
|
||
|
||
# Mall API 返回格式: {"code": 200, "msg": "success", "result": {...}}
|
||
# 检查 API 错误
|
||
if data.get("code") != 200:
|
||
raise Exception(f"API Error [{data.get('code')}]: {data.get('msg') or data.get('message')}")
|
||
|
||
# 返回 result 字段或整个 data
|
||
return data.get("result", data)
|
||
|
||
async def get(
|
||
self,
|
||
endpoint: str,
|
||
params: Optional[dict[str, Any]] = None,
|
||
**kwargs: Any
|
||
) -> dict[str, Any]:
|
||
"""GET request"""
|
||
return await self.request("GET", endpoint, params=params, **kwargs)
|
||
|
||
async def post(
|
||
self,
|
||
endpoint: str,
|
||
json: Optional[dict[str, Any]] = None,
|
||
**kwargs: Any
|
||
) -> dict[str, Any]:
|
||
"""POST request"""
|
||
return await self.request("POST", endpoint, json=json, **kwargs)
|
||
|
||
# ============ Order APIs ============
|
||
|
||
async def get_order_by_id(
|
||
self,
|
||
order_id: str
|
||
) -> dict[str, Any]:
|
||
"""Query order by order ID
|
||
|
||
根据订单号查询订单详情
|
||
|
||
Args:
|
||
order_id: 订单号 (e.g., "202071324")
|
||
|
||
Returns:
|
||
订单详情,包含订单号、状态、商品信息、金额、物流信息等
|
||
Order details including order ID, status, items, amount, logistics info, etc.
|
||
|
||
Example:
|
||
>>> client = MallClient()
|
||
>>> order = await client.get_order_by_id("202071324")
|
||
>>> print(order["order_id"])
|
||
"""
|
||
try:
|
||
result = await self.get(
|
||
"/mall/api/order/show",
|
||
params={"orderId": order_id}
|
||
)
|
||
return result
|
||
except Exception as e:
|
||
raise Exception(f"查询订单失败 (Query order failed): {str(e)}")
|
||
|
||
|
||
# Global Mall client instance
|
||
mall_client: Optional[MallClient] = None
|
||
|
||
|
||
def get_mall_client() -> MallClient:
|
||
"""Get or create global Mall client instance"""
|
||
global mall_client
|
||
if mall_client is None:
|
||
mall_client = MallClient()
|
||
return mall_client
|