import { CommonUtils } 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.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) { // #ifdef APP-PLUS || APP let content = Content; let options = { title: "重要通知", cover: true, // 是否覆盖上一次的通知 when: new Date() // 通知显示时间 }; // TODO 跳转到指定页 let payload = JSON.stringify({}); plus.push.createMessage(content, payload, options); // #endif } } export default new WebSocketServices()