""" 工作流协作管理器 管理多人协作编辑工作流的WebSocket连接和消息同步 """ from typing import Dict, Set, List, Optional from fastapi import WebSocket import json import asyncio from datetime import datetime import logging logger = logging.getLogger(__name__) class CollaborationManager: """工作流协作管理器""" def __init__(self): # workflow_id -> Set[WebSocket] 映射,存储每个工作流的连接 self.active_connections: Dict[str, Set[WebSocket]] = {} # WebSocket -> user_info 映射,存储每个连接的用户信息 self.connection_users: Dict[WebSocket, Dict] = {} # workflow_id -> Dict[user_id, user_info] 映射,存储每个工作流的在线用户 self.workflow_users: Dict[str, Dict[str, Dict]] = {} # 操作锁,用于冲突解决 self.operation_locks: Dict[str, asyncio.Lock] = {} async def connect(self, websocket: WebSocket, workflow_id: str, user_id: str, username: str): """建立协作连接""" await websocket.accept() if workflow_id not in self.active_connections: self.active_connections[workflow_id] = set() self.workflow_users[workflow_id] = {} self.operation_locks[workflow_id] = asyncio.Lock() self.active_connections[workflow_id].add(websocket) user_info = { "user_id": user_id, "username": username, "joined_at": datetime.now().isoformat(), "color": self._get_user_color(user_id) # 为用户分配颜色 } self.connection_users[websocket] = user_info self.workflow_users[workflow_id][user_id] = user_info # 通知其他用户有新用户加入 await self.broadcast_user_joined(workflow_id, user_info, exclude_websocket=websocket) # 发送当前在线用户列表给新用户 await self.send_personal_message({ "type": "collaboration_init", "workflow_id": workflow_id, "current_user": user_info, "online_users": list(self.workflow_users[workflow_id].values()) }, websocket) logger.info(f"用户 {username} ({user_id}) 加入工作流 {workflow_id} 的协作编辑") def disconnect(self, websocket: WebSocket, workflow_id: str): """断开协作连接""" if workflow_id in self.active_connections: self.active_connections[workflow_id].discard(websocket) if websocket in self.connection_users: user_info = self.connection_users[websocket] user_id = user_info["user_id"] # 从工作流用户列表中移除 if user_id in self.workflow_users[workflow_id]: del self.workflow_users[workflow_id][user_id] # 通知其他用户有用户离开 self.broadcast_user_left(workflow_id, user_id, exclude_websocket=websocket) del self.connection_users[websocket] logger.info(f"用户 {user_info.get('username')} ({user_id}) 离开工作流 {workflow_id} 的协作编辑") # 如果没有连接了,清理资源 if not self.active_connections[workflow_id]: del self.active_connections[workflow_id] del self.workflow_users[workflow_id] if workflow_id in self.operation_locks: del self.operation_locks[workflow_id] async def broadcast_operation(self, workflow_id: str, operation: Dict, exclude_websocket: Optional[WebSocket] = None): """广播操作到所有连接的客户端""" if workflow_id not in self.active_connections: return message = { "type": "operation", "workflow_id": workflow_id, "operation": operation, "timestamp": datetime.now().isoformat() } disconnected = set() for websocket in self.active_connections[workflow_id]: if websocket == exclude_websocket: continue try: await websocket.send_json(message) except Exception as e: logger.warning(f"发送协作消息失败: {e}") disconnected.add(websocket) # 清理断开的连接 for ws in disconnected: self.disconnect(ws, workflow_id) async def broadcast_user_joined(self, workflow_id: str, user_info: Dict, exclude_websocket: Optional[WebSocket] = None): """广播用户加入消息""" if workflow_id not in self.active_connections: return message = { "type": "user_joined", "workflow_id": workflow_id, "user": user_info } disconnected = set() for websocket in self.active_connections[workflow_id]: if websocket == exclude_websocket: continue try: await websocket.send_json(message) except Exception as e: logger.warning(f"发送用户加入消息失败: {e}") disconnected.add(websocket) for ws in disconnected: self.disconnect(ws, workflow_id) async def broadcast_user_left(self, workflow_id: str, user_id: str, exclude_websocket: Optional[WebSocket] = None): """广播用户离开消息""" if workflow_id not in self.active_connections: return message = { "type": "user_left", "workflow_id": workflow_id, "user_id": user_id } disconnected = set() for websocket in self.active_connections[workflow_id]: if websocket == exclude_websocket: continue try: await websocket.send_json(message) except Exception as e: logger.warning(f"发送用户离开消息失败: {e}") disconnected.add(websocket) for ws in disconnected: self.disconnect(ws, workflow_id) async def send_personal_message(self, message: Dict, websocket: WebSocket): """发送个人消息""" try: await websocket.send_json(message) except Exception as e: logger.warning(f"发送个人消息失败: {e}") def get_online_users(self, workflow_id: str) -> List[Dict]: """获取在线用户列表""" if workflow_id not in self.workflow_users: return [] return list(self.workflow_users[workflow_id].values()) def _get_user_color(self, user_id: str) -> str: """为用户分配颜色(基于用户ID的哈希)""" colors = [ "#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A", "#98D8C8", "#F7DC6F", "#BB8FCE", "#85C1E2", "#F8B739", "#52BE80" ] hash_value = hash(user_id) % len(colors) return colors[hash_value] async def acquire_lock(self, workflow_id: str): """获取操作锁(用于冲突解决)""" if workflow_id not in self.operation_locks: self.operation_locks[workflow_id] = asyncio.Lock() return await self.operation_locks[workflow_id].acquire() def release_lock(self, workflow_id: str): """释放操作锁""" if workflow_id in self.operation_locks: self.operation_locks[workflow_id].release() # 全局协作管理器实例 collaboration_manager = CollaborationManager()