// src/stores/websocket.ts import { defineStore } from "pinia"; import { $root as protobuf } from "@/common/proto/proto"; import * as Constant from "@/common/constant/Constant"; import { useWalletStore } from "@/stores/modules/walletStore"; import { getMessageApi } from "@/api/path/im.api"; import { MSG_TYPE, MSG_TYPE_MAP } from "@/common/constant/msgType"; import { setMessageHook, handleMessageHook, } from "@/views/im/hook/messagesHook"; const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE; export const useWebSocketStore = defineStore("webSocketStore", { // 状态定义 state: () => ({ socket: null, // socket实例 peer: null, // peer实例 lockConnection: false, // 锁定连接 reconnectAttempts: 0, // 重连次数 maxReconnectAttempts: 5, // 最大重连次数 reconnectInterval: 3000, // 重连间隔 messages: [], // 消息列表 toUserInfo: {}, unreadMessages: [], // 未读消息列表 uuid: null, onMessageCallbacks: [], // 心跳检测配置 heartCheck: { timeout: 10000, // 连接丢失后,多长时间内没有收到服务端的消息,则认为连接已断开 timeoutObj: null, // 定时器对象 serverTimeoutObj: null, // 服务器定时器对象 num: 3, // 重连次数 }, }), persist: { key: "toUserInfo", storage: localStorage, paths: ["toUserInfo"], // 正确的简单路径格式 serializer: { serialize: (state) => JSON.stringify(state.toUserInfo), deserialize: (str) => ({ toUserInfo: JSON.parse(str) }), }, }, // 计算属性 getters: { isConnected: (state) => state.socket?.readyState === WebSocket.OPEN, hasUnreadMessages: (state) => state.unreadMessages.length > 0, }, // 方法 actions: { // 获取消息 async getMessages(params) { const { data } = await getMessageApi({ messageType: params.messageType, uuid: params.uuid, friendUsername: params.friendUsername, }); this.messages = data?.map((item) => { item.avatar = item.avatar ? IM_PATH + item.avatar : item.avatar; return item; }) || []; }, // 发送消息 sendMessage(messageData) { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { console.error("WebSocket未连接"); return false; } // 获取url上uuid参数 const walletStore = useWalletStore(); let data = { ...messageData, fromUsername: walletStore.username, from: walletStore.account, to: this.toUserInfo.uuid, }; console.log("发送消息=", data) try { const MessageType = protobuf.lookupType("protocol.Message"); const messagePB = MessageType.create(data); const buffer = MessageType.encode(messagePB).finish(); this.socket.send(buffer); // 发送完成后:添加头像 data.avatar = walletStore.avatar; setMessageHook(data, this); return true; } catch (error) { console.error("消息编码错误:", error); return false; } }, // 初始化socket startHeartbeat() { const self = this; const _num = this.heartCheck.num; this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj); this.heartCheck.serverTimeoutObj && clearTimeout(this.heartCheck.serverTimeoutObj); this.heartCheck.timeoutObj = setTimeout(() => { if (this.socket?.readyState === WebSocket.OPEN) { const data = { type: "heatbeat", content: "ping", }; const MessageType = protobuf.lookupType("protocol.Message"); const messagePB = MessageType.create(data); const buffer = MessageType.encode(messagePB).finish(); this.socket.send(buffer); } self.heartCheck.serverTimeoutObj = setTimeout(() => { _num--; if (_num <= 0) { console.log("the ping num is more then 3, close socket!"); this.socket?.close(); } }, self.heartCheck.timeout); }, this.heartCheck.timeout); }, resetHeartbeat() { this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj); this.heartCheck.serverTimeoutObj && clearTimeout(this.heartCheck.serverTimeoutObj); this.heartCheck.num = 3; }, // 链接ws connect(userUuid) { if (!userUuid) return; console.log("开始连接..."); this.disconnect(); // 确保先断开现有连接 this.peer = new RTCPeerConnection(); this.socket = new WebSocket( `wss://nim.angeltokens.io/api/v1/socket.io?user=${userUuid}` ); // 连接成功 this.socket.onopen = () => { this.startHeartbeat(); console.log("WebSocket连接成功"); }; this.socket.onmessage = (event) => { this.startHeartbeat(); this.handleMessage(event.data); }; this.socket.onclose = () => { console.log("连接关闭,尝试重新连接..."); this.resetHeartbeat(); this.reconnect(); }; this.socket.onerror = (error) => { console.error("WebSocket错误:", error); this.resetHeartbeat(); this.reconnect(); }; }, // 处理消息 handleMessage(data) { const MessageType = protobuf.lookupType("protocol.Message"); const reader = new FileReader(); reader.onload = (event) => { try { const messagePB = MessageType.decode( new Uint8Array(event.target?.result) ); const message = MessageType.toObject(messagePB, { longs: String, enums: String, bytes: String, }); console.log("收到消息:", message); // 处理消息 handleMessageHook(message, this); if (message.type === "heatbeat") return; if (message.type === Constant.MESSAGE_TRANS_TYPE) { this.dealWebRtcMessage(message); return; } // 其他消息处理逻辑... } catch (error) { console.error("消息解码错误:", error); } }; reader.readAsArrayBuffer(data); }, // 断线重连 reconnect() { if ( this.lockConnection || this.reconnectAttempts >= this.maxReconnectAttempts ) { return; } this.lockConnection = true; this.reconnectAttempts++; console.log( `重新连接中... (尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts})` ); setTimeout(() => { this.connect(localStorage.uuid); this.lockConnection = false; }, this.reconnectInterval); }, // 重连 disconnect() { if (this.socket) { this.resetHeartbeat(); this.socket.close(); this.socket = null; } if (this.peer) { this.peer.close(); this.peer = null; } this.reconnectAttempts = 0; }, // 发送WebRTC消息 dealWebRtcMessage(message) { // 实现WebRTC消息处理逻辑 }, handleMessage(data) { const MessageType = protobuf.lookupType("protocol.Message"); const reader = new FileReader(); reader.onload = (event) => { try { const messagePB = MessageType.decode(new Uint8Array(event.target?.result)); const message = MessageType.toObject(messagePB, { longs: String, enums: String, bytes: String, }); console.log("收到消息:", message); handleMessageHook(message, this); // 调用所有回调函数 this.onMessageCallbacks.forEach(cb => { try { cb(message); } catch (e) { console.error("消息回调错误:", e); } }); if (message.type === "heatbeat") return; if (message.type === Constant.MESSAGE_TRANS_TYPE) { this.dealWebRtcMessage(message); return; } // 其他消息处理逻辑... } catch (error) { console.error("消息解码错误:", error); } }; reader.readAsArrayBuffer(data); }, addOnMessageCallback(cb) { if (typeof cb === "function") { this.onMessageCallbacks.push(cb); } }, removeOnMessageCallback(cb) { this.onMessageCallbacks = this.onMessageCallbacks.filter(item => item !== cb); }, }, });