/** * WebSocket客户端管理器 * 提供实时任务状态更新,替代传统轮询机制 */ class WebSocketTaskClient { constructor() { this.socket = null; this.connected = false; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 1000; // 1秒 this.subscribedTasks = new Set(); this.taskCallbacks = new Map(); // task_id -> callback function this.init(); } init() { // 检查是否支持WebSocket if (!window.io) { console.warn('Socket.IO未加载,回退到轮询模式'); return false; } try { this.socket = io({ transports: ['websocket', 'polling'], upgrade: true, rememberUpgrade: true }); this.setupEventHandlers(); return true; } catch (error) { console.error('WebSocket初始化失败:', error); return false; } } setupEventHandlers() { // 连接成功 this.socket.on('connect', () => { console.log('WebSocket连接成功'); this.connected = true; this.reconnectAttempts = 0; // 重新订阅之前的任务 this.subscribedTasks.forEach(taskId => { this.subscribeTask(taskId); }); }); // 连接断开 this.socket.on('disconnect', (reason) => { console.log('WebSocket连接断开:', reason); this.connected = false; // 自动重连 if (reason === 'io server disconnect') { // 服务器主动断开,不重连 console.log('服务器主动断开连接'); } else { // 网络问题,尝试重连 this.attemptReconnect(); } }); // 任务状态更新 this.socket.on('task_status_update', (data) => { console.log('收到任务状态更新:', data); this.handleTaskStatusUpdate(data); }); // 任务不存在 this.socket.on('task_not_found', (data) => { console.warn('任务不存在:', data.task_id); this.handleTaskNotFound(data.task_id); }); // 连接确认 this.socket.on('connected', (data) => { console.log('WebSocket连接确认:', data); }); // 错误处理 this.socket.on('error', (error) => { console.error('WebSocket错误:', error); }); // 连接错误 this.socket.on('connect_error', (error) => { console.error('WebSocket连接错误:', error); this.attemptReconnect(); }); } attemptReconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('WebSocket重连次数超限,回退到轮询模式'); return; } this.reconnectAttempts++; const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); // 指数退避 console.log(`WebSocket重连尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts},${delay}ms后重试`); setTimeout(() => { if (!this.connected) { this.socket.connect(); } }, delay); } subscribeTask(taskId, callback = null) { if (!this.socket || !this.connected) { console.warn('WebSocket未连接,无法订阅任务:', taskId); return false; } console.log('订阅任务状态:', taskId); this.subscribedTasks.add(taskId); if (callback) { this.taskCallbacks.set(taskId, callback); } this.socket.emit('subscribe_task', { task_id: taskId }); return true; } unsubscribeTask(taskId) { if (!this.socket) { return; } console.log('取消订阅任务:', taskId); this.subscribedTasks.delete(taskId); this.taskCallbacks.delete(taskId); if (this.connected) { this.socket.emit('unsubscribe_task', { task_id: taskId }); } } handleTaskStatusUpdate(data) { const taskId = data.task_id; const status = data.status; // 调用任务特定的回调函数 if (this.taskCallbacks.has(taskId)) { const callback = this.taskCallbacks.get(taskId); try { callback(data); } catch (error) { console.error('任务回调函数执行错误:', error); } } // 如果任务完成,自动取消订阅 if (['completed', 'failed', 'cancelled'].includes(status)) { console.log(`任务 ${taskId} 已完成,状态: ${status}`); setTimeout(() => { this.unsubscribeTask(taskId); }, 1000); // 1秒后取消订阅 } // 触发全局事件 window.dispatchEvent(new CustomEvent('taskStatusUpdate', { detail: data })); } handleTaskNotFound(taskId) { console.warn(`任务 ${taskId} 不存在,可能已被清理`); // 触发任务不存在事件 window.dispatchEvent(new CustomEvent('taskNotFound', { detail: { task_id: taskId } })); // 自动取消订阅 this.unsubscribeTask(taskId); } isConnected() { return this.connected; } getSubscribedTasks() { return Array.from(this.subscribedTasks); } disconnect() { if (this.socket) { this.socket.disconnect(); } } } // 创建全局WebSocket客户端实例 window.wsTaskClient = new WebSocketTaskClient(); // 提供便捷的全局函数 window.subscribeTaskStatus = function(taskId, callback) { if (window.wsTaskClient && window.wsTaskClient.isConnected()) { return window.wsTaskClient.subscribeTask(taskId, callback); } return false; }; window.unsubscribeTaskStatus = function(taskId) { if (window.wsTaskClient) { window.wsTaskClient.unsubscribeTask(taskId); } }; // 页面卸载时清理连接 window.addEventListener('beforeunload', function() { if (window.wsTaskClient) { window.wsTaskClient.disconnect(); } }); console.log('WebSocket任务客户端已初始化');