新增功能: - 天网协作系统 (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
308 lines
7.9 KiB
Python
308 lines
7.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
HTTP API 服务器
|
||
提供天网任务接收接口
|
||
"""
|
||
from flask import Flask, request, jsonify
|
||
import logging
|
||
from datetime import datetime
|
||
from db.task_db.task_model import get_task_manager, TaskStatus
|
||
from core.task_scheduler import get_task_scheduler
|
||
import asyncio
|
||
import threading
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
app = Flask(__name__)
|
||
app.config['JSON_AS_ASCII'] = False # 支持中文
|
||
|
||
task_manager = None
|
||
task_scheduler = None
|
||
|
||
|
||
def get_async_loop():
|
||
"""获取异步事件循环"""
|
||
loop = asyncio.new_event_loop()
|
||
asyncio.set_event_loop(loop)
|
||
return loop
|
||
|
||
|
||
def run_async(coro):
|
||
"""运行异步协程"""
|
||
loop = get_async_loop()
|
||
return loop.run_until_complete(coro)
|
||
|
||
|
||
@app.route('/api/task/receive', methods=['POST'])
|
||
def receive_task():
|
||
"""
|
||
接收天网下发的任务
|
||
|
||
Request Body:
|
||
{
|
||
"task_id": "TASK_20260226_001",
|
||
"type": "send_file_after_reply",
|
||
"customer": {
|
||
"name": "小明",
|
||
"id": "customer_123"
|
||
},
|
||
"trigger": {
|
||
"type": "customer_reply",
|
||
"keyword": "好的"
|
||
},
|
||
"action": {
|
||
"type": "send_file",
|
||
"file_url": "https://xxx.com/file.zip",
|
||
"message": "这是您要的文件"
|
||
},
|
||
"priority": "normal",
|
||
"timeout_hours": 24,
|
||
"created_by": "设计师 lz"
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"code": 200,
|
||
"message": "任务接收成功",
|
||
"data": {
|
||
"task_id": "TASK_20260226_001",
|
||
"status": "pending"
|
||
}
|
||
}
|
||
"""
|
||
try:
|
||
data = request.get_json()
|
||
|
||
if not data:
|
||
return jsonify({
|
||
'code': 400,
|
||
'message': '请求体不能为空'
|
||
}), 400
|
||
|
||
# 验证必填字段
|
||
required_fields = ['task_id', 'type', 'customer', 'trigger', 'action']
|
||
for field in required_fields:
|
||
if field not in data:
|
||
return jsonify({
|
||
'code': 400,
|
||
'message': f'缺少必填字段:{field}'
|
||
}), 400
|
||
|
||
# 添加时间戳
|
||
data['created_at'] = datetime.now().isoformat()
|
||
data['status'] = 'pending'
|
||
|
||
# 保存到数据库
|
||
success = task_manager.add_task(data)
|
||
|
||
if success:
|
||
logger.info(f"任务接收成功:{data['task_id']}")
|
||
return jsonify({
|
||
'code': 200,
|
||
'message': '任务接收成功',
|
||
'data': {
|
||
'task_id': data['task_id'],
|
||
'status': 'pending'
|
||
}
|
||
})
|
||
else:
|
||
logger.error(f"任务保存失败:{data['task_id']}")
|
||
return jsonify({
|
||
'code': 500,
|
||
'message': '任务保存失败'
|
||
}), 500
|
||
|
||
except Exception as e:
|
||
logger.error(f"接收任务异常:{e}")
|
||
return jsonify({
|
||
'code': 500,
|
||
'message': f'服务器错误:{str(e)}'
|
||
}), 500
|
||
|
||
|
||
@app.route('/api/task/cancel', methods=['POST'])
|
||
def cancel_task():
|
||
"""
|
||
取消任务
|
||
|
||
Request Body:
|
||
{
|
||
"task_id": "TASK_20260226_001",
|
||
"reason": "客户已退款"
|
||
}
|
||
"""
|
||
try:
|
||
data = request.get_json()
|
||
task_id = data.get('task_id')
|
||
reason = data.get('reason')
|
||
|
||
if not task_id:
|
||
return jsonify({
|
||
'code': 400,
|
||
'message': '缺少 task_id'
|
||
}), 400
|
||
|
||
task_manager.cancel_task(task_id, reason)
|
||
|
||
logger.info(f"任务已取消:{task_id}")
|
||
return jsonify({
|
||
'code': 200,
|
||
'message': '任务已取消',
|
||
'data': {
|
||
'task_id': task_id,
|
||
'status': 'cancelled'
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"取消任务异常:{e}")
|
||
return jsonify({
|
||
'code': 500,
|
||
'message': f'服务器错误:{str(e)}'
|
||
}), 500
|
||
|
||
|
||
@app.route('/api/task/status/<task_id>', methods=['GET'])
|
||
def get_task_status(task_id):
|
||
"""
|
||
查询任务状态
|
||
|
||
Response:
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"task_id": "TASK_20260226_001",
|
||
"status": "pending",
|
||
"created_at": "2026-02-26 16:30:00",
|
||
"triggered_at": null,
|
||
"completed_at": null
|
||
}
|
||
}
|
||
"""
|
||
try:
|
||
task = task_manager.get_task(task_id)
|
||
|
||
if not task:
|
||
return jsonify({
|
||
'code': 404,
|
||
'message': '任务不存在'
|
||
}), 404
|
||
|
||
return jsonify({
|
||
'code': 200,
|
||
'data': {
|
||
'task_id': task['task_id'],
|
||
'type': task['type'],
|
||
'status': task['status'],
|
||
'priority': task['priority'],
|
||
'retry_count': task['retry_count'],
|
||
'created_at': task['created_at'],
|
||
'created_by': task['created_by'],
|
||
'triggered_at': task.get('triggered_at'),
|
||
'completed_at': task.get('completed_at'),
|
||
'error_message': task.get('error_message')
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"查询任务状态异常:{e}")
|
||
return jsonify({
|
||
'code': 500,
|
||
'message': f'服务器错误:{str(e)}'
|
||
}), 500
|
||
|
||
|
||
@app.route('/api/task/list', methods=['GET'])
|
||
def list_tasks():
|
||
"""
|
||
查询任务列表
|
||
|
||
Query Params:
|
||
- customer_id: 客户 ID(可选)
|
||
- status: 任务状态(可选)
|
||
- page: 页码(默认 1)
|
||
- page_size: 每页数量(默认 20)
|
||
"""
|
||
try:
|
||
customer_id = request.args.get('customer_id')
|
||
status = request.args.get('status')
|
||
page = int(request.args.get('page', 1))
|
||
page_size = int(request.args.get('page_size', 20))
|
||
|
||
if status:
|
||
if status == 'pending':
|
||
tasks = task_manager.get_pending_tasks(customer_id)
|
||
else:
|
||
# TODO: 实现其他状态查询
|
||
tasks = []
|
||
else:
|
||
tasks = task_manager.get_pending_tasks(customer_id)
|
||
|
||
# 分页
|
||
total = len(tasks)
|
||
start = (page - 1) * page_size
|
||
end = start + page_size
|
||
tasks_page = tasks[start:end]
|
||
|
||
return jsonify({
|
||
'code': 200,
|
||
'data': {
|
||
'total': total,
|
||
'page': page,
|
||
'page_size': page_size,
|
||
'tasks': tasks_page
|
||
}
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"查询任务列表异常:{e}")
|
||
return jsonify({
|
||
'code': 500,
|
||
'message': f'服务器错误:{str(e)}'
|
||
}), 500
|
||
|
||
|
||
@app.route('/api/health', methods=['GET'])
|
||
def health_check():
|
||
"""健康检查"""
|
||
return jsonify({
|
||
'code': 200,
|
||
'message': 'OK',
|
||
'data': {
|
||
'timestamp': datetime.now().isoformat(),
|
||
'service': 'ai-cs-tianwang-bridge'
|
||
}
|
||
})
|
||
|
||
|
||
def start_http_server(host='0.0.0.0', port=6060, debug=False):
|
||
"""启动 HTTP 服务器"""
|
||
global task_manager, task_scheduler
|
||
|
||
task_manager = get_task_manager()
|
||
logger.info(f"HTTP API 服务器启动:http://{host}:{port}")
|
||
|
||
# 在新线程中运行 Flask
|
||
thread = threading.Thread(
|
||
target=app.run,
|
||
kwargs={
|
||
'host': host,
|
||
'port': port,
|
||
'debug': debug,
|
||
'use_reloader': False
|
||
},
|
||
daemon=True
|
||
)
|
||
thread.start()
|
||
|
||
return thread
|
||
|
||
|
||
if __name__ == '__main__':
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='[%(asctime)s] %(levelname)s: %(message)s'
|
||
)
|
||
|
||
start_http_server(port=6060, debug=True)
|