import { defineStore } from "pinia"; // #ifdef H5 const socketUrl = "http://192.168.1.42:2348"; // #endif // #ifndef H5 const socketUrl = "ws://192.168.1.42:2348"; // #endif // 聊天 export const useChatStore = defineStore("chat", { state: () => { return { socketUrl, isConnect: false, socketTask: null, onReceiveMsg: () => {}, chatList: [], shop_id: "", group_id:'', // ========== Socket 保活与重连状态 ========== isManualClose: false, // 是否主动关闭(true:主动关闭,不重连;false:意外断开,自动重连) heartbeatTimer: null, // 心跳定时器 reconnectTimer: null, // 重连定时器 connectionMonitorTimer: null, // 连接状态监控定时器 reconnectCount: 0, // 当前重连次数 maxReconnectCount: 8, // 最大重连次数 reconnectDelay: 1000, // 初始重连延迟(ms) maxReconnectDelay: 8000, // 最大重连延迟(ms) heartbeatInterval: 15000, // 心跳间隔15s // ========== 应用与网络状态监听 ========== isAppActive: true, // 应用是否在前台 lastHeartbeatTime: 0, // 上次心跳时间戳 _listenersInitialized: false, // 状态监听器是否已初始化 // ========== 消息去重机制 ========== recentMessages: new Map(), // 最近处理的消息映射表:key=消息特征哈希,value=处理时间戳 deduplicationWindow: 5000, // 去重时间窗口(5秒内相同消息只处理一次) maxRecentMessages: 100, // 最近消息缓存最大数量 // ========== 连接状态锁 ========== isConnecting: false, // 是否正在建立连接(防止重复创建连接) lastConnectTime: 0, // 上次连接时间(防止短时间内频繁连接) // ========== 消息回调队列 ========== receiveMsgCallbacks: [], // 消息接收回调队列,支持多页面同时监听 }; }, actions: { // ========== 初始化 ========== init() { if (!this.isConnect) { return uni.showToast({ title: "请先连接socket", icon: "none", }); } this.sendMessage( { type: "OnbocChat", operate_type: "init", shop_id: this.shop_id, token: uni.cache.get("token"), }, false ); }, // ========== 发送消息(核心优化) ========== sendMessage(msg, isAutoAppend = true) { // 1. 主动关闭状态:提示用户 if (this.isManualClose) { return uni.showToast({ title: "Socket已主动关闭,请重新连接", icon: "none", }); } // 2. 连接未建立或 SocketTask 已失效:立即重连并延迟发送 if (!this.isConnect || !this.socketTask) { console.warn("Socket连接未建立,尝试重连..."); // 立即重连 this.forceReconnect(); // 延迟发送,确保重连成功 setTimeout(() => { this.sendMessage(msg, isAutoAppend); }, 1500); // 延长延迟时间,确保重连完成 return; } // 3. 正常发送消息 console.log("发送的消息", msg); const message = isAutoAppend ? { type: "OnbocChat", operate_type: "sendMsg", shop_id: this.shop_id, token: uni.getStorageSync("iToken")?.tokenValue || uni.cache.get("token") || "", ...msg, } : msg; try { this.socketTask.send({ data: JSON.stringify(message), success: (res) => { console.log("发送成功", res); }, fail: (error) => { console.error("发送失败,触发重连", error); // 发送失败,立即重连 this.forceReconnect(); // 重连后重试发送 setTimeout(() => { this.sendMessage(msg, isAutoAppend); }, 1500); }, }); } catch (error) { console.error("发送消息异常,触发重连", error); // 捕获异常,立即重连 this.forceReconnect(); // 重连后重试发送 setTimeout(() => { this.sendMessage(msg, isAutoAppend); }, 1500); } }, // ========== 发送心跳包 ========== sendHeartbeat() { if (!this.isConnect || this.isManualClose || !this.socketTask) return; const now = Date.now(); this.lastHeartbeatTime = now; // 记录心跳时间 this.socketTask.send({ data: JSON.stringify({ type: "ping_interval", }), fail: (error) => { console.log("心跳发送失败,触发重连", error); this.forceReconnect(); }, }); }, // ========== 启动心跳定时器 ========== startHeartbeat() { // 清除旧定时器,避免重复 if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); } // 立即发送一次心跳,确保连接活跃 this.sendHeartbeat(); // 每15s发送一次心跳 this.heartbeatTimer = setInterval(() => { this.sendHeartbeat(); }, this.heartbeatInterval); }, // ========== 停止心跳定时器 ========== stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } }, // ========== 启动连接状态监控 ========== startConnectionMonitor() { // 清除旧定时器 if (this.connectionMonitorTimer) { clearInterval(this.connectionMonitorTimer); } // 每30秒检查一次连接状态 this.connectionMonitorTimer = setInterval(() => { // 如果应该连接但实际未连接,触发重连 if (!this.isManualClose && this.isConnect && !this.socketTask) { console.warn("连接监控:发现连接状态异常,强制重置"); this.forceReconnect(); } }, 30000); }, // ========== 停止连接状态监控 ========== stopConnectionMonitor() { if (this.connectionMonitorTimer) { clearInterval(this.connectionMonitorTimer); this.connectionMonitorTimer = null; } }, // ========== 智能重连逻辑 ========== reconnect() { // 1. 主动关闭或已连接:不重连 if (this.isManualClose || this.isConnect) return; // 2. 正在连接中:不重连 if (this.isConnecting) { console.log("正在建立连接中,跳过重连"); return; } // 3. 短时间内已尝试连接:不重连(防止频繁连接) const now = Date.now(); if (now - this.lastConnectTime < 1000) { console.log("短时间内已尝试连接,跳过重连"); return; } // 4. 超过最大重连次数:停止重连 if (this.reconnectCount >= this.maxReconnectCount) { console.log("已达最大重连次数,停止重连"); return; } // 5. 清除旧重连定时器 if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); } // 6. 计算重连延迟(退避算法) const baseDelay = this.isAppActive ? 500 : this.reconnectDelay; const delay = Math.min( baseDelay * Math.pow(2, this.reconnectCount), this.maxReconnectDelay ); console.log(`第${this.reconnectCount + 1}次重连,延迟${delay}ms`); // 7. 延迟执行重连 this.reconnectTimer = setTimeout(() => { this.connectSocket(); this.reconnectCount++; }, delay); }, // ========== 强制重连方法 ========== forceReconnect() { // 主动关闭状态下不重连 if (this.isManualClose) return; console.log("执行强制重连"); // 1. 清理现有连接资源,强制重置所有状态 if (this.socketTask) { try { this.socketTask.close({ code: 1000, // 正常关闭 reason: "force reconnect", }); } catch (error) { console.error("关闭Socket失败", error); } finally { // 强制重置所有状态 this.socketTask = null; this.isConnect = false; this.isConnecting = false; } } // 2. 重置重连状态 this.reconnectCount = 0; this.stopHeartbeat(); // 3. 立即重新连接 setTimeout(() => { this.connectSocket(); }, 100); }, // ========== 内部关闭Socket方法(不设置手动关闭标记) ========== closeSocketInternal() { // 1. 停止所有定时器 this.stopHeartbeat(); this.stopConnectionMonitor(); if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } // 2. 关闭Socket连接(Uniapp会自动清理监听器) if (this.socketTask) { try { this.socketTask.close({ code: 1000, // 正常关闭 reason: "internal close", }); } catch (error) { console.error("关闭Socket失败", error); } finally { // 3. 重置状态 this.socketTask = null; this.isConnect = false; this.isConnecting = false; } } }, // ========== 初始化状态监听器 ========== initStateListeners() { // 避免重复初始化 if (this._listenersInitialized) return; console.log("初始化状态监听器"); // 监听应用前后台切换 uni.onAppShow(() => { console.log("应用切前台,检查Socket连接"); this.isAppActive = true; // 应用切前台,强制重连 this.forceReconnect(); }); uni.onAppHide(() => { console.log("应用切后台"); this.isAppActive = false; }); // 监听网络状态变化 uni.onNetworkStatusChange((res) => { console.log("网络状态变化", res); if (res.isConnected) { // 网络恢复,自动重连 console.log("网络恢复,触发重连"); this.forceReconnect(); } }); this._listenersInitialized = true; }, // ========== 连接Socket ========== connectSocket() { // 1. 严格检查:避免重复连接 if (this.isConnect || this.isConnecting || this.isManualClose) { console.warn("跳过连接:当前状态不允许建立新连接", { isConnect: this.isConnect, isConnecting: this.isConnecting, isManualClose: this.isManualClose, }); // 关键:如果 socketTask 已失效但 isConnect 为 true,强制重置状态 if (this.isConnect && !this.socketTask) { console.warn("Socket连接状态异常,强制重置"); this.isConnect = false; this.forceReconnect(); } return; } // 2. 设置连接中状态 this.isConnecting = true; this.lastConnectTime = Date.now(); // 3. 重置主动关闭标记 this.isManualClose = false; console.log("开始创建新的Socket连接"); // 4. 创建Socket连接 this.socketTask = uni.connectSocket({ url: this.socketUrl, success: (res) => { console.log("Socket连接请求发送成功"); }, fail: (res) => { console.log("Socket连接请求失败", res); // 请求失败,重置状态 this.isConnecting = false; // 触发重连 this.reconnect(); }, }); // 5. 连接成功回调 this.socketTask.onOpen((res) => { console.log("Socket连接成功"); // 重置连接状态 this.isConnect = true; this.isConnecting = false; this.reconnectCount = 0; // 重置重连次数 this.init(); // 初始化聊天 this.startHeartbeat(); // 启动心跳 this.startConnectionMonitor(); // 启动连接状态监控 // 初始化状态监听器 this.initStateListeners(); }); // 6. 接收消息回调 - 支持多回调 this.socketTask.onMessage((res) => { try { const data = JSON.parse(res.data); console.log("收到服务器消息", data); // 处理发送消息(服务器转发的自己发送的消息) if (data?.operate_type === "sendMsg") { // 添加方向标识:send const sendMsg = { ...data.data, direction: "send", }; this.processMessage(sendMsg); } // 处理接收消息(他人发送的消息) if (data?.operate_type === "receive_msg") { // 添加方向标识:receive const receiveMsg = { ...data.data, coupon: data.data.coupon ? JSON.parse(data.data.coupon) : {}, operate_type: "receive_msg", direction: "receive", }; this.processMessage(receiveMsg); } // 处理心跳响应 if (data?.operate_type === "heartbeat_ack") { console.log("心跳响应正常"); } } catch (error) { console.error("消息解析失败", error); } }); // 7. 连接错误回调 this.socketTask.onError((res) => { console.log("Socket连接错误", res); // 重置连接状态 this.isConnect = false; this.isConnecting = false; // 错误触发重连 this.forceReconnect(); }); // 8. 连接关闭回调 this.socketTask.onClose(() => { console.log("Socket连接已关闭"); // 重置连接状态 this.isConnect = false; this.isConnecting = false; this.stopHeartbeat(); // 停止心跳 this.stopConnectionMonitor(); // 停止连接监控 // 只有非主动关闭时才重连 if (!this.isManualClose) { console.log("意外断开,触发重连"); this.reconnect(); } else { console.log("主动关闭,不重连"); } }); }, // ========== 消息处理与去重 ========== processMessage(msg) { // 1. 生成消息特征哈希 const msgHash = this.generateMessageHash(msg); const now = Date.now(); // 2. 清理过期消息 this.cleanupExpiredMessages(now); // 3. 去重逻辑:只对接收的消息应用严格去重 if (msg.direction === "receive") { // 接收消息:5秒内相同特征的消息只处理一次 const lastProcessTime = this.recentMessages.get(msgHash); if ( lastProcessTime && now - lastProcessTime < this.deduplicationWindow ) { console.log("重复接收消息,已忽略(特征哈希:", msgHash, ")"); return; // 重复消息,直接返回 } // 记录接收消息的处理时间 this.recentMessages.set(msgHash, now); } else { // 发送消息:直接通过,不做严格去重 console.log("发送消息,直接处理(特征哈希:", msgHash, ")"); } // 4. 限制最近消息数量,避免内存泄漏 if (this.recentMessages.size > this.maxRecentMessages) { // 移除最早的消息 const firstKey = this.recentMessages.keys().next().value; this.recentMessages.delete(firstKey); } // 5. 正常处理消息 if (this.group_id == msg.to_id) { this.chatList.unshift(msg); } // 6. 触发所有消息回调(支持多页面同时监听) this.triggerReceiveMsgCallbacks(msg); }, // ========== 触发消息回调 ========== triggerReceiveMsgCallbacks(msg) { // 调用旧的单个回调(兼容原有代码) if (typeof this.onReceiveMsg === "function") { this.onReceiveMsg(msg); } // 调用所有注册的回调 this.receiveMsgCallbacks.forEach((callback) => { if (typeof callback === "function") { callback(msg); } }); }, // ========== 注册消息回调 ========== registerReceiveMsgCallback(callback) { if ( typeof callback === "function" && !this.receiveMsgCallbacks.includes(callback) ) { this.receiveMsgCallbacks.push(callback); } }, // ========== 移除消息回调 ========== removeReceiveMsgCallback(callback) { const index = this.receiveMsgCallbacks.indexOf(callback); if (index > -1) { this.receiveMsgCallbacks.splice(index, 1); } }, // ========== 生成消息特征哈希 ========== generateMessageHash(msg) { // 基于消息的核心字段生成哈希 const { content, // 消息内容 msg_type, // 消息类型 timestamp, // 时间戳 operate_type, // 操作类型 from_user_id, // 发送者ID to_user_id, // 接收者ID image_url, // 图片URL coupon, // 优惠券信息 direction, // 消息方向 } = msg; // 组合核心字段,生成唯一字符串 // 发送消息添加随机数,确保每次发送的消息哈希唯一 const msgFeatures = JSON.stringify({ content: content || "", msg_type: msg_type || "", operate_type: operate_type || "", from_user_id: from_user_id || "", to_user_id: to_user_id || "", image_url: image_url || "", coupon_id: coupon?.id || "", direction: direction || "", // 发送消息添加随机数,确保每次发送的消息哈希唯一 random: direction === "send" ? Math.random().toString(36).substr(2, 9) : "", }); // 使用简单的哈希算法生成32位哈希值 return this.simpleHash(msgFeatures); }, // ========== 简单哈希算法 ========== simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; // 转换为32位整数 } return Math.abs(hash).toString(16); // 转换为16进制字符串 }, // ========== 清理过期消息 ========== cleanupExpiredMessages(currentTime) { for (const [hash, time] of this.recentMessages.entries()) { if (currentTime - time > this.deduplicationWindow) { this.recentMessages.delete(hash); } } }, // ========== 主动关闭Socket ========== closeSocket() { // 1. 设置主动关闭标记 this.isManualClose = true; // 2. 关闭Socket连接(内部方法会处理状态重置) this.closeSocketInternal(); console.log("Socket已主动关闭"); }, }, unistorage: false, // 开启后对 state 的数据读写都将持久化 });