liming пре 3 дана
родитељ
комит
45369f5d2c
3 измењених фајлова са 309 додато и 220 уклоњено
  1. 2 2
      .env
  2. 304 215
      src/stores/modules/webSocketStore.js
  3. 3 3
      src/updater/update.json

+ 2 - 2
.env

@@ -9,13 +9,13 @@ VITE_PRO_PATH='https://wallet.angeltokens.io'
 
 
 VITE_IM_PATH_FIlE ='https://nim.angeltokens.io/api/v1/file/'
-VITE_PRO_IM_WSS ='wss://nim.angeltokens.io/api/v1/socket.io'
+# VITE_PRO_IM_WSS ='wss://nim.angeltokens.io/api/v1/socket.io'
 VITE_PRO_IM_PATH='https://nim.angeltokens.io' 
 
 VITE_DEV_IM_PATH='https://nim.angeltokens.io'
 
 # VITE_IM_PATH_FIlE ='http://192.168.0.59:8888/api/v1/file/'
-# VITE_PRO_IM_WSS ='ws://192.168.0.59:8888/api/v1/socket.io'
+VITE_PRO_IM_WSS ='ws://192.168.0.59:8888/api/v1/socket.io'
 # VITE_DEV_IM_PATH='http://192.168.0.59:8888'
 
 

+ 304 - 215
src/stores/modules/webSocketStore.js

@@ -1,136 +1,199 @@
-// src/stores/websocket.ts
+// src/stores/websocket.js
 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;
