From 44dd9c4eb8a2489c63a5eb53fd4254bbb3510e95 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Thu, 18 Jun 2026 09:14:57 +0800
Subject: [PATCH] Merge branch 'feature/v9.0/9.0.4'

---
 src/hooks/useGlobalWS.js |  106 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 98 insertions(+), 8 deletions(-)

diff --git a/src/hooks/useGlobalWS.js b/src/hooks/useGlobalWS.js
index 450c29a..eed40a2 100644
--- a/src/hooks/useGlobalWS.js
+++ b/src/hooks/useGlobalWS.js
@@ -1,21 +1,45 @@
 import {useUserStore} from "@/store/index.js";
 import {getEnvObj} from "@/utils/index.js";
-import {enterRoom} from "@/utils/voiceCallByTX/index.js";
+import useAppStore from "../store/modules/app/index.js";
 
 let socketTask = null
+let heartbeatTimer = null
+// SocketTask 没有稳定的 readyState,这里用事件维护连接状态。
+let isSocketOpen = false
+const HEARTBEAT_INTERVAL = 30000
 
 export function useGlobalWS() {
 	const userStore = useUserStore();
+  const appStore = useAppStore();
+
 	const userId = computed(() => userStore?.userInfo?.user_id)
 	const access_token = computed(() => userStore?.userInfo?.access_token)
 	const {VITE_APP_WS_API_URL} = getEnvObj()
 
+  // 后端正常业务消息是 JSON;心跳或异常数据不应该中断消息监听。
+  function parseSocketMessage(data) {
+    try {
+      return JSON.parse(data)
+    } catch (err) {
+      console.warn('ws消息解析失败:', data)
+      return null
+    }
+  }
+
+  // 心跳响应只用于保活,不进入业务刷新/退出登录逻辑。
+  function isHeartbeatMessage(payload) {
+    return payload?.type === 'pong' || payload?.type === 'ping'
+  }
+
 	// 消息处理
 	function messageHandler(payload) {
+    if (!payload || isHeartbeatMessage(payload)) return
 		switch (payload.biz_code) {
 			case 'JOB_ISREFRESH':
+        appStore.setJobUpdateKeyAdd()
 				break
 			case 'DEVICE_ISREFRESH':
+        appStore.setDeviceUpdateKeyAdd()
 				break
 			case 'DOWNLOAD_PROGRESS':
 				break
@@ -25,9 +49,6 @@
 					url: '/pages/login/index'
 				})
 				break
-			case 'VoiceCall':
-				enterRoom(payload, userId.value)
-				break
 			default:
 				break;
 		}
@@ -35,7 +56,11 @@
 
 	// 关闭ws
 	function closeWS() {
-		socketTask?.close({
+    stopHeartbeat()
+    isSocketOpen = false
+    const currentSocketTask = socketTask
+    socketTask = null
+		currentSocketTask?.close({
 			success: () => {
 				console.log('ws关闭连接');
 			},
@@ -51,7 +76,7 @@
 			+ `?x-auth-token=${encodeURI(access_token?.value)}`
 			+ `&model_type=3&workspace-id=${userId.value}`
 		// 创建连接
-		socketTask = uni.connectSocket({
+		const currentSocketTask = uni.connectSocket({
 			url: url,
 			success: () => {
 				console.log('ws连接成功');
@@ -60,11 +85,76 @@
 				console.error('ws连接失败:', err);
 			}
 		});
+    socketTask = currentSocketTask
 		// 消息监听
-		socketTask.onMessage((result) => {
-			messageHandler(JSON.parse(result.data))
+		currentSocketTask.onMessage((result) => {
+      // 旧连接的异步回调可能晚于新连接触发,忽略旧实例事件。
+      if (socketTask !== currentSocketTask) return
+			messageHandler(parseSocketMessage(result.data))
 		})
+    //==================================
+    // 监听连接打开
+    currentSocketTask.onOpen(() => {
+      // 连接成功后才启动心跳,避免连接中/已关闭时发送失败。
+      if (socketTask !== currentSocketTask) return
+      console.log('✅ WebSocket连接已建立')
+      isSocketOpen = true
+      // reconnectAttempts = 0 // 连接成功后重置重连次数
+      // 可以在这里发送心跳或订阅消息
+      startHeartbeat()
+    })
+    // 监听连接关闭
+    currentSocketTask.onClose((res) => {
+      // 关闭后必须停掉心跳,否则定时器会持续发送到失效连接。
+      if (socketTask !== currentSocketTask) return
+      console.log(`WebSocket连接关闭,代码: ${res.code}, 原因: ${res.reason}`)
+      isSocketOpen = false
+      stopHeartbeat()
+      // 根据不同的关闭代码处理
+      if (res.code === 1000) { // 正常关闭
+        console.log('连接正常关闭')
+      } else if (res.code === 1006) { // 异常关闭
+        console.log('连接异常关闭,尝试重连...')
+      } else if (res.code === 1011) { // 服务器内部错误
+        console.log('服务器内部错误(1011),延迟重连...')
+      } else {
+        console.log('其他原因关闭,尝试重连...')
+      }
+    })
+
+    // 监听错误
+    currentSocketTask.onError((err) => {
+      if (socketTask !== currentSocketTask) return
+      console.error('WebSocket发生错误:', err)
+      isSocketOpen = false
+      stopHeartbeat()
+    })
 	}
 
+  function startHeartbeat() {
+    stopHeartbeat()
+    heartbeatTimer = setInterval(() => {
+      if (socketTask && isSocketOpen) {
+        // 保持和 PC 端一致,按 JSON ping 包保活。
+        socketTask.send({
+          data: JSON.stringify({ type: 'ping', timestamp: Date.now() }),
+          fail: (err) => {
+            console.error('ws心跳发送失败:', err)
+            isSocketOpen = false
+            stopHeartbeat()
+          }
+        });
+      }
+    }, HEARTBEAT_INTERVAL)
+  }
+
+  function stopHeartbeat() {
+    if (heartbeatTimer) {
+      clearInterval(heartbeatTimer)
+      heartbeatTimer = null
+    }
+  }
+
+
 	watch(access_token, initWS, {immediate: true})
 }

--
Gitblit v1.9.3