chenhaozhe
6 天以前 9e825bb9263ed29897bfaf467bfd0f86da8d3e5f
utils/WebSocketServices.js
@@ -3,84 +3,214 @@
} from "@/utils/common.js";
class WebSocketServices {
    constructor() {
        this.wsInstance = null; // WebSocket 实例
        this.isConnecting = false; // 避免并发重连
        this.isConnected = false; // 避免并发重连
        this.wsUrl = CommonUtils.httpFormatWs()
    }
   constructor() {
       this.wsInstance = null; // WebSocket 实例
       this.isConnecting = false; // 连接中状态(避免并发重连)
       this.isConnected = false; // 已连接状态
       this.isReconnectStopped = false; // 停止重连标记
       this.wsUrl = CommonUtils.httpFormatWs(); // WebSocket 基础地址
       // 超时校验配置
       this.noMessageTimeout = 45000; // 45秒内没收到任何业务消息 → 判定连接失效(服务端30秒发一次Ping,留15秒容错)
       this.noMessageTimer = null; // 无消息超时定时器
       // 监听函数引用
       this.openListener = null;
       this.messageListener = null;
       this.closeListener = null;
       this.errorListener = null;
       // 缓存用户信息
       this.currentUserId = "";
       this.currentUserName = "";
     }
     /**
      * 建立 WebSocket 连接
      * @param {string/number} userId - 用户ID(必填)
      * @param {string} userName - 用户名(必填)
      * @param {number} count - 当前重连次数
      * @param {number} limit - 最大重连次数(默认3次)
      */
     createConnect(userId, userName, count = 0, limit = 3) {
       // 缓存用户信息(用于重连)
       this.currentUserId = userId;
       this.currentUserName = userName;
       // 前置校验:避免无效连接和并发重连
       if (this.isConnecting || this.isConnected) return;
       if (!userId && userId !== 0) { // 兼容 userId 为 0 的合法场景
         CommonUtils.showTips({ message: "用户标识不能为空,无法建立WebSocket连接" });
         return;
       }
       if (count > limit) {
         CommonUtils.showTips({
           message: `WebSocket 重连次数超出最大限制(${limit}次),已停止重连`,
         });
         this.isReconnectStopped = true;
         return;
       }
       console.log(`[WebSocket] 开始建立连接(第 ${count || 0} 次)`, {
         wsUrl: this.wsUrl,
         userId,
         userName,
       });
       // 清除历史残留:监听+定时器
       this.clearAllListeners();
       this.clearNoMessageTimer();
       // 发起连接
       this.isConnecting = true;
       this.wsInstance = uni.connectSocket({
         url: `${this.wsUrl}?userId=${encodeURIComponent(userId)}&userName=${encodeURIComponent(userName)}`,
         fail: (error) => {
           console.error("[WebSocket] 连接发起失败", error);
           this.isConnecting = false;
           this.triggerReconnect(count);
         },
       });
       // 监听连接成功:启动无消息超时校验
       this.openListener = uni.onSocketOpen((res) => {
         console.log("[WebSocket] 连接建立成功", res);
         this.isConnecting = false;
         this.isConnected = true;
         this.isReconnectStopped = false;
         this.startNoMessageCheck();
         count = 0;
       });
       this.messageListener = uni.onSocketMessage((res) => {
         try {
           const message = JSON.parse(res.data);
           console.log("[WebSocket] 收到业务消息", message);
           // 重置无消息定时器(有业务消息=连接正常)
           this.resetNoMessageTimer();
           // 处理业务消息
           if (message.Type === "Message") {
             const content = JSON.parse(message.Content);
             this.showTaskTip(`您有${content.length}条消息需要处理!`);
             // this.emit("message", content); // 支持外部监听
           }
         } catch (error) {
           console.error("[WebSocket] 消息解析失败", error, res.data);
         }
       });
       // 监听连接关闭:仅异常关闭触发重连
       this.closeListener = uni.onSocketClose((res) => {
         console.log("[WebSocket] 连接关闭", res);
         this.isConnecting = false;
         this.isConnected = false;
         this.clearNoMessageTimer();
         // 正常关闭(code=1000)或主动停止重连时,不重连
         if (!this.isReconnectStopped && res.code !== 1000) {
           this.triggerReconnect(count);
         }
       });
       // 监听连接错误:触发重连
       this.errorListener = uni.onSocketError((error) => {
         console.error("[WebSocket] 连接错误", error);
         this.isConnecting = false;
         this.clearNoMessageTimer();
         if (!this.isReconnectStopped) {
           this.triggerReconnect(count);
         }
       });
     }
     /**
      * 统一触发重连(延迟3秒)
      * @param {number} count - 当前重连次数
      */
     triggerReconnect(count) {
       console.log(`[WebSocket] 准备第 ${count + 1} 次重连`);
       setTimeout(() => {
         this.createConnect(this.currentUserId, this.currentUserName, count + 1);
       }, 3000);
     }
     /**
      * 启动“无业务消息”超时校验
      * 逻辑:45秒内没收到任何业务消息 → 判定连接失效(服务端30秒发Ping,底层已处理,此处仅校验业务通道)
      */
     startNoMessageCheck() {
       this.clearNoMessageTimer();
       this.noMessageTimer = setTimeout(() => {
         console.warn("[WebSocket] 45秒未收到业务消息,判定连接失效,主动重连");
         this.closeSocket();
         this.createConnect(this.currentUserId, this.currentUserName);
       }, this.noMessageTimeout);
     }
     /**
      * 收到业务消息后,重置无消息定时器
      */
     resetNoMessageTimer() {
       this.startNoMessageCheck();
     }
     /**
      * 清除无消息定时器
      */
     clearNoMessageTimer() {
       if (this.noMessageTimer) {
         clearTimeout(this.noMessageTimer);
         this.noMessageTimer = null;
       }
     }
     /**
      * 清除所有 Socket 监听
      */
     clearAllListeners() {
       if (this.openListener) {
         uni.offSocketOpen(this.openListener);
         this.openListener = null;
       }
       if (this.messageListener) {
         uni.offSocketMessage(this.messageListener);
         this.messageListener = null;
       }
       if (this.closeListener) {
         uni.offSocketClose(this.closeListener);
         this.closeListener = null;
       }
       if (this.errorListener) {
         uni.offSocketError(this.errorListener);
         this.errorListener = null;
       }
     }
     /**
      * 主动关闭 WebSocket 连接 (登出时关闭WebSocket连接)
      */
     closeSocket() {
       this.isReconnectStopped = true;
       this.clearAllListeners();
       this.clearNoMessageTimer();
       if (this.wsInstance) {
         uni.closeSocket({
           success: () => console.log("[WebSocket] 主动关闭连接成功"),
           fail: (error) => console.error("[WebSocket] 主动关闭连接失败", error),
         });
         this.wsInstance = null;
       }
       this.isConnected = false;
       this.isConnecting = false;
     }
    // 建立WebSocket连接
    createConnect(userId, userName) { // 使用用户标识 作为后端连接的凭据
        console.log('wsUrl: ', this.wsUrl);
        console.log('userId: ', userId);
        console.log('userName: ', userName);
        if (this.isConnecting || !userId || this.isConnected) {
            return
        }
        this.wsInstance = uni.connectSocket({
            url: this.wsUrl + `?userId=${encodeURIComponent(userId)}&userName=${encodeURIComponent(userName)}`,
            success() {
                this.isConnecting = true
            }
        })
        // 监听套接字连接建立
        uni.onSocketOpen((res) => {
            console.log('[webSocket]: 套接字连接建立成功');
            this.isConnecting = false
            this.isConnected = true
            console.log('res: ', res);
            this.wsInstance = res.socketTask
        })
        uni.onSocketMessage((res) => {
            let message = JSON.parse(res.data)
            console.log('message: ', message);
            if (message.Type == 'Message') {
                // 消息信号
                let content = JSON.parse(message.Content)
                console.log('content: ', content);
                this.showTaskTip(`您有${content.length}条消息需要处理!`)
            } else if (message.Type == 'ping') {
                // 心跳信号
                uni.sendSocketMessage({
                    data: "pong"
                })
            }
        })
    }
    // 重连
    reConnect(reCount = 1, limit = 3) {
        if (reCount > limit) {
            uni.showToast({
                icon: 'none',
                title: `超出最大重连次数。已退出连接`
            })
            this.isConnecting = false
            return
        }
        uni.showToast({
            icon: 'none',
            title: `正在尝试重连,重连次数 ${reCount}`
        })
        reConnect(reCount + 1, limit)
        uni.hideToast()
    }
    // 连接注销
    disConnect() {
    }
    showTaskTip(Content) {
        console.log('Content: ', Content);
        // #ifdef APP-PLUS || APP
        console.log('Content2: ', Content);
        let content = Content;
        let options = {
            title: "重要通知",