+// 配置常量
+const WS_CONFIG = {
+  HEARTBEAT_INTERVAL: 30000, // 30秒
+  HEARTBEAT_TIMEOUT: 10000, // 10秒
+  HEARTBEAT_RETRY_LIMIT: 3,
+  RECONNECT_BASE_INTERVAL: 1000,
+  RECONNECT_MAX_INTERVAL: 30000,
+  RECONNECT_MAX_ATTEMPTS: Infinity,
+  RECONNECT_MULTIPLIER: 1.5,
+  MESSAGE_QUEUE_MAX_SIZE: 100,
+  MESSAGE_QUEUE_FLUSH_INTERVAL: 5000,
+};
 
 export const useWebSocketStore = defineStore("webSocketStore", {
-  // 状态定义
   state: () => ({
-    socket: null, // socket实例
-    peer: null, // peer实例
-    lockConnection: false, // 锁定连接
-    reconnectAttempts: 0, // 重连次数 
-    messages: [], // 消息列表
+    socket: null,
+    peer: null,
+    connectionState: "disconnected", // 'connecting', 'connected', 'disconnecting', 'error'
+    reconnectAttempts: 0,
+    messages: [],
     toUserInfo: {},
-    unreadMessages: [], // 未读消息列表
+    unreadMessages: [],
     uuid: null,
     onMessageCallbacks: [],
-
-    // 心跳检测配置
-    heartCheck: {
-      interval: 30000, // 心跳发送间隔(30秒)
-      timeout: 10000, // 响应超时时间(10秒)
-      timeoutObj: null,
-      serverTimeoutObj: null,
-      retryLimit: 3, // 重试次数
-      retryCount: 0, // 当前重试计数
+    messageQueue: [],
+    lastActivityTime: 0,
+    stats: {
+      sentMessages: 0,
+      receivedMessages: 0,
+      failedMessages: 0,
+      reconnects: 0,
+    },
+    heartbeat: {
+      timer: null,
+      timeout: null,
+      retryCount: 0,
     },
+    reconnect: {
+      timer: null,
+      currentAttempt: 0,
+    },
+    queueFlushTimer: null,
   }),
+
   persist: {
     key: "toUserInfo",
     storage: localStorage,
-    paths: ["toUserInfo"], // 正确的简单路径格式
+    paths: ["toUserInfo", "uuid"],
     serializer: {
-      serialize: (state) => JSON.stringify(state.toUserInfo),
-      deserialize: (str) => ({ toUserInfo: JSON.parse(str) }),
+      serialize: (state) =>
+        JSON.stringify({
+          toUserInfo: state.toUserInfo,
+          uuid: state.uuid,
+        }),
+      deserialize: (str) => {
+        const data = JSON.parse(str);
+        return {
+          toUserInfo: data.toUserInfo || {},
+          uuid: data.uuid || null,
+        };
+      },
     },
   },
-  // 计算属性
+
   getters: {
-    isConnected: (state) => state.socket?.readyState === WebSocket.OPEN,
-    hasUnreadMessages: (state) => state.unreadMessages.length > 0,
+    isConnected(state) {
+      return state.connectionState === "connected";
+    },
+    hasUnreadMessages(state) {
+      return state.unreadMessages.length > 0;
+    },
+    connectionStatus(state) {
+      return state.connectionState;
+    },
   },
 
-  // 方法
   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;
+    // 初始化连接
+    connect(userUuid, callback) {
+      if (!userUuid) {
+        console.error("连接失败: 缺少 userUuid");
+        return;
       }
 
-      // 获取url上uuid参数
-      const walletStore = useWalletStore();
-      let data = {
-        ...messageData,
-        fromUsername: walletStore.username,
-        from: walletStore.account,
-        to: this.toUserInfo.uuid,
-      };
-      console.log("发送消息=", data);
+      // 如果正在连接或已连接,则先断开
+      if (
+        this.connectionState === "connected" ||
+        this.connectionState === "connecting"
+      ) {
+        this.disconnect();
+      }
+
+      this.uuid = userUuid;
+      this.connectionState = "connecting";
+      this.stats.reconnects++;
+
       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;
+        const wsUrl = `${import.meta.env.VITE_PRO_IM_WSS}?user=${userUuid}`;
+        this.socket = new WebSocket(wsUrl);
+
+        this.socket.onopen = () => {
+          this.connectionState = "connected";
+          this.reconnect.currentAttempt = 0;
+          this.lastActivityTime = Date.now();
+          this.startHeartbeat();
+          this.startQueueFlush();
+
+          if (callback && typeof callback === "function") {
+            callback();
+          }
+
+          console.log("WebSocket 连接成功");
+          this.flushMessageQueue(); // 连接成功后立即发送队列中的消息
+        };
+
+        this.socket.onmessage = (event) => {
+          this.lastActivityTime = Date.now();
+          this.handleMessage(event.data);
+        };
+
+        this.socket.onclose = () => {
+          this.connectionState = "disconnected";
+          this.cleanupTimers();
+
+          if (
+            this.reconnect.currentAttempt < WS_CONFIG.RECONNECT_MAX_ATTEMPTS
+          ) {
+            this.scheduleReconnect();
+          }
+        };
+
+        this.socket.onerror = (error) => {
+          this.connectionState = "error";
+          console.error("WebSocket 错误:", error);
+          this.cleanupTimers();
+          this.scheduleReconnect();
+        };
       } catch (error) {
-        console.error("消息编码错误:", error);
-        return false;
+        console.error("创建 WebSocket 失败:", error);
+        this.connectionState = "error";
+        this.scheduleReconnect();
       }
     },
-    // 初始化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);
-    // },
 
+    // 断开连接
+    disconnect() {
+      this.connectionState = "disconnecting";
+      this.cleanupTimers();
+
+      if (this.socket) {
+        try {
+          this.socket.close();
+        } catch (e) {
+          console.error("关闭 WebSocket 时出错:", e);
+        }
+        this.socket = null;
+      }
+
+      if (this.peer) {
+        this.peer.close();
+        this.peer = null;
+      }
+
+      this.connectionState = "disconnected";
+    },
+
+    // 清理所有定时器
+    cleanupTimers() {
+      if (this.heartbeat.timer) clearTimeout(this.heartbeat.timer);
+      if (this.heartbeat.timeout) clearTimeout(this.heartbeat.timeout);
+      if (this.reconnect.timer) clearTimeout(this.reconnect.timer);
+      if (this.queueFlushTimer) clearInterval(this.queueFlushTimer);
+
+      this.heartbeat.timer = null;
+      this.heartbeat.timeout = null;
+      this.reconnect.timer = null;
+      this.queueFlushTimer = null;
+      this.heartbeat.retryCount = 0;
+    },
+
+    // 启动心跳检测
     startHeartbeat() {
-      this.resetHeartbeat();
+      this.cleanupHeartbeat();
 
-      this.heartCheck.timeoutObj = setTimeout(() => {
+      this.heartbeat.timer = setTimeout(() => {
         if (this.isConnected) {
           try {
             const pingMsg = {
@@ -139,155 +202,155 @@ export const useWebSocketStore = defineStore("webSocketStore", {
               timestamp: Date.now(),
             };
 
-            const MessageType = protobuf.lookupType("protocol.Message");
-            const messagePB = MessageType.create(pingMsg);
-            const buffer = MessageType.encode(messagePB).finish();
-            this.socket.send(buffer);
+            this.sendRawMessage(pingMsg);
 
             // 设置响应超时检测
-            this.heartCheck.serverTimeoutObj = setTimeout(() => {
-              this.heartCheck.retryCount++;
+            this.heartbeat.timeout = setTimeout(() => {
+              this.heartbeat.retryCount++;
 
-              if (this.heartCheck.retryCount >= this.heartCheck.retryLimit) {
+              if (
+                this.heartbeat.retryCount >= WS_CONFIG.HEARTBEAT_RETRY_LIMIT
+              ) {
                 console.error("心跳响应超时,关闭连接");
-                this.socket?.close();
+                this.disconnect();
               } else {
                 console.warn(
-                  `心跳无响应,第${this.heartCheck.retryCount}次重试`
+                  `心跳无响应,第${this.heartbeat.retryCount}次重试`
                 );
                 this.startHeartbeat(); // 重新发送心跳
               }
-            }, this.heartCheck.timeout);
+            }, WS_CONFIG.HEARTBEAT_TIMEOUT);
           } catch (error) {
             console.error("心跳发送失败:", error);
-            this.reconnect();
+            this.scheduleReconnect();
           }
         }
-      }, this.heartCheck.interval);
-    },
-
-    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.uuid = userUuid;
-      this.disconnect(); // 确保先断开现有连接
-
-      this.peer = new RTCPeerConnection();
-      this.socket = new WebSocket(
-        `${import.meta.env.VITE_PRO_IM_WSS}?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();
-      };
+      }, WS_CONFIG.HEARTBEAT_INTERVAL);
     },
 
-    // 处理消息
-    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);
+    // 清理心跳定时器
+    cleanupHeartbeat() {
+      if (this.heartbeat.timer) clearTimeout(this.heartbeat.timer);
+      if (this.heartbeat.timeout) clearTimeout(this.heartbeat.timeout);
+      this.heartbeat.timer = null;
+      this.heartbeat.timeout = null;
     },
-    // 断线重连
 
-    // 替换现有的reconnect方法
-    reconnect() {
-      if (this.lockConnection) return;
+    // 安排重连
+    scheduleReconnect() {
+      if (this.reconnect.timer) return;
 
-      this.lockConnection = true;
-      this.reconnectConfig.currentAttempt++;
+      this.reconnect.currentAttempt++;
 
       // 指数退避算法计算重连间隔
       const delay = Math.min(
-        this.reconnectConfig.baseInterval *
+        WS_CONFIG.RECONNECT_BASE_INTERVAL *
           Math.pow(
-            this.reconnectConfig.multiplier,
-            this.reconnectConfig.currentAttempt - 1
+            WS_CONFIG.RECONNECT_MULTIPLIER,
+            this.reconnect.currentAttempt - 1
           ),
-        this.reconnectConfig.maxInterval
+        WS_CONFIG.RECONNECT_MAX_INTERVAL
       );
 
       console.log(
-        `将在 ${delay}ms 后尝试第 ${this.reconnectConfig.currentAttempt} 次重连`
+        `将在 ${delay}ms 后尝试第 ${this.reconnect.currentAttempt} 次重连`
       );
 
-      setTimeout(() => {
-        if (!this.isConnected) {
+      this.reconnect.timer = setTimeout(() => {
+        this.reconnect.timer = null;
+        if (!this.isConnected && this.uuid) {
           this.connect(this.uuid);
         }
-        this.lockConnection = false;
       }, delay);
     },
-    // 重连
-    disconnect() {
-      if (this.socket) {
-        this.resetHeartbeat();
-        this.socket.close();
-        this.socket = null;
+
+    // 发送原始消息(不经过队列)
+    sendRawMessage(messageData) {
+      if (!this.isConnected) {
+        throw new Error("WebSocket 未连接");
       }
-      if (this.peer) {
-        this.peer.close();
-        this.peer = null;
+
+      try {
+        const MessageType = protobuf.lookupType("protocol.Message");
+        const messagePB = MessageType.create(messageData);
+        const buffer = MessageType.encode(messagePB).finish();
+        this.socket.send(buffer);
+        this.stats.sentMessages++;
+        return true;
+      } catch (error) {
+        console.error("消息编码错误:", error);
+        this.stats.failedMessages++;
+        return false;
+      }
+    },
+
+    // 发送消息(自动排队)
+    sendMessage(messageData) {
+      const walletStore = useWalletStore();
+
+      // 构造完整消息
+      const fullMessage = {
+        ...messageData,
+        fromUsername: walletStore.username,
+        from: walletStore.account,
+        to: this.toUserInfo.uuid,
+        avatar: walletStore.avatar,
+        timestamp: Date.now(),
+      };
+
+      // 如果连接正常,直接发送
+      if (this.isConnected) {
+        const success = this.sendRawMessage(fullMessage);
+        if (success) {
+          setMessageHook(fullMessage, this);
+          return true;
+        }
       }
-      this.heartCheck.retryCount = 0;
+
+      // 如果发送失败或未连接,加入队列
+      if (this.messageQueue.length < WS_CONFIG.MESSAGE_QUEUE_MAX_SIZE) {
+        this.messageQueue.push(fullMessage);
+        console.log("消息已加入队列,等待发送");
+        return true;
+      } else {
+        console.error("消息队列已满,丢弃消息");
+        return false;
+      }
+    },
+
+    // 启动队列刷新定时器
+    startQueueFlush() {
+      if (this.queueFlushTimer) return;
+
+      this.queueFlushTimer = setInterval(() => {
+        this.flushMessageQueue();
+      }, WS_CONFIG.MESSAGE_QUEUE_FLUSH_INTERVAL);
     },
 
-    // 发送WebRTC消息
-    dealWebRtcMessage(message) {
-      // 实现WebRTC消息处理逻辑
+    // 发送队列中的所有消息
+    flushMessageQueue() {
+      if (!this.isConnected || this.messageQueue.length === 0) return;
+
+      while (this.messageQueue.length > 0) {
+        const message = this.messageQueue.shift();
+        try {
+          const success = this.sendRawMessage(message);
+          if (success) {
+            setMessageHook(message, this);
+          } else {
+            // 发送失败,重新放回队列开头
+            this.messageQueue.unshift(message);
+            break;
+          }
+        } catch (error) {
+          console.error("发送队列消息失败:", error);
+          this.messageQueue.unshift(message);
+          break;
+        }
+      }
     },
+
+    // 处理接收到的消息
     handleMessage(data) {
       const MessageType = protobuf.lookupType("protocol.Message");
       const reader = new FileReader();
@@ -295,7 +358,7 @@ export const useWebSocketStore = defineStore("webSocketStore", {
       reader.onload = (event) => {
         try {
           const messagePB = MessageType.decode(
-            new Uint8Array(event.target?.result)
+            new Uint8Array(event.target.result)
           );
           const message = MessageType.toObject(messagePB, {
             longs: String,
@@ -303,6 +366,9 @@ export const useWebSocketStore = defineStore("webSocketStore", {
             bytes: String,
           });
 
+          this.stats.receivedMessages++;
+          this.lastActivityTime = Date.now();
+
           console.log("收到消息:", message);
           handleMessageHook(message, this);
 
@@ -315,31 +381,54 @@ export const useWebSocketStore = defineStore("webSocketStore", {
             }
           });
 
-          if (message.type === "heatbeat") return;
-
-          if (message.type === Constant.MESSAGE_TRANS_TYPE) {
-            this.dealWebRtcMessage(message);
+          if (message.type === "heatbeat") {
+            this.heartbeat.retryCount = 0; // 重置心跳重试计数
             return;
           }
-          // 其他消息处理逻辑...
         } catch (error) {
           console.error("消息解码错误:", error);
+          this.stats.failedMessages++;
         }
       };
 
       reader.readAsArrayBuffer(data);
     },
 
+    // 添加消息回调
     addOnMessageCallback(cb) {
-      if (typeof cb === "function") {
+      if (typeof cb === "function" && !this.onMessageCallbacks.includes(cb)) {
         this.onMessageCallbacks.push(cb);
       }
     },
 
+    // 移除消息回调
     removeOnMessageCallback(cb) {
       this.onMessageCallbacks = this.onMessageCallbacks.filter(
         (item) => item !== cb
       );
     },
+
+    // 获取历史消息
+    async getMessages(params) {
+      try {
+        const { data } = await getMessageApi({
+          messageType: params.messageType,
+          uuid: params.uuid,
+          friendUsername: params.friendUsername,
+        });
+
+        this.messages = (data || []).map((item) => {
+          item.avatar = item.avatar
+            ? `${import.meta.env.VITE_IM_PATH_FIlE}${item.avatar}`
+            : item.avatar;
+          return item;
+        });
+
+        return this.messages;
+      } catch (error) {
+        console.error("获取消息失败:", error);
+        throw error;
+      }
+    },
   },
 });

+ 3 - 3
src/updater/update.json

@@ -1,7 +1,7 @@
 {
-  "version": "1.0.57",
-  "releaseDate": "2025-08-12 02:33:58",
-  "checksum": "867cb1666dc532d80bb550ce287d4c10058c0cf8d8db6f1a9968eff0046c8feb",
+  "version": "1.0.58",
+  "releaseDate": "2025-08-12 02:43:05",
+  "checksum": "48e526eac4245521c54e9a6d9bfbec66f643528cd91c934bf44fbf07024a8dd3",
   "minBinaryVersion": "1.0.0",
   "mandatory": true,
   "upDataDescription": "✨修正一些錯誤。。。。!!!"