feat: 完整功能部署 v1.0

新增功能:
- 天网协作系统 (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
This commit is contained in:
2026-02-28 11:20:40 +08:00
parent 5aedf1665d
commit a6c42d505a
171 changed files with 7979 additions and 328 deletions

0
db/__init__.py Normal file → Executable file
View File

0
db/__pycache__/__init__.cpython-310.pyc Normal file → Executable file
View File

0
db/__pycache__/chat_log_db.cpython-310.pyc Normal file → Executable file
View File

0
db/__pycache__/customer_db.cpython-310.pyc Normal file → Executable file
View File

0
db/__pycache__/deal_outcome_db.cpython-310.pyc Normal file → Executable file
View File

0
db/__pycache__/designer_roster_db.cpython-310.pyc Normal file → Executable file
View File

0
db/chat_log_db.py Normal file → Executable file
View File

0
db/chat_log_db/chats.db Normal file → Executable file
View File

0
db/customer_db.py Normal file → Executable file
View File

0
db/deal_outcome_db.py Normal file → Executable file
View File

0
db/deal_outcome_db/outcomes.db Normal file → Executable file
View File

0
db/designer_roster_db.py Normal file → Executable file
View File

0
db/designer_roster_db/roster.db Normal file → Executable file
View File

412
db/image_tasks_db.py Normal file
View File

@@ -0,0 +1,412 @@
# -*- coding: utf-8 -*-
"""
图片任务数据库管理
支持客户后续增加需求细节
"""
import sqlite3
import json
import logging
from typing import Optional, List, Dict
from pathlib import Path
from datetime import datetime
from enum import Enum
logger = logging.getLogger(__name__)
class TaskStatus(Enum):
"""任务状态"""
PENDING = "pending" # 待付款
PAID = "paid" # 已付款,待处理
PROCESSING = "processing" # 处理中
AWAITING_CONFIRM = "awaiting_confirm" # 已完成,待客户确认
COMPLETED = "completed" # 已完成
FAILED = "failed" # 失败
CANCELLED = "cancelled" # 已取消
class ImageTaskManager:
"""图片任务管理器"""
def __init__(self, db_path: str = None):
if db_path is None:
db_path = Path(__file__).parent / "image_tasks.db"
self.db_path = db_path
self._init_db()
logger.info(f"图片任务管理器初始化完成,数据库:{self.db_path}")
def _init_db(self):
"""初始化数据库"""
self.db_path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 创建图片任务表
cursor.execute('''
CREATE TABLE IF NOT EXISTS image_tasks (
task_id TEXT PRIMARY KEY,
customer_id TEXT NOT NULL,
customer_name TEXT,
original_image TEXT NOT NULL,
operation TEXT DEFAULT 'enhance',
requirements TEXT, -- JSON 格式:复杂度、比例、透视等
customer_notes TEXT, -- 客户备注/需求细节
status TEXT DEFAULT 'pending',
created_at TEXT,
paid_at TEXT,
started_at TEXT,
completed_at TEXT,
result_image TEXT,
error_message TEXT,
retry_count INTEGER DEFAULT 0,
-- 店铺信息
acc_id TEXT,
acc_type TEXT DEFAULT 'AliWorkbench'
)
''')
# 创建需求变更记录表(支持客户后续增加需求)
cursor.execute('''
CREATE TABLE IF NOT EXISTS task_requirement_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
change_type TEXT, -- add_note/modify_operation/add_requirement
old_value TEXT,
new_value TEXT,
changed_at TEXT,
changed_by TEXT, -- customer/staff
FOREIGN KEY (task_id) REFERENCES image_tasks(task_id)
)
''')
# 创建索引
cursor.execute('CREATE INDEX IF NOT EXISTS idx_customer ON image_tasks(customer_id)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_status ON image_tasks(status)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_created ON image_tasks(created_at)')
conn.commit()
conn.close()
logger.info("数据库表初始化完成")
def _get_conn(self):
"""获取数据库连接"""
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
return conn
def create_task(self, task_id: str, customer_id: str, customer_name: str,
original_image: str, operation: str = 'enhance',
requirements: dict = None, acc_id: str = '', acc_type: str = 'AliWorkbench') -> bool:
"""创建图片任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
requirements_json = json.dumps(requirements) if requirements else None
cursor.execute('''
INSERT INTO image_tasks (
task_id, customer_id, customer_name, original_image,
operation, requirements, customer_notes, status,
created_at, acc_id, acc_type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
task_id,
customer_id,
customer_name,
original_image,
operation,
requirements_json,
'', # 初始备注为空
TaskStatus.PENDING.value,
datetime.now().isoformat(),
acc_id,
acc_type
))
conn.commit()
conn.close()
logger.info(f"图片任务创建成功:{task_id}")
return True
except Exception as e:
logger.error(f"创建图片任务失败:{e}")
return False
def get_task(self, task_id: str) -> Optional[dict]:
"""查询任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('SELECT * FROM image_tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
conn.close()
if row:
task = dict(row)
# 解析 JSON 字段
if task.get('requirements'):
task['requirements'] = json.loads(task['requirements'])
return task
return None
except Exception as e:
logger.error(f"查询任务失败:{e}")
return None
def get_customer_tasks(self, customer_id: str, status: str = None) -> List[dict]:
"""查询客户的任务列表"""
try:
conn = self._get_conn()
cursor = conn.cursor()
if status:
cursor.execute('''
SELECT * FROM image_tasks
WHERE customer_id = ? AND status = ?
ORDER BY created_at DESC
''', (customer_id, status))
else:
cursor.execute('''
SELECT * FROM image_tasks
WHERE customer_id = ?
ORDER BY created_at DESC
''', (customer_id,))
rows = cursor.fetchall()
conn.close()
tasks = []
for row in rows:
task = dict(row)
if task.get('requirements'):
task['requirements'] = json.loads(task['requirements'])
tasks.append(task)
return tasks
except Exception as e:
logger.error(f"查询客户任务失败:{e}")
return []
def update_status(self, task_id: str, status: TaskStatus):
"""更新任务状态"""
try:
conn = self._get_conn()
cursor = conn.cursor()
updates = ['status = ?']
params = [status.value]
# 根据状态设置时间
if status == TaskStatus.PAID:
updates.append('paid_at = ?')
params.append(datetime.now().isoformat())
elif status == TaskStatus.PROCESSING:
updates.append('started_at = ?')
params.append(datetime.now().isoformat())
elif status in [TaskStatus.COMPLETED, TaskStatus.FAILED]:
updates.append('completed_at = ?')
params.append(datetime.now().isoformat())
params.append(task_id)
cursor.execute(f'''
UPDATE image_tasks
SET {', '.join(updates)}
WHERE task_id = ?
''', params)
conn.commit()
conn.close()
logger.info(f"任务状态更新:{task_id} -> {status.value}")
except Exception as e:
logger.error(f"更新任务状态失败:{e}")
def update_result(self, task_id: str, result_image: str, error_message: str = None):
"""更新处理结果"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('''
UPDATE image_tasks
SET result_image = ?, error_message = ?
WHERE task_id = ?
''', (result_image, error_message, task_id))
conn.commit()
conn.close()
logger.info(f"任务结果更新:{task_id}")
except Exception as e:
logger.error(f"更新任务结果失败:{e}")
def add_customer_note(self, task_id: str, note: str, changed_by: str = 'customer') -> bool:
"""
客户添加需求备注(支持后续增加细节)
Args:
task_id: 任务 ID
note: 备注内容
changed_by: 修改者customer/staff
Returns:
bool: 是否成功
"""
try:
conn = self._get_conn()
cursor = conn.cursor()
# 获取旧备注
cursor.execute('SELECT customer_notes FROM image_tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
old_note = row['customer_notes'] if row else ''
# 更新备注
new_note = f"{old_note}\n[{datetime.now().strftime('%m-%d %H:%M')}] {note}" if old_note else f"[{datetime.now().strftime('%m-%d %H:%M')}] {note}"
cursor.execute('''
UPDATE image_tasks
SET customer_notes = ?
WHERE task_id = ?
''', (new_note, task_id))
# 记录变更历史
cursor.execute('''
INSERT INTO task_requirement_changes (
task_id, change_type, old_value, new_value, changed_at, changed_by
) VALUES (?, ?, ?, ?, ?, ?)
''', (
task_id,
'add_note',
old_note or '',
note,
datetime.now().isoformat(),
changed_by
))
conn.commit()
conn.close()
logger.info(f"客户添加备注成功:{task_id}")
return True
except Exception as e:
logger.error(f"添加客户备注失败:{e}")
return False
def modify_operation(self, task_id: str, new_operation: str, changed_by: str = 'customer') -> bool:
"""
修改操作类型(客户后续修改需求)
Args:
task_id: 任务 ID
new_operation: 新操作类型
changed_by: 修改者
Returns:
bool: 是否成功
"""
try:
conn = self._get_conn()
cursor = conn.cursor()
# 获取旧操作
cursor.execute('SELECT operation FROM image_tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
old_operation = row['operation'] if row else ''
# 更新操作
cursor.execute('''
UPDATE image_tasks
SET operation = ?
WHERE task_id = ?
''', (new_operation, task_id))
# 记录变更历史
cursor.execute('''
INSERT INTO task_requirement_changes (
task_id, change_type, old_value, new_value, changed_at, changed_by
) VALUES (?, ?, ?, ?, ?, ?)
''', (
task_id,
'modify_operation',
old_operation,
new_operation,
datetime.now().isoformat(),
changed_by
))
conn.commit()
conn.close()
logger.info(f"修改操作类型成功:{task_id} -> {new_operation}")
return True
except Exception as e:
logger.error(f"修改操作类型失败:{e}")
return False
def get_requirement_history(self, task_id: str) -> List[dict]:
"""获取需求变更历史"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM task_requirement_changes
WHERE task_id = ?
ORDER BY changed_at DESC
''', (task_id,))
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
except Exception as e:
logger.error(f"查询需求历史失败:{e}")
return []
def get_pending_tasks(self) -> List[dict]:
"""获取所有待处理任务"""
return self.get_customer_tasks('', 'pending')
def increment_retry(self, task_id: str) -> int:
"""增加重试次数"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('''
UPDATE image_tasks
SET retry_count = retry_count + 1
WHERE task_id = ?
''', (task_id,))
cursor.execute('SELECT retry_count FROM image_tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
conn.close()
return row['retry_count'] if row else 0
except Exception as e:
logger.error(f"增加重试次数失败:{e}")
return 999
# 单例
_task_manager: Optional[ImageTaskManager] = None
def get_image_task_manager() -> ImageTaskManager:
"""获取图片任务管理器单例"""
global _task_manager
if _task_manager is None:
_task_manager = ImageTaskManager()
return _task_manager

Binary file not shown.

321
db/task_db/task_model.py Normal file
View File

@@ -0,0 +1,321 @@
# -*- coding: utf-8 -*-
"""
天网任务数据模型
"""
import sqlite3
import json
import logging
from datetime import datetime, timedelta
from typing import Optional, Dict, List
from pathlib import Path
from enum import Enum
logger = logging.getLogger(__name__)
class TaskStatus(Enum):
"""任务状态"""
PENDING = "pending" # 待触发
WAITING = "waiting" # 等待触发
RUNNING = "running" # 执行中
COMPLETED = "completed" # 已完成
FAILED = "failed" # 失败
CANCELLED = "cancelled" # 已取消
class TaskPriority(Enum):
"""任务优先级"""
NORMAL = "normal"
HIGH = "high"
URGENT = "urgent"
class TaskManager:
"""任务管理器 - SQLite 存储"""
def __init__(self, db_path: str = None):
if db_path is None:
db_path = Path(__file__).parent / "tasks.db"
self.db_path = db_path
self._init_db()
logger.info(f"任务管理器初始化完成,数据库:{self.db_path}")
def _init_db(self):
"""初始化数据库"""
self.db_path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 创建任务表
cursor.execute('''
CREATE TABLE IF NOT EXISTS tasks (
task_id TEXT PRIMARY KEY,
specified_customer_id TEXT,
specified_customer_name TEXT,
type TEXT NOT NULL,
customer_name TEXT,
customer_id TEXT,
trigger_type TEXT,
trigger_keyword TEXT,
trigger_keywords TEXT, -- JSON array
action_type TEXT,
action_file_url TEXT,
action_message TEXT,
priority TEXT DEFAULT 'normal',
timeout_hours INTEGER DEFAULT 24,
status TEXT DEFAULT 'pending',
retry_count INTEGER DEFAULT 0,
max_retry INTEGER DEFAULT 3,
created_at TEXT,
created_by TEXT,
triggered_at TEXT,
completed_at TEXT,
error_message TEXT,
result TEXT -- JSON
)
''')
# 创建索引
cursor.execute('CREATE INDEX IF NOT EXISTS idx_status ON tasks(status)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_customer ON tasks(customer_id)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_created ON tasks(created_at)')
conn.commit()
conn.close()
logger.info("数据库表初始化完成")
def _get_conn(self):
"""获取数据库连接"""
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
return conn
def add_task(self, task: dict) -> bool:
"""添加任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
# 处理 keywords 数组
trigger_keywords = task.get('trigger', {}).get('keywords', [])
if isinstance(trigger_keywords, list):
trigger_keywords = json.dumps(trigger_keywords)
cursor.execute('''
INSERT OR REPLACE INTO tasks (
task_id, specified_customer_id, specified_customer_name,
type, customer_name, customer_id,
trigger_type, trigger_keyword, trigger_keywords,
action_type, action_file_url, action_message,
priority, timeout_hours, status, retry_count, max_retry,
created_at, created_by
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
task.get('task_id'),
task.get('customer', {}).get('id'),
task.get('customer', {}).get('name'),
task.get('type'),
task.get('customer', {}).get('name'),
task.get('customer', {}).get('id'),
task.get('trigger', {}).get('type'),
task.get('trigger', {}).get('keyword'),
trigger_keywords,
task.get('action', {}).get('type'),
task.get('action', {}).get('file_url'),
task.get('action', {}).get('message'),
task.get('priority', 'normal'),
task.get('timeout_hours', 24),
task.get('status', 'pending'),
task.get('retry_count', 0),
task.get('max_retry', 3),
task.get('created_at', datetime.now().isoformat()),
task.get('created_by')
))
conn.commit()
conn.close()
logger.info(f"任务添加成功:{task.get('task_id')}")
return True
except Exception as e:
logger.error(f"添加任务失败:{e}")
return False
def get_task(self, task_id: str) -> Optional[dict]:
"""查询任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('SELECT * FROM tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
conn.close()
if row:
return dict(row)
return None
except Exception as e:
logger.error(f"查询任务失败:{e}")
return None
def update_task_status(self, task_id: str, status: TaskStatus, error_message: str = None, result: dict = None):
"""更新任务状态"""
try:
conn = self._get_conn()
cursor = conn.cursor()
updates = ['status = ?']
params = [status.value]
if status == TaskStatus.RUNNING:
updates.append('triggered_at = ?')
params.append(datetime.now().isoformat())
if status in [TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED]:
updates.append('completed_at = ?')
params.append(datetime.now().isoformat())
if error_message:
updates.append('error_message = ?')
params.append(error_message)
if result:
updates.append('result = ?')
params.append(json.dumps(result))
params.append(task_id)
cursor.execute(f'''
UPDATE tasks
SET {', '.join(updates)}
WHERE task_id = ?
''', params)
conn.commit()
conn.close()
logger.info(f"任务状态更新:{task_id} -> {status.value}")
except Exception as e:
logger.error(f"更新任务状态失败:{e}")
def increment_retry(self, task_id: str) -> int:
"""增加重试次数,返回当前重试次数"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('''
UPDATE tasks
SET retry_count = retry_count + 1
WHERE task_id = ?
''', (task_id,))
cursor.execute('SELECT retry_count, max_retry FROM tasks WHERE task_id = ?', (task_id,))
row = cursor.fetchone()
conn.close()
if row:
return row['retry_count']
return 0
except Exception as e:
logger.error(f"增加重试次数失败:{e}")
return 999 # 超过最大重试次数
def get_pending_tasks(self, customer_id: str = None) -> List[dict]:
"""获取待触发的任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
if customer_id:
cursor.execute('''
SELECT * FROM tasks
WHERE status = 'pending'
AND customer_id = ?
ORDER BY
CASE priority
WHEN 'urgent' THEN 1
WHEN 'high' THEN 2
ELSE 3
END,
created_at
''', (customer_id,))
else:
cursor.execute('''
SELECT * FROM tasks
WHERE status = 'pending'
ORDER BY
CASE priority
WHEN 'urgent' THEN 1
WHEN 'high' THEN 2
ELSE 3
END,
created_at
''')
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
except Exception as e:
logger.error(f"获取待触发任务失败:{e}")
return []
def get_timeout_tasks(self) -> List[dict]:
"""获取超时任务"""
try:
conn = self._get_conn()
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM tasks
WHERE status = 'pending'
AND datetime(created_at, '+' || timeout_hours || ' hours') < datetime('now')
''')
rows = cursor.fetchall()
conn.close()
return [dict(row) for row in rows]
except Exception as e:
logger.error(f"获取超时任务失败:{e}")
return []
def cancel_task(self, task_id: str, reason: str = None):
"""取消任务"""
self.update_task_status(task_id, TaskStatus.CANCELLED, error_message=reason)
logger.info(f"任务已取消:{task_id}")
def get_statistics(self) -> dict:
"""获取任务统计"""
try:
conn = self._get_conn()
cursor = conn.cursor()
stats = {}
for status in TaskStatus:
cursor.execute('SELECT COUNT(*) FROM tasks WHERE status = ?', (status.value,))
stats[status.value] = cursor.fetchone()[0]
conn.close()
return stats
except Exception as e:
logger.error(f"获取统计失败:{e}")
return {}
# 单例
_task_manager: Optional[TaskManager] = None
def get_task_manager() -> TaskManager:
"""获取任务管理器单例"""
global _task_manager
if _task_manager is None:
_task_manager = TaskManager()
return _task_manager

BIN
db/task_db/tasks.db Normal file

Binary file not shown.