import asyncio from typing import Dict, List, Tuple import httpx import logging from app.config import settings logger = logging.getLogger(__name__) async def fetch_brand_name( brand_id: str, semaphore: asyncio.Semaphore, ) -> Tuple[str, str]: """ 获取单个品牌名称. Args: brand_id: 品牌ID semaphore: 并发控制信号量 Returns: (brand_id, brand_name) 元组, 失败时 brand_name 为 brand_id """ async with semaphore: try: # 构建请求头,包含 Bearer Token 认证 (T-020) headers = {} if settings.BRAND_API_TOKEN: headers["Authorization"] = f"Bearer {settings.BRAND_API_TOKEN}" async with httpx.AsyncClient( timeout=settings.BRAND_API_TIMEOUT ) as client: response = await client.get( f"{settings.BRAND_API_BASE_URL}/v1/yuntu/brands", params={"brand_id": brand_id}, headers=headers, ) if response.status_code == 200: data = response.json() # T-019: 正确解析品牌API响应 # 响应格式: {"total": 1, "data": [{"brand_id": xxx, "brand_name": "xxx"}]} if isinstance(data, dict): data_list = data.get("data", []) if isinstance(data_list, list) and len(data_list) > 0: first_item = data_list[0] if isinstance(first_item, dict): name = first_item.get("brand_name") if name: return brand_id, name except httpx.TimeoutException: logger.warning(f"Brand API timeout for brand_id: {brand_id}") except httpx.RequestError as e: logger.warning(f"Brand API request error for brand_id: {brand_id}, error: {e}") except Exception as e: logger.error(f"Unexpected error fetching brand {brand_id}: {e}") # 失败时降级返回 brand_id return brand_id, brand_id async def get_brand_names(brand_ids: List[str]) -> Dict[str, str]: """ 批量获取品牌名称. Args: brand_ids: 品牌ID列表 Returns: brand_id -> brand_name 映射字典 """ # 过滤空值并去重 unique_ids = list(set(filter(None, brand_ids))) if not unique_ids: return {} # 创建并发控制信号量 semaphore = asyncio.Semaphore(settings.BRAND_API_CONCURRENCY) # 批量并发请求 tasks = [fetch_brand_name(brand_id, semaphore) for brand_id in unique_ids] results = await asyncio.gather(*tasks, return_exceptions=True) # 构建映射表 brand_map: Dict[str, str] = {} for result in results: if isinstance(result, tuple): brand_id, brand_name = result brand_map[brand_id] = brand_name elif isinstance(result, Exception): logger.error(f"Error in batch brand fetch: {result}") return brand_map