import { defineStore } from 'pinia' export const useWebSocketStore = defineStore('websocket', () => { let socketTask = null; let reconnectTimer = null; let heartbeatTimer = null; let reconnectAttempts = 0; const MAX_RECONNECT_ATTEMPTS = 5; const HEARTBEAT_INTERVAL = 30000; // 30秒心跳 // 连接状态 const connectionState = ref('disconnected'); // disconnected, connecting, connected, reconnecting /** * 建立WebSocket连接 * @param {Object} registerInfo - 注册信息 * @param {string} registerInfo.device_id - 设备ID * @param {string} registerInfo.token - JWT Token */ const buildWebSocket = async (registerInfo) => { try { // 确保先关闭已有连接 if (socketTask) { socketTask.close({ code: 1000, reason: '重新连接' }); socketTask = null; } // 清除重连定时器 if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } connectionState.value = 'connecting'; // WebSocket地址 const wsUrl = `wss://api.faxianwo.me/api/v1/ws?device_id=${registerInfo.device_id}`; console.log('开始连接WebSocket:', wsUrl); // uniapp WebSocket连接配置 socketTask = uni.connectSocket({ url: wsUrl, header: { 'Authorization': `Bearer ${registerInfo.token}`, 'Content-Type': 'application/json' }, // 移除protocols参数 success: () => { console.log('WebSocket连接请求发送成功'); }, fail: (error) => { console.error('WebSocket连接请求失败:', error); connectionState.value = 'disconnected'; handleConnectionError(error); } }); // 连接成功事件 socketTask.onOpen((response) => { console.log('WebSocket连接成功:', response); connectionState.value = 'connected'; reconnectAttempts = 0; // 重置重连次数 // 发送认证确认消息 sendAuthConfirmation(); // 开始心跳 startHeartbeat(); // 显示连接成功提示 uni.showToast({ title: '连接成功', icon: 'success', duration: 2000 }); }); // 接收消息事件 socketTask.onMessage((message) => { try { const data = JSON.parse(message.data); console.log('收到WebSocket消息:', data); handleIncomingMessage(data); } catch (error) { console.error('解析WebSocket消息失败:', error, message.data); } }); // 连接错误事件 socketTask.onError((error) => { console.error('WebSocket连接错误:', error); connectionState.value = 'disconnected'; stopHeartbeat(); handleConnectionError(error); }); // 连接关闭事件 socketTask.onClose((closeEvent) => { console.log('WebSocket连接关闭:', closeEvent); connectionState.value = 'disconnected'; stopHeartbeat(); // 如果不是主动关闭,尝试重连 if (closeEvent.code !== 1000 && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { attemptReconnect(registerInfo); } }); return socketTask; } catch (error) { console.error('建立WebSocket连接异常:', error); connectionState.value = 'disconnected'; return null; } }; /** * 发送认证确认消息 */ const sendAuthConfirmation = () => { if (!socketTask || connectionState.value !== 'connected') { console.warn('WebSocket未连接,无法发送认证确认'); return; } const authMessage = { type: 'auth_confirm', id: `auth_${Date.now()}`, data: { timestamp: Date.now() } }; socketTask.send({ data: JSON.stringify(authMessage), success: () => console.log('认证确认发送成功'), fail: (err) => console.error('认证确认发送失败:', err) }); }; /** * 开始心跳检测 */ const startHeartbeat = () => { stopHeartbeat(); // 先清除已有的心跳 heartbeatTimer = setInterval(() => { if (socketTask && connectionState.value === 'connected') { const heartbeatMessage = { type: 'heartbeat', id: `hb_${Date.now()}`, data: { timestamp: Date.now() } }; socketTask.send({ data: JSON.stringify(heartbeatMessage), success: () => console.log('心跳发送成功'), fail: (err) => { console.error('心跳发送失败:', err); // 心跳失败可能表示连接有问题 connectionState.value = 'disconnected'; } }); } }, HEARTBEAT_INTERVAL); }; /** * 停止心跳检测 */ const stopHeartbeat = () => { if (heartbeatTimer) { clearInterval(heartbeatTimer); heartbeatTimer = null; } }; /** * 处理接收到的消息 */ const handleIncomingMessage = (message) => { switch (message.type) { case 'new_message': // 处理新消息 console.log('收到新消息:', message.data); break; case 'message_read': // 处理已读回执 console.log('消息已读:', message.data); break; case 'message_recalled': // 处理消息撤回 console.log('消息撤回:', message.data); break; case 'heartbeat_response': // 心跳响应 console.log('收到心跳响应'); break; default: console.log('收到其他类型消息:', message); } }; /** * 发送消息的通用方法 */ const sendMessage = (messageType, messageData) => { if (!socketTask || connectionState.value !== 'connected') { console.warn('WebSocket未连接,无法发送消息'); return false; } const message = { type: messageType, id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, data: messageData }; socketTask.send({ data: JSON.stringify(message), success: () => console.log(` ${messageType} 发送成功`), fail: (err) => console.error(` ${messageType} 发送失败:`, err) }); return true; }; /** * 发送聊天消息 */ const sendChatMessage = (receiverId, content, msgType = 0, chatType = 0) => { return sendMessage('send_message', { receiverId: String(receiverId), // 确保是字符串格式 chatType, msgType, content, timestamp: Date.now() }); }; /** * 处理连接错误 */ const handleConnectionError = (error) => { let errorMessage = '连接错误'; // 完善的错误码处理 switch (error.errCode) { case 1000: errorMessage = '正常关闭'; break; case 1001: errorMessage = '连接断开'; break; case 1002: errorMessage = '协议错误'; break; case 1003: errorMessage = '数据格式错误'; break; case 1006: errorMessage = '异常断开'; break; case 1011: errorMessage = '服务器错误'; break; case 1012: errorMessage = '服务重启'; break; default: console.log(`未知错误代码: ${error.errCode}`); errorMessage = `连接错误 (${error.errCode})`; } console.error(` ${errorMessage}:`, error); // 只有在非正常关闭时才显示错误提示 if (error.errCode !== 1000) { uni.showToast({ title: errorMessage, icon: 'none', duration: 3000 }); } }; /** * 重连机制 */ const attemptReconnect = (registerInfo) => { if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { console.error('已达到最大重连次数,停止重连'); uni.showToast({ title: '连接失败,请检查网络', icon: 'none', duration: 3000 }); return; } reconnectAttempts++; connectionState.value = 'reconnecting'; // 指数退避策略 const delay = Math.min(2000 * Math.pow(1.5, reconnectAttempts), 30000); console.log(` 将在 ${delay}ms 后尝试重连 (第${reconnectAttempts}次)`); reconnectTimer = setTimeout(async () => { console.log(`开始重连 (第${reconnectAttempts}次)`); try { const task = await buildWebSocket(registerInfo); if (task) { console.log('重连成功'); } } catch (error) { console.error(`重连失败 (第${reconnectAttempts}次):`, error); // 继续尝试重连 if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { attemptReconnect(registerInfo); } } }, delay); }; /** * 关闭WebSocket连接 */ const closeWebSocket = () => { console.log('主动关闭WebSocket连接'); // 停止心跳 stopHeartbeat(); // 清除重连定时器 if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } // 重置重连计数器 reconnectAttempts = 0; // 关闭连接 if (socketTask) { socketTask.close({ code: 1000, // 正常关闭 reason: '用户主动关闭', success: () => { console.log('WebSocket已关闭'); connectionState.value = 'disconnected'; }, fail: (err) => console.error('关闭WebSocket失败:', err) }); socketTask = null; } }; /** * 获取连接状态 */ const getConnectionState = () => connectionState.value; /** * 检查是否已连接 */ const isConnected = () => connectionState.value === 'connected'; return { // 主要方法 buildWebSocket, closeWebSocket, sendMessage, sendChatMessage, // 状态查询 getConnectionState, isConnected, // 响应式状态(用于UI绑定) connectionState: readonly(connectionState) }; });