新增功能: - 天网协作系统 (HTTP API 端口 6060) - 三种工作流 (查找图片/处理图片/转人工派单) - 图片任务数据库 (支持客户后续增加需求) - 图绘派单系统集成 (API: 8005) - 文字检测与加价 (60-80 元高价值订单) - 风险评估与接单判断 - 作图失败自动转人工 新增文档: - 项目功能汇总.md - 三种工作流功能说明.md - 文字加价功能说明.md - 风险评估功能说明.md - 图片任务数据库功能说明.md - 图绘派单系统集成说明.md - 作图失败转接人工说明.md - DEPLOYMENT.md - TIANWANG_INTEGRATION.md 核心修改: - core/pydantic_ai_agent.py - core/workflow.py - core/websocket_client.py - image/image_analyzer.py - services/service_tuhui_dispatch.py - db/image_tasks_db.py 版本:v1.0 日期:2026-02-28
230 lines
7.7 KiB
Python
230 lines
7.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
图绘派单系统 API 客户端
|
||
"""
|
||
import httpx
|
||
import logging
|
||
from typing import List, Dict, Optional
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class TuhuiDispatchClient:
|
||
"""图绘派单系统客户端"""
|
||
|
||
def __init__(self):
|
||
self.base_url = "http://1.12.50.92:8005"
|
||
self.api_key = "tuhui_dispatch_key_2026"
|
||
self.headers = {"X-API-Key": self.api_key}
|
||
self.timeout = 10.0
|
||
|
||
async def get_dispatch_queue(self) -> Optional[Dict]:
|
||
"""
|
||
获取派单队列
|
||
|
||
Returns:
|
||
dict: {
|
||
"pending_tasks": {"count": 3, "tasks": [...]},
|
||
"online_designers": {"count": 2, "designers": [...]},
|
||
"suggestion": "橘子"
|
||
}
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.get(
|
||
f"{self.base_url}/dispatch/queue",
|
||
headers=self.headers
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
return response.json()
|
||
else:
|
||
logger.error(f"获取派单队列失败:HTTP {response.status_code}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取派单队列异常:{e}")
|
||
return None
|
||
|
||
async def get_online_designers(self) -> List[str]:
|
||
"""
|
||
获取在线设计师
|
||
|
||
Returns:
|
||
list: ["橘子", "婷婷", ...]
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.get(
|
||
f"{self.base_url}/online/designers",
|
||
headers=self.headers
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
data = response.json()
|
||
return data.get("online_users", [])
|
||
else:
|
||
logger.error(f"获取在线设计师失败:HTTP {response.status_code}")
|
||
return []
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取在线设计师异常:{e}")
|
||
return []
|
||
|
||
async def create_task(self, task_name: str, description: str = "",
|
||
task_type: str = "design", priority: int = 1,
|
||
deadline: str = None) -> Optional[str]:
|
||
"""
|
||
创建任务
|
||
|
||
Args:
|
||
task_name: 任务名称
|
||
description: 任务描述
|
||
task_type: 任务类型
|
||
priority: 优先级(1-5)
|
||
deadline: 截止时间
|
||
|
||
Returns:
|
||
task_id: 任务 ID,失败返回 None
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.post(
|
||
f"{self.base_url}/tasks",
|
||
headers={**self.headers, "Content-Type": "application/json"},
|
||
json={
|
||
"task_name": task_name,
|
||
"description": description,
|
||
"task_type": task_type,
|
||
"priority": priority,
|
||
"deadline": deadline
|
||
}
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
data = response.json()
|
||
task_id = data.get("task_id")
|
||
logger.info(f"创建任务成功:{task_id}")
|
||
return task_id
|
||
else:
|
||
logger.error(f"创建任务失败:HTTP {response.status_code}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建任务异常:{e}")
|
||
return None
|
||
|
||
async def assign_task(self, task_id: str, designer_name: str,
|
||
notes: str = "") -> bool:
|
||
"""
|
||
分配任务给设计师
|
||
|
||
Args:
|
||
task_id: 任务 ID
|
||
designer_name: 设计师姓名
|
||
notes: 备注信息
|
||
|
||
Returns:
|
||
bool: 是否成功
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.post(
|
||
f"{self.base_url}/tasks/{task_id}/assign",
|
||
headers={**self.headers, "Content-Type": "application/json"},
|
||
json={
|
||
"designer_name": designer_name,
|
||
"notes": notes or "AI 客服自动派单"
|
||
}
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
data = response.json()
|
||
logger.info(f"任务分配成功:{task_id} → {designer_name}")
|
||
return True
|
||
else:
|
||
logger.error(f"分配任务失败:HTTP {response.status_code}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"分配任务异常:{e}")
|
||
return False
|
||
|
||
async def get_task_status(self, task_id: str) -> Optional[Dict]:
|
||
"""
|
||
查询任务状态
|
||
|
||
Args:
|
||
task_id: 任务 ID
|
||
|
||
Returns:
|
||
dict: 任务状态信息
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.get(
|
||
f"{self.base_url}/tasks/{task_id}",
|
||
headers=self.headers
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
return response.json()
|
||
else:
|
||
logger.error(f"查询任务状态失败:HTTP {response.status_code}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logger.error(f"查询任务状态异常:{e}")
|
||
return None
|
||
|
||
async def complete_task(self, task_id: str, notes: str = "") -> bool:
|
||
"""
|
||
完成任务
|
||
|
||
Args:
|
||
task_id: 任务 ID
|
||
notes: 备注信息
|
||
|
||
Returns:
|
||
bool: 是否成功
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||
response = await client.post(
|
||
f"{self.base_url}/tasks/{task_id}/complete",
|
||
headers={**self.headers, "Content-Type": "application/json"},
|
||
json={
|
||
"notes": notes or "客户已确认,效果满意"
|
||
}
|
||
)
|
||
|
||
if response.status_code == 200:
|
||
logger.info(f"任务完成:{task_id}")
|
||
return True
|
||
else:
|
||
logger.error(f"完成任务失败:HTTP {response.status_code}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"完成任务异常:{e}")
|
||
return False
|
||
|
||
async def health_check(self) -> bool:
|
||
"""健康检查"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||
response = await client.get(f"{self.base_url}/health")
|
||
return response.status_code == 200
|
||
except:
|
||
return False
|
||
|
||
|
||
# 单例
|
||
_client: Optional[TuhuiDispatchClient] = None
|
||
|
||
def get_tuhui_dispatch_client() -> TuhuiDispatchClient:
|
||
"""获取派单客户端单例"""
|
||
global _client
|
||
if _client is None:
|
||
_client = TuhuiDispatchClient()
|
||
return _client
|