#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 智能轮询派单系统 - 自动轮询派单队列 - 负载均衡:轮流分配 + 工作量感知 - 自动重试机制 - 详细日志记录 """ import requests import time import json from datetime import datetime from collections import deque # 配置 API_BASE = "http://1.12.50.92:8005" API_KEY = "tuhui_dispatch_key_2026" HEADERS = {"X-API-Key": API_KEY} # 轮询间隔(秒) POLL_INTERVAL = 10 MAX_RETRIES = 3 RETRY_DELAY = 3 # 负载均衡:记录最近分配的设计师(轮询分配) recent_assignments = deque(maxlen=10) def log(message: str, level: str = "INFO"): """打印日志""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print(f"[{timestamp}] [{level}] {message}") def get_online_designers() -> list: """获取在线设计师""" try: response = requests.get(f"{API_BASE}/online/designers", headers=HEADERS, timeout=5) if response.status_code == 200: data = response.json() return data["online_users"] except Exception as e: log(f"获取在线设计师失败:{e}", "ERROR") return [] def get_pending_tasks(limit: int = 10) -> list: """获取待分配任务""" try: response = requests.get(f"{API_BASE}/tasks/pending", headers=HEADERS, timeout=5, params={"limit": limit}) if response.status_code == 200: data = response.json() return data["tasks"] except Exception as e: log(f"获取待分配任务失败:{e}", "ERROR") return [] def get_designer_workload() -> dict: """获取设计师工作量""" try: response = requests.get(f"{API_BASE}/designers/workload", headers=HEADERS, timeout=5) if response.status_code == 200: data = response.json() # 转换为字典:设计师 -> 待处理任务数 return {d["designer"]: d["pending_tasks"] for d in data["designers"]} except Exception as e: log(f"获取工作量失败:{e}", "ERROR") return {} def select_designer_smart(online_designers: list) -> str: """ 智能选择设计师(负载均衡) 策略: 1. 优先选最近没被分配的设计师(轮询) 2. 如果都有任务,选工作量最少的 3. 如果都没数据,随机选 """ if not online_designers: return None if len(online_designers) == 1: return online_designers[0] # 策略 1:找最近没被分配的 for designer in online_designers: if designer not in recent_assignments: log(f"选择设计师(轮询):{designer}") return designer # 策略 2:找工作量最少的 workload = get_designer_workload() min_workload = float('inf') selected = None for designer in online_designers: pending = workload.get(designer, 0) if pending < min_workload: min_workload = pending selected = designer if selected: log(f"选择设计师(工作量最少:{min_workload}):{selected}") return selected # 策略 3:随机选第一个 log(f"选择设计师(默认):{online_designers[0]}") return online_designers[0] def assign_task_with_retry(task_id: str, designer: str, max_retries: int = MAX_RETRIES) -> bool: """带重试的分配任务""" for attempt in range(max_retries): try: response = requests.post( f"{API_BASE}/tasks/{task_id}/assign", headers=HEADERS, json={ "designer_name": designer, "notes": "系统自动派单" }, timeout=5 ) if response.status_code == 200: log(f"✅ 任务 {task_id} 已分配给 {designer}", "SUCCESS") # 记录分配历史 recent_assignments.append(designer) return True else: log(f"派单失败 (尝试 {attempt+1}/{max_retries}): {response.text}", "ERROR") except Exception as e: log(f"网络错误 (尝试 {attempt+1}/{max_retries}): {e}", "ERROR") if attempt < max_retries - 1: time.sleep(RETRY_DELAY) return False def poll_and_dispatch(): """主轮询函数""" log("🚀 智能轮询派单系统启动", "START") log(f"轮询间隔:{POLL_INTERVAL}秒") log(f"API 地址:{API_BASE}") consecutive_errors = 0 max_consecutive_errors = 10 while True: try: # 1. 获取待分配任务 tasks = get_pending_tasks(limit=20) if not tasks: log("📭 暂无待分配任务") consecutive_errors = 0 time.sleep(POLL_INTERVAL) continue log(f"📋 待分配任务:{len(tasks)} 个") # 2. 获取在线设计师 online_designers = get_online_designers() if not online_designers: log("😴 暂无设计师在线") consecutive_errors = 0 time.sleep(POLL_INTERVAL) continue log(f"👥 在线设计师:{len(online_designers)} 人 - {', '.join(online_designers)}") # 3. 按优先级排序任务 tasks.sort(key=lambda x: x["priority"], reverse=True) # 4. 智能分配 assigned_count = 0 for task in tasks: designer = select_designer_smart(online_designers) if designer: success = assign_task_with_retry(task["task_id"], designer) if success: assigned_count += 1 # 短暂等待,避免并发冲突 time.sleep(0.5) else: log(f"❌ 任务 {task['task_id']} 分配失败", "ERROR") break # 停止分配,下次轮询再试 else: log("⚠️ 无法选择设计师", "ERROR") break log(f"📊 本轮分配:{assigned_count}/{len(tasks)} 个任务", "SUCCESS") consecutive_errors = 0 except Exception as e: log(f"❌ 轮询异常:{e}", "ERROR") consecutive_errors += 1 if consecutive_errors >= max_consecutive_errors: log(f"⚠️ 连续 {consecutive_errors} 次错误,等待 30 秒...", "WARNING") time.sleep(30) consecutive_errors = 0 else: time.sleep(POLL_INTERVAL) # 正常轮询间隔 time.sleep(POLL_INTERVAL) if __name__ == "__main__": try: poll_and_dispatch() except KeyboardInterrupt: log("👋 轮询派单系统已停止", "INFO")