import { getToken } from '@/utils/auth' /** * 当前 WebSocket 实例 */ let ws = null /** * 重连定时器 */ let reconnectTimer = null /** * 是否人为关闭(用于区分主动关闭 vs 异常断开) */ let manualClose = false /** * 当前连接的部门ID(用于断线重连) */ let currentDeptId = null /** * 当前消息回调函数 */ let currentOnMessage = null /** * WebSocket路径 */ const WS_PATH = '/ws' /** * 重连间隔(毫秒) */ const RECONNECT_INTERVAL = 5000 /** * 获取 WebSocket 基础地址 * 适配: * 1. VITE_APP_BASE_API 是完整URL(http://xxx) * 2. VITE_APP_BASE_API 是相对路径(/dev-api) */ function getWsBaseUrl() { const baseApi = import.meta.env.VITE_APP_BASE_API || '' // 如果是完整URL(http/https) if (/^https?:\/\//.test(baseApi)) { // 转换为 ws/wss return baseApi.replace(/^http/, 'ws').replace(/\/$/, '') } // 根据当前页面协议自动判断 ws / wss const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' return `${protocol}//${window.location.host}${baseApi}`.replace(/\/$/, '') } /** * 构建 WebSocket 连接地址 * @param deptId 部门ID(用于数据隔离) */ function buildWsUrl(deptId) { const url = new URL(`${getWsBaseUrl()}${WS_PATH}`) const token = getToken() // 当前后端仍依赖 deptId 做数据隔离 url.searchParams.set('deptId', deptId) // token 用于后续鉴权升级(预留) if (token) { url.searchParams.set('token', token) } return url.toString() } /** * 建立 WebSocket 连接 * @param deptId 当前用户部门ID * @param onMessage 消息回调函数 */ export function connectWs(deptId, onMessage) { // 必须传 deptId(当前后端依赖) if (!deptId) { console.warn('[WebSocket] 缺少 deptId') return } currentDeptId = deptId currentOnMessage = onMessage manualClose = false // 清除旧的重连任务 clearReconnectTimer() // 如果已经连接或正在连接,直接返回(防止重复连接) if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { return } // 创建 WebSocket 连接 ws = new WebSocket(buildWsUrl(deptId)) /** * 连接成功 */ ws.onopen = () => { console.log('[WebSocket] 连接成功 deptId=', deptId) } /** * 收到消息 */ ws.onmessage = (event) => { let data = event.data // 尝试解析 JSON try { data = JSON.parse(event.data) } catch (e) { // 非JSON消息保持原样 } // 回调给业务层 currentOnMessage && currentOnMessage(data, event) } /** * 连接关闭 */ ws.onclose = () => { console.log('[WebSocket] 连接关闭 deptId=', currentDeptId) ws = null // 非人为关闭 → 自动重连 if (!manualClose) { reconnectTimer = setTimeout(() => { console.log('[WebSocket] 开始重连 deptId=', currentDeptId) connectWs(currentDeptId, currentOnMessage) }, RECONNECT_INTERVAL) } } /** * 连接异常 */ ws.onerror = (error) => { console.error('[WebSocket] 连接异常', error) } } /** * 主动关闭 WebSocket */ export function closeWs() { manualClose = true // 清除重连任务 clearReconnectTimer() if (ws) { ws.close() ws = null } } /** * 发送消息 * @param data 支持字符串或对象 * @returns 是否发送成功 */ export function sendWs(data) { // 连接未就绪直接返回 if (!ws || ws.readyState !== WebSocket.OPEN) { console.warn('[WebSocket] 发送失败,连接未建立') return false } // 自动转JSON ws.send(typeof data === 'string' ? data : JSON.stringify(data)) return true } /** * 获取当前连接状态 */ export function getWsState() { return ws ? ws.readyState : WebSocket.CLOSED } /** * 清除重连定时器 */ function clearReconnectTimer() { if (reconnectTimer) { clearTimeout(reconnectTimer) reconnectTimer = null } }