# WebSocket 任务通知接收示例 ## 一、WebSocket 配置说明 ### 后端配置 - **连接端点**: `/user/websocket`(支持 SockJS 备用方案) - **用户前缀**: `/user` - **订阅目的地**: `/user/queue/tasks-progress` - **协议**: STOMP over WebSocket ### 消息格式(TaskProgressDto) ```typescript interface TaskProgressDto { taskNo: string; // 任务编号 status: string; // 任务状态: created/queued/processing/completed/failed progress: number; // 进度百分比 0-100 message: string; // 进度消息 resultUrl?: string; // 结果URL(完成时) errorMessage?: string; // 错误信息(失败时) } ``` --- ## 二、前端依赖安装 ### 使用 npm ```bash npm install @stomp/stompjs sockjs-client ``` ### 使用 yarn ```bash yarn add @stomp/stompjs sockjs-client ``` ### CDN 引入(HTML) ```html ``` --- ## 三、基础连接示例 ### 1. 原生 JavaScript + STOMP.js ```javascript // 引入依赖(如果使用模块化) import SockJS from 'sockjs-client'; import { Client } from '@stomp/stompjs'; // WebSocket 配置 const WEBSOCKET_URL = 'http://localhost:8081/ws'; const AUTH_TOKEN = 'YOUR_JWT_TOKEN'; // 创建 STOMP 客户端 const stompClient = new Client({ // 使用 SockJS 作为 WebSocket 实现 webSocketFactory: () => new SockJS(WEBSOCKET_URL), // 连接头(携带认证信息) connectHeaders: { Authorization: `Bearer ${AUTH_TOKEN}` }, // 连接成功回调 onConnect: (frame) => { console.log('WebSocket 连接成功:', frame); // 订阅任务进度更新 stompClient.subscribe('/user/queue/tasks-progress', (message) => { const notification = JSON.parse(message.body); console.log('收到任务通知:', notification); // 处理通知 handleTaskNotification(notification); }); }, // 连接失败回调 onStompError: (frame) => { console.error('STOMP 错误:', frame); }, // WebSocket 错误回调 onWebSocketError: (error) => { console.error('WebSocket 错误:', error); }, // WebSocket 关闭回调 onWebSocketClose: (event) => { console.log('WebSocket 连接关闭:', event); }, // 自动重连配置 reconnectDelay: 5000, // 5秒后重连 heartbeatIncoming: 4000, heartbeatOutgoing: 4000 }); // 激活连接 stompClient.activate(); // 处理任务通知 function handleTaskNotification(notification) { const { taskNo, status, progress, message, resultUrl, errorMessage } = notification; switch(status) { case 'created': console.log(`[${taskNo}] 任务已创建`); break; case 'queued': console.log(`[${taskNo}] 任务排队中: ${message}`); break; case 'processing': console.log(`[${taskNo}] 处理中 ${progress}%: ${message}`); updateProgressBar(taskNo, progress); break; case 'completed': console.log(`[${taskNo}] 任务完成: ${resultUrl}`); showCompletedNotification(taskNo, resultUrl); break; case 'failed': console.error(`[${taskNo}] 任务失败: ${errorMessage || message}`); showErrorNotification(taskNo, errorMessage || message); break; } } // 断开连接 function disconnect() { if (stompClient.connected) { stompClient.deactivate(); console.log('WebSocket 已断开'); } } ``` --- ## 四、完整封装类(推荐) ### TaskWebSocketClient.js ```javascript import SockJS from 'sockjs-client'; import { Client } from '@stomp/stompjs'; class TaskWebSocketClient { constructor(baseUrl = 'http://localhost:8081', token) { this.baseUrl = baseUrl; this.token = token; this.client = null; this.listeners = { onTaskUpdate: [], onConnect: [], onDisconnect: [], onError: [] }; } /** * 连接 WebSocket */ connect() { if (this.client && this.client.connected) { console.warn('WebSocket 已连接'); return Promise.resolve(); } return new Promise((resolve, reject) => { this.client = new Client({ webSocketFactory: () => new SockJS(`${this.baseUrl}/ws`), connectHeaders: { Authorization: `Bearer ${this.token}` }, onConnect: (frame) => { console.log('✅ WebSocket 连接成功'); // 订阅任务进度更新 this.client.subscribe('/user/queue/tasks-progress', (message) => { try { const notification = JSON.parse(message.body); this._notifyListeners('onTaskUpdate', notification); } catch (error) { console.error('解析消息失败:', error); } }); this._notifyListeners('onConnect', frame); resolve(frame); }, onStompError: (frame) => { console.error('❌ STOMP 错误:', frame); this._notifyListeners('onError', frame); reject(frame); }, onWebSocketError: (error) => { console.error('❌ WebSocket 错误:', error); this._notifyListeners('onError', error); }, onWebSocketClose: (event) => { console.log('🔌 WebSocket 连接关闭'); this._notifyListeners('onDisconnect', event); }, reconnectDelay: 5000, heartbeatIncoming: 4000, heartbeatOutgoing: 4000 }); this.client.activate(); }); } /** * 断开 WebSocket */ disconnect() { if (this.client && this.client.connected) { this.client.deactivate(); console.log('WebSocket 已断开'); } } /** * 添加任务更新监听器 */ onTaskUpdate(callback) { this.listeners.onTaskUpdate.push(callback); return () => this._removeListener('onTaskUpdate', callback); } /** * 添加连接监听器 */ onConnect(callback) { this.listeners.onConnect.push(callback); return () => this._removeListener('onConnect', callback); } /** * 添加断开连接监听器 */ onDisconnect(callback) { this.listeners.onDisconnect.push(callback); return () => this._removeListener('onDisconnect', callback); } /** * 添加错误监听器 */ onError(callback) { this.listeners.onError.push(callback); return () => this._removeListener('onError', callback); } /** * 通知所有监听器 */ _notifyListeners(event, data) { this.listeners[event].forEach(callback => { try { callback(data); } catch (error) { console.error(`监听器执行错误 (${event}):`, error); } }); } /** * 移除监听器 */ _removeListener(event, callback) { const index = this.listeners[event].indexOf(callback); if (index > -1) { this.listeners[event].splice(index, 1); } } /** * 检查连接状态 */ isConnected() { return this.client && this.client.connected; } } export default TaskWebSocketClient; ``` --- ## 五、使用示例 ### 1. React 组件示例 ```jsx import React, { useEffect, useState } from 'react'; import TaskWebSocketClient from './TaskWebSocketClient'; function TaskMonitor() { const [tasks, setTasks] = useState({}); const [wsClient, setWsClient] = useState(null); useEffect(() => { // 获取 Token(从 localStorage 或其他地方) const token = localStorage.getItem('jwt_token'); // 创建 WebSocket 客户端 const client = new TaskWebSocketClient('http://localhost:8081', token); // 监听任务更新 const unsubscribe = client.onTaskUpdate((notification) => { console.log('任务更新:', notification); // 更新任务状态 setTasks(prev => ({ ...prev, [notification.taskNo]: notification })); // 根据状态显示不同提示 if (notification.status === 'completed') { showSuccessToast(`任务 ${notification.taskNo} 已完成!`); } else if (notification.status === 'failed') { showErrorToast(`任务 ${notification.taskNo} 失败: ${notification.errorMessage}`); } }); // 连接 WebSocket client.connect().catch(error => { console.error('连接失败:', error); }); setWsClient(client); // 清理函数 return () => { unsubscribe(); client.disconnect(); }; }, []); return (