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: uni.getStorageSync("shopId"), token: uni.getStorageSync("iToken").tokenValue || "", group_id:'', isManualClose: false, // 是否主动关闭(true:不重连;false:自动重连) heartbeatTimer: null, // 心跳定时器 reconnectTimer: null, // 重连定时器 connectionMonitorTimer: null, // 连接状态监控定时器 reconnectCount: 0, // 当前重连次数 maxReconnectCount: 5, // 最大重连次数(避免无限重连) reconnectDelay: 1000, // 初始重连延迟(ms) maxReconnectDelay: 8000, // 最大重连延迟(ms) heartbeatInterval: 30000, // 心跳间隔(30s,根据服务器调整) isAppActive: true, // 应用是否在前台 lastHeartbeatTime: 0, // 上次心跳时间戳 lastConnectTime: 0, // 上次连接时间(防止短时间内频繁连接) // ========== 新增:修复未定义的状态 ========== _listenersInitialized: false, // 状态监听器是否已初始化 // ========== 新增:连接状态管理 ========== isConnecting: false, // 是否正在建立连接(防止重复调用connectSocket) // ========== 新增:消息去重机制 ========== recentMessages: new Map(), // 最近处理的消息映射表:key=消息特征哈希,value=处理时间戳 deduplicationWindow: 5000, // 去重时间窗口(5秒内相同消息只处理一次) maxRecentMessages: 100, // 最近消息缓存最大数量 // ========== 新增:多页面消息监听支持 ========== receiveMsgCallbacks: [], // 消息接收回调队列,支持多页面同时监听 }; }, actions: { // ========== 初始化状态监听 ========== initStateListeners() { // 避免重复初始化 if (this._listenersInitialized) return; console.log("初始化状态监听器"); // 监听应用前后台切换 uni.onAppShow(() => { console.log("应用切前台,检查Socket连接"); this.isAppActive = true; // 应用切前台,强制重连(无论当前状态如何) this.forceReconnect(); }); uni.onAppHide(() => { console.log("应用切后台"); this.isAppActive = false; // 后台可选择暂停心跳(根据实际需求调整) // this.stopHeartbeat(); }); // 监听网络状态变化 uni.onNetworkStatusChange((res) => { console.log("网络状态变化", res); if (res.isConnected) { // 网络恢复,自动重连 console.log("网络恢复,触发重连"); this.forceReconnect(); } }); this._listenersInitialized = true; }, // ========== 强制重连方法 ========== forceReconnect() { // 主动关闭状态下不重连 if (this.isManualClose) return; console.log("执行强制重连"); // 1. 清理现有连接资源 this.cleanupSocket(); // 2. 重置重连状态 this.reconnectCount = 0; this.stopHeartbeat(); this.stopConnectionMonitor(); // 3. 立即重新连接 setTimeout(() => { this.connectSocket(); }, 100); }, // ========== 新增:清理Socket资源 ========== cleanupSocket() { 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; } } }, init() { if (!this.isConnect) { return uni.showToast({ title: "请先连接socket", icon: "none", }); } this.sendMessage( { type: "OnbocChat", operate_type: "init", shop_id: this.shop_id, token: this.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.reconnect(); // 延长延迟时间,确保重连完成 setTimeout(() => { this.sendMessage(msg, isAutoAppend); }, 1500); return; } // 3. 正常发送消息 console.log("发送的消息", msg); const message = isAutoAppend ? { type: "OnbocChat", operate_type: "sendMsg", shop_id: uni.getStorageSync("shopId"), token: uni.getStorageSync("iToken").tokenValue || "", ...msg, } : msg; try { this.socketTask.send({ data: JSON.stringify(message), success: (res) => { console.log("发送成功", res); }, fail: (error) => { console.error("发送失败,触发重连", error); // 发送失败可能是连接断开,触发重连 if (!this.isManualClose) { this.forceReconnect(); // 重连后重试发送 setTimeout(() => { this.sendMessage(msg, isAutoAppend); }, 1500); } }, }); } catch (error) { console.error("发送消息异常,触发重连", error); // 捕获异常,立即重连 if (!this.isManualClose) { 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. 计算重连延迟(退避算法:1s → 2s → 4s → 8s → 8s...) const delay = Math.min( this.reconnectDelay * Math.pow(2, this.reconnectCount), this.maxReconnectDelay ); console.log(`第${this.reconnectCount + 1}次重连,延迟${delay}ms`); // 7. 延迟执行重连 this.reconnectTimer = setTimeout(() => { this.connectSocket(); this.reconnectCount++; }, delay); }, // ========== 优化:连接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(); // 缩短心跳间隔到15s(更频繁的心跳维持连接活跃) this.heartbeatInterval = 15000; this.startHeartbeat(); this.startConnectionMonitor(); // 启动连接状态监控 // 初始化状态监听(仅在首次连接时执行) if (!this._listenersInitialized) { this.initStateListeners(); this._listenersInitialized = true; } }); // 6. 接收消息回调(添加容错处理和消息去重) this.socketTask.onMessage((res) => { try { const data = JSON.parse(res.data); console.log("收到服务器消息", data); // 处理发送消息 if (data?.operate_type === "sendMsg") { this.processMessage({ ...data.data, direction: 'send' // 添加消息方向标识 }); } // 处理接收消息 if (data?.operate_type === "receive_msg") { const msg = { ...data.data, operate_type: "receive_msg", direction: 'receive' // 添加消息方向标识 }; this.processMessage(msg); } // 处理心跳响应(如果服务器返回) 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); } // 触发所有消息回调(支持多页面同时监听) 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(如果有) 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 || '', direction: direction || '', // 发送消息添加随机数,确保每次发送的消息哈希唯一 random: direction === 'send' ? Math.random().toString(36).substr(2, 9) : '' }); // 使用简单的哈希算法生成32位哈希值 let hash = 0; for (let i = 0; i < msgFeatures.length; i++) { const char = msgFeatures.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. 停止所有定时器,清理资源 this.stopHeartbeat(); this.stopConnectionMonitor(); if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } // 3. 清理Socket资源 this.cleanupSocket(); // 4. 重置连接状态 this.isConnect = false; this.reconnectCount = 0; // 重置重连次数 console.log("Socket已主动关闭"); }, }, unistorage: false, // 开启后对 state 的数据读写都将持久化 });