From 7239313f0fa32e469016456282d4dfeda4e2aa3d Mon Sep 17 00:00:00 2001 From: zoujing Date: Thu, 8 Jan 2026 14:46:44 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=8F=91?= =?UTF-8?q?=E9=80=81=E6=B6=88=E6=81=AF=E7=9A=84=E4=B8=AD=E6=96=AD=E9=87=8D?= =?UTF-8?q?=E8=BF=9E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatMainList/index.vue | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/index/components/chat/ChatMainList/index.vue b/src/pages/index/components/chat/ChatMainList/index.vue index 653c8fd..bc51ac3 100644 --- a/src/pages/index/components/chat/ChatMainList/index.vue +++ b/src/pages/index/components/chat/ChatMainList/index.vue @@ -581,18 +581,19 @@ const sendChat = (message, isInstruct = false) => { initWebSocket(); // 短暂延迟后再次检查连接状态 setTimeout(() => { - if (webSocketManager && webSocketManager.isConnected()) { + const connected = webSocketManager && webSocketManager.isConnected(); + isSessionActive.value = connected; + // 更新AI消息状态 + const aiMsgIndex = chatMsgList.value.length - 1; + if (aiMsgIndex >= 0 && chatMsgList.value[aiMsgIndex].msgType === MessageRole.AI) { + chatMsgList.value[aiMsgIndex].msg = connected ? "" : "发送消息失败,请重试"; + chatMsgList.value[aiMsgIndex].isLoading = connected; + } + if (connected) { // 连接成功后重新发送消息 sendChat(message, isInstruct); } else { console.error("WebSocket重新初始化失败"); - isSessionActive.value = false; - // 更新AI消息状态为失败 - const aiMsgIndex = chatMsgList.value.length - 1; - if (aiMsgIndex >= 0 && chatMsgList.value[aiMsgIndex].msgType === MessageRole.AI) { - chatMsgList.value[aiMsgIndex].msg = "发送消息失败,请重试"; - chatMsgList.value[aiMsgIndex].isLoading = false; - } } }, 1000); return; From fb15b8aec17fc8e7e97b582eb4af2bcdf79c25e3 Mon Sep 17 00:00:00 2001 From: zoujing Date: Thu, 8 Jan 2026 15:03:43 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E5=8F=91=E9=80=81=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatMainList/index.vue | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/pages/index/components/chat/ChatMainList/index.vue b/src/pages/index/components/chat/ChatMainList/index.vue index bc51ac3..8ecb168 100644 --- a/src/pages/index/components/chat/ChatMainList/index.vue +++ b/src/pages/index/components/chat/ChatMainList/index.vue @@ -148,8 +148,8 @@ let commonType = ""; // WebSocket 相关 let webSocketManager = null; -/// WebSocket 连接状态 -let webSocketConnectStatus = false; +/// 使用统一的连接状态判断函数,避免状态不同步 +const isWsConnected = () => !!(webSocketManager && typeof webSocketManager.isConnected === "function" && webSocketManager.isConnected()); // 当前会话的消息ID,用于保持发送和终止的messageId一致 let currentSessionMessageId = null; @@ -251,7 +251,7 @@ const sendMessageAction = (inputText) => { /// 添加通知 const addNoticeListener = () => { uni.$on(NOTICE_EVENT_LOGIN_SUCCESS, () => { - if (!webSocketConnectStatus) { + if (!isWsConnected()) { initHandler(); } }); @@ -375,14 +375,12 @@ const initWebSocket = async () => { onOpen: (event) => { console.log("WebSocket连接成功"); // 重置会话状态 - webSocketConnectStatus = true; isSessionActive.value = false; // 连接成功时重置会话状态,避免影响新消息发送 }, // 连接断开回调 onClose: (event) => { console.error("WebSocket连接断开:", event); - webSocketConnectStatus = false; // 停止当前会话 isSessionActive.value = false; }, @@ -390,7 +388,6 @@ const initWebSocket = async () => { // 错误回调 onError: (error) => { console.error("WebSocket错误:", error); - webSocketConnectStatus = false; isSessionActive.value = false; }, @@ -410,11 +407,9 @@ const initWebSocket = async () => { // 初始化连接 await webSocketManager.connect(); console.log("WebSocket连接初始化成功"); - webSocketConnectStatus = true; return true; } catch (error) { console.error("WebSocket连接失败:", error); - webSocketConnectStatus = false; return false; } }; @@ -429,7 +424,7 @@ const handleWebSocketMessage = (data) => { // 确保消息内容是字符串类型 if (data.content && typeof data.content !== "string") { - data.content = String(data.content); + data.content = JSON.stringify(data.content); } // 直接拼接内容到AI消息 @@ -491,7 +486,7 @@ const sendMessage = async (message, isInstruct = false) => { await checkToken(); // 检查WebSocket连接状态,如果未连接,尝试重新连接 - if (!webSocketConnectStatus) { + if (!isWsConnected()) { console.log("WebSocket未连接,尝试重新连接..."); // 显示加载提示 uni.showLoading({ @@ -505,7 +500,7 @@ const sendMessage = async (message, isInstruct = false) => { await new Promise(resolve => setTimeout(resolve, 1000)); // 检查连接是否成功建立 - if (!webSocketConnectStatus) { + if (!isWsConnected()) { uni.hideLoading(); uni.showToast({ title: "连接服务器失败,请稍后重试", @@ -551,7 +546,7 @@ const sendMessage = async (message, isInstruct = false) => { }; // 通用WebSocket消息发送函数 -const sendWebSocketMessage = (messageType, messageContent, options = {}) => { +const sendWebSocketMessage = (messageType, messageContent) => { const args = { conversationId: conversationId.value, agentId: agentId.value, @@ -636,7 +631,7 @@ const stopRequest = () => { console.log("停止请求"); // 发送中断消息给服务器 (messageType=2) - sendWebSocketMessage(2, "stop_request", { silent: true }); + sendWebSocketMessage(2, "stop_request"); // 直接将AI消息状态设为停止 const aiMsgIndex = chatMsgList.value.length - 1; @@ -677,7 +672,6 @@ const resetConfig = () => { if (webSocketManager) { webSocketManager.destroy(); webSocketManager = null; - webSocketConnectStatus = false; } // 重置消息状态 From af615f666c748f526d332bdb9500730f647f0b0c Mon Sep 17 00:00:00 2001 From: zoujing Date: Thu, 8 Jan 2026 16:26:10 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=B6=88=E6=81=AF=E4=B8=8Esocket?= =?UTF-8?q?=E7=9A=84=E4=BC=98=E5=8C=96=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatMainList/index.vue | 350 +++++++++++++----- src/utils/WebSocketManager.js | 13 +- 2 files changed, 268 insertions(+), 95 deletions(-) diff --git a/src/pages/index/components/chat/ChatMainList/index.vue b/src/pages/index/components/chat/ChatMainList/index.vue index 8ecb168..852e2e5 100644 --- a/src/pages/index/components/chat/ChatMainList/index.vue +++ b/src/pages/index/components/chat/ChatMainList/index.vue @@ -151,6 +151,19 @@ let webSocketManager = null; /// 使用统一的连接状态判断函数,避免状态不同步 const isWsConnected = () => !!(webSocketManager && typeof webSocketManager.isConnected === "function" && webSocketManager.isConnected()); +// pendingMap: messageId -> msgIndex +const pendingMap = new Map(); +// pendingTimeouts: messageId -> timeoutId +const pendingTimeouts = new Map(); +// 超时时间(ms) +const MESSAGE_TIMEOUT = 30000; + +// 防止并发初始化 websocket +let isInitializing = false; +let pendingInitPromise = null; +// sleep helper +const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); + // 当前会话的消息ID,用于保持发送和终止的messageId一致 let currentSessionMessageId = null; @@ -355,85 +368,136 @@ const getMainPageData = async () => { /// =============对话↓================ // 初始化WebSocket const initWebSocket = async () => { - // 清理旧实例 - if (webSocketManager) { - webSocketManager.destroy(); + // 防止并发初始化 + if (isInitializing) { + return pendingInitPromise; } - // 使用配置的WebSocket服务器地址 - const token = getAccessToken(); - const wsUrl = `${appStore.serverConfig.wssUrl}?access_token=${token}`; + isInitializing = true; + pendingInitPromise = (async () => { + // 清理旧实例 + if (webSocketManager) { + try { + webSocketManager.destroy(); + } catch (e) { + console.warn("destroy old webSocketManager failed:", e); + } + webSocketManager = null; + } - // 初始化WebSocket管理器 - webSocketManager = new WebSocketManager({ - wsUrl: wsUrl, - reconnectInterval: 3000, // 重连间隔 - maxReconnectAttempts: 5, // 最大重连次数 - heartbeatInterval: 30000, // 心跳间隔 + // 使用配置的WebSocket服务器地址 + const token = getAccessToken(); + const wsUrl = `${appStore.serverConfig.wssUrl}?access_token=${token}`; - // 连接成功回调 - onOpen: (event) => { - console.log("WebSocket连接成功"); - // 重置会话状态 - isSessionActive.value = false; // 连接成功时重置会话状态,避免影响新消息发送 - }, + // 初始化WebSocket管理器 + webSocketManager = new WebSocketManager({ + wsUrl: wsUrl, + reconnectInterval: 3000, // 重连间隔 + maxReconnectAttempts: 5, // 最大重连次数 + heartbeatInterval: 30000, // 心跳间隔 - // 连接断开回调 - onClose: (event) => { - console.error("WebSocket连接断开:", event); - // 停止当前会话 - isSessionActive.value = false; - }, + // 连接成功回调 + onOpen: (event) => { + console.log("WebSocket连接成功"); + // 重置会话状态 + isSessionActive.value = false; // 连接成功时重置会话状态,避免影响新消息发送 + }, - // 错误回调 - onError: (error) => { - console.error("WebSocket错误:", error); - isSessionActive.value = false; - }, + // 连接断开回调 + onClose: (event) => { + console.error("WebSocket连接断开:", event); + // 停止当前会话 + isSessionActive.value = false; + }, - // 消息回调 - onMessage: (data) => { - handleWebSocketMessage(data); - }, + // 错误回调 + onError: (error) => { + console.error("WebSocket错误:", error); + isSessionActive.value = false; + }, - // 获取会话ID回调 (用于心跳检测) - getConversationId: () => conversationId.value, + // 消息回调 + onMessage: (data) => { + handleWebSocketMessage(data); + }, - // 获取代理ID回调 (用于心跳检测) - getAgentId: () => agentId.value, - }); + // 获取会话ID回调 (用于心跳检测) + getConversationId: () => conversationId.value, - try { - // 初始化连接 - await webSocketManager.connect(); - console.log("WebSocket连接初始化成功"); - return true; - } catch (error) { - console.error("WebSocket连接失败:", error); - return false; - } + // 获取代理ID回调 (用于心跳检测) + getAgentId: () => agentId.value, + }); + + try { + // 初始化连接 + await webSocketManager.connect(); + console.log("WebSocket连接初始化成功"); + return true; + } catch (error) { + console.error("WebSocket连接失败:", error); + return false; + } finally { + isInitializing = false; + pendingInitPromise = null; + } + })(); + + return pendingInitPromise; }; // 处理WebSocket消息 const handleWebSocketMessage = (data) => { - const aiMsgIndex = chatMsgList.value.length - 1; - if (!chatMsgList.value[aiMsgIndex] || aiMsgIndex < 0) { - console.error("处理WebSocket消息时找不到对应的AI消息项"); + // 验证关键字段(若服务端传回 conversationId/agentId,则校验是否属于当前会话) + if (data.conversationId && data.conversationId !== conversationId.value) { + console.warn("收到不属于当前会话的消息,忽略", data.conversationId); + return; + } + if (data.agentId && data.agentId !== agentId.value) { + console.warn("收到不属于当前 agent 的消息,忽略", data.agentId); return; } // 确保消息内容是字符串类型 if (data.content && typeof data.content !== "string") { - data.content = JSON.stringify(data.content); + try { + data.content = JSON.stringify(data.content); + } catch (e) { + data.content = String(data.content); + } } - // 直接拼接内容到AI消息 + // 优先使用 messageId 进行匹配 + const msgId = data.messageId || data.id || data.msgId; + let aiMsgIndex = -1; + if (msgId && pendingMap.has(msgId)) { + aiMsgIndex = pendingMap.get(msgId); + } else if (!msgId && currentSessionMessageId && pendingMap.has(currentSessionMessageId)) { + // 服务端未返回 messageId 的场景:优先使用当前会话的 messageId 映射 + aiMsgIndex = pendingMap.get(currentSessionMessageId); + } else { + // 向后搜索最近的 AI 消息 + for (let i = chatMsgList.value.length - 1; i >= 0; i--) { + if (chatMsgList.value[i] && chatMsgList.value[i].msgType === MessageRole.AI) { + aiMsgIndex = i; + break; + } + } + if (aiMsgIndex === -1) { + console.error("处理WebSocket消息时找不到对应的AI消息项"); + return; + } + } + + // 直接拼接内容到对应 AI 消息 if (data.content) { if (chatMsgList.value[aiMsgIndex].isLoading) { - chatMsgList.value[aiMsgIndex].msg = ""; + // 首次收到内容:替换“加载中”文案并取消 loading 状态(恢复原始渲染逻辑) + chatMsgList.value[aiMsgIndex].msg = data.content; + chatMsgList.value[aiMsgIndex].isLoading = false; + } else { + // 后续流式内容追加 + chatMsgList.value[aiMsgIndex].msg += data.content; } - chatMsgList.value[aiMsgIndex].msg += data.content; - chatMsgList.value[aiMsgIndex].isLoading = false; nextTick(() => scrollToBottom()); } @@ -458,8 +522,20 @@ const handleWebSocketMessage = (data) => { chatMsgList.value[aiMsgIndex].question = data.question; } + // 清理 pendingMap / timeout + const ownedMessageId = chatMsgList.value[aiMsgIndex].messageId || msgId; + if (ownedMessageId) { + if (pendingTimeouts.has(ownedMessageId)) { + clearTimeout(pendingTimeouts.get(ownedMessageId)); + pendingTimeouts.delete(ownedMessageId); + } + pendingMap.delete(ownedMessageId); + } + // 重置会话状态 isSessionActive.value = false; + // 清理当前会话的 messageId,避免保留陈旧 id + resetMessageState(); } }; @@ -545,31 +621,76 @@ const sendMessage = async (message, isInstruct = false) => { console.log("发送的新消息:", JSON.stringify(newMsg)); }; -// 通用WebSocket消息发送函数 -const sendWebSocketMessage = (messageType, messageContent) => { +// 通用WebSocket消息发送函数 -> 返回 Promise +const sendWebSocketMessage = async (messageType, messageContent, options = {}) => { const args = { conversationId: conversationId.value, agentId: agentId.value, messageType: String(messageType), // 消息类型 0-对话 1-指令 2-中断停止 3-心跳检测 messageContent: messageContent, - messageId: currentSessionMessageId, + messageId: options.messageId || currentSessionMessageId, }; - try { - // 直接调用webSocketManager的sendMessage方法,利用其内部的消息队列机制 - // 即使当前连接断开,消息也会被加入队列,等待连接恢复后发送 - const result = webSocketManager.sendMessage(args); - console.log(`WebSocket消息已发送 [类型:${messageType}]:`, args); - return result; - } catch (error) { - console.error("发送WebSocket消息失败:", error); + const maxRetries = typeof options.retries === 'number' ? options.retries : 3; + const baseDelay = typeof options.baseDelay === 'number' ? options.baseDelay : 300; // ms + const maxDelay = typeof options.maxDelay === 'number' ? options.maxDelay : 5000; // ms + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + // 确保连接 + if (!isWsConnected()) { + if (options.tryReconnect) { + try { + await initWebSocket(); + } catch (e) { + console.error('reconnect failed in sendWebSocketMessage:', e); + } + } + } + + if (!isWsConnected()) { + if (!options.silent) console.warn('WebSocket 未连接,无法发送消息', args); + // 如果还有重试机会,进行等待后重试 + if (attempt < maxRetries) { + const delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt)); + await sleep(delay); + continue; + } + isSessionActive.value = false; + return false; + } + + try { + const raw = webSocketManager.sendMessage(args); + // 兼容可能返回同步布尔或 Promise 的实现 + const result = await Promise.resolve(raw); + if (result) { + console.log(`WebSocket消息已发送 [类型:${messageType}]:`, args); + return true; + } + // 若返回 false,准备重试 + console.warn('webSocketManager.sendMessage 返回 false,准备重试', { attempt, args }); + } catch (error) { + console.error('发送WebSocket消息异常:', error, args); + } + + // 失败且还有重试机会,等待指数退避 + if (attempt < maxRetries) { + const delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt)); + await sleep(delay + Math.floor(Math.random() * 100)); + continue; + } + + // 最后一次失败 isSessionActive.value = false; return false; } + // 不可达,但为了类型安全 + isSessionActive.value = false; + return false; }; // 发送获取AI聊天消息 -const sendChat = (message, isInstruct = false) => { +const sendChat = async (message, isInstruct = false) => { // 检查WebSocket管理器是否存在,如果不存在,尝试重新初始化 if (!webSocketManager) { console.error("WebSocket管理器不存在,尝试重新初始化..."); @@ -596,9 +717,10 @@ const sendChat = (message, isInstruct = false) => { const messageType = isInstruct ? 1 : 0; const messageContent = isInstruct ? commonType : message; + // 生成 messageId 并保存到当前会话变量(stopRequest 可能使用) currentSessionMessageId = IdUtils.generateMessageId(); - // 插入AI消息 + // 插入AI消息,并在 pendingMap 中记录 const aiMsg = { msgId: `msg_${chatMsgList.value.length}`, msgType: MessageRole.AI, @@ -608,49 +730,88 @@ const sendChat = (message, isInstruct = false) => { type: MessageType.TEXT, url: "", }, + messageId: currentSessionMessageId, }; chatMsgList.value.push(aiMsg); // 添加AI消息后滚动到底部 setTimeoutScrollToBottom(); const aiMsgIndex = chatMsgList.value.length - 1; - // 发送消息 - const success = sendWebSocketMessage(messageType, messageContent); + // 记录 pendingMap + pendingMap.set(currentSessionMessageId, aiMsgIndex); + + // 启动超时回退 + const timeoutId = setTimeout(() => { + const idx = pendingMap.get(currentSessionMessageId); + if (idx != null && chatMsgList.value[idx] && chatMsgList.value[idx].isLoading) { + chatMsgList.value[idx].msg = "请求超时,请重试"; + chatMsgList.value[idx].isLoading = false; + pendingMap.delete(currentSessionMessageId); + pendingTimeouts.delete(currentSessionMessageId); + isSessionActive.value = false; + setTimeoutScrollToBottom(); + } + }, MESSAGE_TIMEOUT); + pendingTimeouts.set(currentSessionMessageId, timeoutId); + + // 发送消息,支持重连尝试 + const success = await sendWebSocketMessage(messageType, messageContent, { messageId: currentSessionMessageId, tryReconnect: true }); if (!success) { - chatMsgList.value[aiMsgIndex].msg = "发送消息失败,请重试"; - chatMsgList.value[aiMsgIndex].isLoading = false; + const idx = pendingMap.get(currentSessionMessageId); + if (idx != null && chatMsgList.value[idx]) { + chatMsgList.value[idx].msg = "发送消息失败,请重试"; + chatMsgList.value[idx].isLoading = false; + } + // 清理 pending + if (pendingTimeouts.has(currentSessionMessageId)) { + clearTimeout(pendingTimeouts.get(currentSessionMessageId)); + pendingTimeouts.delete(currentSessionMessageId); + } + pendingMap.delete(currentSessionMessageId); isSessionActive.value = false; } - - // 重置消息状态,为新消息做准备 - resetMessageState(); }; // 停止请求函数 -const stopRequest = () => { +const stopRequest = async () => { console.log("停止请求"); - // 发送中断消息给服务器 (messageType=2) - sendWebSocketMessage(2, "stop_request"); + // 发送中断消息给服务器 (messageType=2),带上当前 messageId + try { + await sendWebSocketMessage(2, "stop_request", { messageId: currentSessionMessageId, silent: true }); + } catch (e) { + console.warn("stopRequest send failed:", e); + } - // 直接将AI消息状态设为停止 - const aiMsgIndex = chatMsgList.value.length - 1; - if ( - chatMsgList.value[aiMsgIndex] && - chatMsgList.value[aiMsgIndex].msgType === MessageRole.AI - ) { + // 直接将AI消息状态设为停止(优先使用 pendingMap 映射) + let aiMsgIndex = -1; + if (currentSessionMessageId && pendingMap.has(currentSessionMessageId)) { + aiMsgIndex = pendingMap.get(currentSessionMessageId); + } else { + aiMsgIndex = chatMsgList.value.length - 1; + } + + if (chatMsgList.value[aiMsgIndex] && + chatMsgList.value[aiMsgIndex].msgType === MessageRole.AI) { chatMsgList.value[aiMsgIndex].isLoading = false; - if ( - chatMsgList.value[aiMsgIndex].msg && - chatMsgList.value[aiMsgIndex].msg.trim() && - !chatMsgList.value[aiMsgIndex].msg.startsWith("加载中") - ) { + if (chatMsgList.value[aiMsgIndex].msg && + chatMsgList.value[aiMsgIndex].msg.trim() && + !chatMsgList.value[aiMsgIndex].msg.startsWith("加载中")) { // 保留已显示内容 } else { chatMsgList.value[aiMsgIndex].msg = "请求已停止"; } } + // 清理 pending + if (currentSessionMessageId) { + if (pendingTimeouts.has(currentSessionMessageId)) { + clearTimeout(pendingTimeouts.get(currentSessionMessageId)); + pendingTimeouts.delete(currentSessionMessageId); + } + pendingMap.delete(currentSessionMessageId); + } + // 重置会话状态 isSessionActive.value = false; setTimeoutScrollToBottom(); @@ -678,6 +839,17 @@ const resetConfig = () => { resetMessageState(); isSessionActive.value = false; + // 清理 pendingMap / pendingTimeouts + try { + for (const t of pendingTimeouts.values()) { + clearTimeout(t); + } + } catch (e) { + // ignore + } + pendingTimeouts.clear(); + pendingMap.clear(); + // 清理定时器 if (holdKeyboardTimer.value) { clearTimeout(holdKeyboardTimer.value); diff --git a/src/utils/WebSocketManager.js b/src/utils/WebSocketManager.js index d3bcb35..b867516 100644 --- a/src/utils/WebSocketManager.js +++ b/src/utils/WebSocketManager.js @@ -28,10 +28,11 @@ export class WebSocketManager { // 回调函数 this.callbacks = { - onConnect: options.onConnect || (() => {}), - onDisconnect: options.onDisconnect || (() => {}), - onError: options.onError || (() => {}), - onMessage: options.onMessage || (() => {}), + // 支持两套回调命名:onConnect/onDisconnect 与 onOpen/onClose(兼容调用方) + onConnect: options.onConnect || options.onOpen || (() => { }), + onDisconnect: options.onDisconnect || options.onClose || (() => { }), + onError: options.onError || (() => { }), + onMessage: options.onMessage || (() => { }), getConversationId: options.getConversationId || (() => ""), getAgentId: options.getAgentId || (() => ""), }; @@ -327,7 +328,7 @@ export class WebSocketManager { const messageData = { ...message, timestamp: Date.now(), - retryCount: 0, + retryCount: typeof message.retryCount === 'number' ? message.retryCount : 0, }; if (this.connectionState) { @@ -392,7 +393,7 @@ export class WebSocketManager { agentId: this.callbacks.getAgentId ? this.callbacks.getAgentId() : "", - messageType: 3, // 心跳检测 + messageType: '3', // 心跳检测 messageContent: "heartbeat", messageId: IdUtils.generateMessageId(), }; From 06ec029555b21df83e08f2a8bb6add23bbf350ac Mon Sep 17 00:00:00 2001 From: zoujing Date: Thu, 8 Jan 2026 17:01:47 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat=EF=BC=9A=E9=87=8D=E8=BF=9E=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/chat/ChatMainList/index.vue | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/pages/index/components/chat/ChatMainList/index.vue b/src/pages/index/components/chat/ChatMainList/index.vue index 852e2e5..f15a943 100644 --- a/src/pages/index/components/chat/ChatMainList/index.vue +++ b/src/pages/index/components/chat/ChatMainList/index.vue @@ -662,13 +662,31 @@ const sendWebSocketMessage = async (messageType, messageContent, options = {}) = try { const raw = webSocketManager.sendMessage(args); // 兼容可能返回同步布尔或 Promise 的实现 - const result = await Promise.resolve(raw); - if (result) { - console.log(`WebSocket消息已发送 [类型:${messageType}]:`, args); - return true; - } - // 若返回 false,准备重试 - console.warn('webSocketManager.sendMessage 返回 false,准备重试', { attempt, args }); + const result = await Promise.resolve(raw); + if (result) { + console.log(`WebSocket消息已发送 [类型:${messageType}]:`, args); + return true; + } + + // 若返回 false,消息可能已经被 manager 入队并触发连接流程。 + // 在这种情况下避免立即当作失败处理,而是等待短暂时间以观察连接是否建立并由 manager 发送队列。 + console.warn('webSocketManager.sendMessage 返回 false,等待连接或队列发送...', { attempt, args }); + const waitForConnectMs = typeof options.waitForConnectMs === 'number' ? options.waitForConnectMs : 5000; + if (webSocketManager && typeof webSocketManager.isConnected === 'function' && !webSocketManager.isConnected()) { + const startTs = Date.now(); + while (Date.now() - startTs < waitForConnectMs) { + await sleep(200); + if (webSocketManager.isConnected()) { + // 给 manager 一点时间处理队列并发送 + await sleep(150); + console.log('检测到 manager 已连接,假定队列消息已发送', args); + return true; + } + } + console.warn('等待 manager 建连超时,进入重试逻辑', { waitForConnectMs, args }); + } else { + console.warn('sendMessage 返回 false 但 manager 看起来已连接或不可用,继续重试', { args }); + } } catch (error) { console.error('发送WebSocket消息异常:', error, args); }