import os import logging import httpx import asyncio from typing import Optional from services.service_designer_alert import designer_alert_service logger = logging.getLogger("cs_agent") class DispatchService: """ 8006 即时派发服务 API 接入 (正式对齐版) """ def __init__(self): self.base_url = os.getenv("DISPATCH_BASE_URL", "http://1.12.50.92:8006").rstrip("/") self.api_key = os.getenv("DISPATCH_API_KEY", "tuhui_dispatch_key_2026") self.timeout = float(os.getenv("DISPATCH_TIMEOUT_SECONDS", "10")) self.max_retries = int(os.getenv("DISPATCH_MAX_RETRIES", "3")) @staticmethod def _clip_text(text: str, limit: int = 500) -> str: if text is None: return "" if len(text) <= limit: return text return f"{text[:limit]}...(截断, 共{len(text)}字)" async def assign_designer(self, user_id: str = "") -> Optional[str]: """ 调用 /assign 接口,一键获取当前可用持有的设计师名字 """ url = f"{self.base_url}/assign" headers = {"X-API-Key": self.api_key} u_tag = f" user={user_id}" if user_id else "" logger.info(f"[Dispatch]{u_tag} 请求开始: GET {url}") async with httpx.AsyncClient(timeout=self.timeout, trust_env=False) as client: for attempt in range(1, self.max_retries + 1): try: response = await client.get(url, headers=headers) body = self._clip_text(response.text) if response.status_code == 200: try: data = response.json() except Exception: logger.error(f"[Dispatch]{u_tag} 200但响应非JSON: {body}") return None if data.get("success"): designer = data.get("assigned_to") logger.info(f"[Dispatch]{u_tag} 派单成功!设计师: {designer}") return designer logger.warning(f"[Dispatch]{u_tag} 派单被拒: {data.get('reason')} body={body}") await designer_alert_service.notify_if_needed( trigger=f"dispatch_rejected:{data.get('reason') or 'unknown'}", customer_id=user_id, ) return None if response.status_code == 401: logger.error(f"[Dispatch]{u_tag} 授权失败(401),请检查 DISPATCH_API_KEY。Body={body}") return None logger.error(f"[Dispatch]{u_tag} API 异常,状态码: {response.status_code} body={body}") if response.status_code < 500: return None except Exception as e: logger.error(f"[Dispatch]{u_tag} 网络请求失败 attempt={attempt}/{self.max_retries}: {e}") if attempt < self.max_retries: await asyncio.sleep(0.8 * attempt) return None # 全局单例 dispatch_service = DispatchService()