Files
assistant/mcp_servers/shared/mall_client.py
wangliang e093995368 feat: 增强 Agent 系统和完善项目结构
主要改进:
- Agent 增强: 订单查询、售后支持、客服路由等功能优化
- 新增语言检测和 Token 管理模块
- 改进 Chatwoot webhook 处理和用户标识
- MCP 服务器增强: 订单 MCP 和 Strapi MCP 功能扩展
- 新增商城客户端、知识库、缓存和同步模块
- 添加多语言提示词系统 (YAML)
- 完善项目结构: 整理文档、脚本和测试文件
- 新增调试和测试工具脚本

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 16:28:47 +08:00

181 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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",
"Device-Type": "pc",
"tenant-Id": self.tenant_id,
"currency-code": self.currency_code,
"language-id": self.language_id,
"source": self.source,
},
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