wtt
19 小时以前 30190a149dfcf465104d08c54ec4e2d4d8074b17
utils/WebSocketServices.js
@@ -5,82 +5,216 @@
class WebSocketServices {
    constructor() {
        this.wsInstance = null; // WebSocket 实例
        this.isConnecting = false; // 避免并发重连
        this.isConnected = false; // 避免并发重连
        this.wsUrl = CommonUtils.httpFormatWs()
        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连接
    createConnect(userId, userName) { // 使用用户标识 作为后端连接的凭据
        console.log('wsUrl: ', this.wsUrl);
        console.log('userId: ', userId);
        console.log('userName: ', userName);
        if (this.isConnecting || !userId || this.isConnected) {
            return
    /**
     * 建立 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)}`,
            success() {
                this.isConnecting = true
            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);
            }
        })
        });
        // 监听套接字连接建立
        uni.onSocketOpen((res) => {
            console.log('[webSocket]: 套接字连接建立成功');
            this.isConnecting = false
            this.isConnected = true
            console.log('res: ', res);
            this.wsInstance = res.socketTask
        })
        // 监听连接关闭:仅异常关闭触发重连
        this.closeListener = uni.onSocketClose((res) => {
            console.log("[WebSocket] 连接关闭", res);
            this.isConnecting = false;
            this.isConnected = false;
            this.clearNoMessageTimer();
        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"
                })
            // 正常关闭(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);
            }
        });
    }
    // 重连
    reConnect(reCount = 1, limit = 3) {
        if (reCount > limit) {
            uni.showToast({
                icon: 'none',
                title: `超出最大重连次数。已退出连接`
            })
            this.isConnecting = false
            return
    /**
     * 统一触发重连(延迟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;
        }
        uni.showToast({
            icon: 'none',
            title: `正在尝试重连,重连次数 ${reCount}`
        })
        reConnect(reCount + 1, limit)
        uni.hideToast()
    }
    // 连接注销
    disConnect() {
    /**
     * 清除所有 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) {
        console.log('Content: ', Content);
        // #ifdef APP-PLUS || APP
        console.log('Content2: ', Content);
        let content = Content;
        let options = {
            title: "重要通知",