zrg
昨天 5b37957eec9e5f301f7594b0f0f2d208ddea0cdc
utils/WebSocketServices.js
@@ -3,210 +3,214 @@
} from "@/utils/common.js";
class WebSocketServices {
   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;
     }
    constructor() {
        this.wsInstance = null; // WebSocket 实例
        this.isConnecting = false; // 连接中状态(避免并发重连)
        this.isConnected = false; // 已连接状态
        this.isReconnectStopped = false; // 停止重连标记
        this.wsUrl = CommonUtils.httpFormatWs(); // WebSocket 基础地址
        // 超时校验配置
        this.businessMessageInterval = 5 * 60 * 1000;
        this.noMessageTimeout = this.businessMessageInterval + 2 * 60 * 1000;
        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) {
        // 登录建立连接时,需要重新获取http地址,可能会变更
        this.wsUrl = CommonUtils.httpFormatWs();
        // 缓存用户信息(用于重连)
        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);
    }
    /**
     * 启动“无业务消息”超时校验
     */
    startNoMessageCheck() {
        this.clearNoMessageTimer();
        this.noMessageTimer = setTimeout(() => {
            console.warn("[WebSocket] 7分钟未收到业务消息,判定连接失效,主动重连");
            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;
    }
    showTaskTip(Content) {