feat: 重构订单和物流信息展示格式

主要改动:
- 订单列表:使用 order_list 格式,展示 5 个订单(全部状态)
- 订单详情:使用 order_detail 格式,优化价格和时间显示
- 物流信息:使用 logistics 格式,根据 track id 动态生成步骤
- 商品图片:从 orderProduct.imageUrl 字段获取
- 时间格式:统一为 YYYY-MM-DD HH:MM:SS
- 多语言支持:amountLabel、orderTime 支持中英文
- 配置管理:新增 FRONTEND_URL 环境变量
- API 集成:改进 Mall API tracks 数据解析
- 认证优化:account_id 从 webhook 动态获取

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
wangliang
2026-01-23 18:49:40 +08:00
parent e8e89601a5
commit 0b5d0a8086
11 changed files with 1493 additions and 394 deletions

View File

@@ -47,25 +47,36 @@ class MallClient:
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"""
async def _get_client(self, extra_headers: Optional[dict[str, str]] = None) -> httpx.AsyncClient:
"""Get or create HTTP client with default headers
Args:
extra_headers: 额外的请求头,某些接口需要特殊的 header如 Authorization2
"""
if self._client is None:
default_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,
"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",
}
# 合并额外的 headers用于 Authorization2 等)
if extra_headers:
default_headers.update(extra_headers)
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",
},
headers=default_headers,
timeout=30.0
)
return self._client
@@ -82,7 +93,8 @@ class MallClient:
endpoint: str,
params: Optional[dict[str, Any]] = None,
json: Optional[dict[str, Any]] = None,
headers: Optional[dict[str, str]] = None
headers: Optional[dict[str, str]] = None,
use_authorization2: bool = False
) -> dict[str, Any]:
"""Make API request and handle response
@@ -92,11 +104,20 @@ class MallClient:
params: Query parameters
json: JSON body
headers: Additional headers
use_authorization2: 是否使用 Authorization2 header 而不是 Authorization
Returns:
Response data
"""
client = await self._get_client()
# 如果需要 Authorization2关闭现有 client 并用新的 headers 创建
if use_authorization2:
if self._client:
await self._client.aclose()
self._client = None
extra_headers = {"Authorization2": f"Bearer {self.api_token}"}
client = await self._get_client(extra_headers)
else:
client = await self._get_client()
# Merge additional headers
request_headers = {}
@@ -126,19 +147,21 @@ class MallClient:
self,
endpoint: str,
params: Optional[dict[str, Any]] = None,
use_authorization2: bool = False,
**kwargs: Any
) -> dict[str, Any]:
"""GET request"""
return await self.request("GET", endpoint, params=params, **kwargs)
return await self.request("GET", endpoint, params=params, use_authorization2=use_authorization2, **kwargs)
async def post(
self,
endpoint: str,
json: Optional[dict[str, Any]] = None,
use_authorization2: bool = False,
**kwargs: Any
) -> dict[str, Any]:
"""POST request"""
return await self.request("POST", endpoint, json=json, **kwargs)
return await self.request("POST", endpoint, json=json, use_authorization2=use_authorization2, **kwargs)
# ============ Order APIs ============
@@ -171,6 +194,77 @@ class MallClient:
except Exception as e:
raise Exception(f"查询订单失败 (Query order failed): {str(e)}")
async def get_order_list(
self,
page: int = 1,
limit: int = 10,
customer_id: int = 0,
order_types: Optional[list[int]] = None,
shipping_status: int = 10000,
date_added: Optional[str] = None,
date_end: Optional[str] = None,
no: Optional[str] = None,
status: Optional[int] = None,
is_drop_shopping: int = 0
) -> dict[str, Any]:
"""Query order list with filters
查询订单列表,支持多种筛选条件
Args:
page: 页码 (default: 1)
limit: 每页数量 (default: 10)
customer_id: 客户ID (default: 0)
order_types: 订单类型数组,如 [1, 2] (default: None)
shipping_status: 物流状态 (default: 10000)
date_added: 开始日期,格式 YYYY-MM-DD (default: None)
date_end: 结束日期,格式 YYYY-MM-DD (default: None)
no: 订单号 (default: None)
status: 订单状态 (default: None)
is_drop_shopping: 是否代发货 (default: 0)
Returns:
订单列表和分页信息
Order list and pagination info
Example:
>>> client = MallClient()
>>> result = await client.get_order_list(page=1, limit=10)
>>> print(result["data"]) # 订单列表
>>> print(result["total"]) # 总数
"""
try:
params = {
"page": page,
"limit": limit,
"customerId": customer_id,
"shippingStatus": shipping_status,
"isDropShopping": is_drop_shopping
}
# 可选参数
if order_types:
# orderTypes 是数组,需要特殊处理
# API 格式: orderTypes[]=1&orderTypes[]=2
params["orderTypes"] = order_types
if date_added:
params["dateAdded"] = date_added
if date_end:
params["dateEnd"] = date_end
if no:
params["no"] = no
if status is not None:
params["status"] = status
result = await self.get(
"/mall/api/order/list",
params=params,
use_authorization2=True # 订单列表接口需要 Authorization2
)
return result
except Exception as e:
raise Exception(f"查询订单列表失败 (Query order list failed): {str(e)}")
# Global Mall client instance
mall_client: Optional[MallClient] = None