| | |
| | | |
| | | let socketTask = null |
| | | let heartbeatTimer = null |
| | | // SocketTask 没有稳定的 readyState,这里用事件维护连接状态。 |
| | | let isSocketOpen = false |
| | | const HEARTBEAT_INTERVAL = 30000 |
| | | |
| | | export function useGlobalWS() { |
| | | const userStore = useUserStore(); |
| | |
| | | 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() |
| | |
| | | // 关闭ws |
| | | function closeWS() { |
| | | stopHeartbeat() |
| | | socketTask?.close({ |
| | | isSocketOpen = false |
| | | const currentSocketTask = socketTask |
| | | socketTask = null |
| | | currentSocketTask?.close({ |
| | | success: () => { |
| | | console.log('ws关闭连接'); |
| | | }, |
| | |
| | | + `?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连接成功'); |
| | |
| | | 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)) |
| | | }) |
| | | //================================== |
| | | // 监听连接打开 |
| | | socketTask.onOpen((res) => { |
| | | currentSocketTask.onOpen(() => { |
| | | // 连接成功后才启动心跳,避免连接中/已关闭时发送失败。 |
| | | if (socketTask !== currentSocketTask) return |
| | | console.log('✅ WebSocket连接已建立') |
| | | isSocketOpen = true |
| | | // reconnectAttempts = 0 // 连接成功后重置重连次数 |
| | | // 可以在这里发送心跳或订阅消息 |
| | | // startHeartbeat() |
| | | startHeartbeat() |
| | | }) |
| | | // 监听连接关闭 |
| | | socketTask.onClose((res) => { |
| | | currentSocketTask.onClose((res) => { |
| | | // 关闭后必须停掉心跳,否则定时器会持续发送到失效连接。 |
| | | if (socketTask !== currentSocketTask) return |
| | | console.log(`WebSocket连接关闭,代码: ${res.code}, 原因: ${res.reason}`) |
| | | startHeartbeat() |
| | | isSocketOpen = false |
| | | stopHeartbeat() |
| | | // 根据不同的关闭代码处理 |
| | | if (res.code === 1000) { // 正常关闭 |
| | | console.log('连接正常关闭') |
| | |
| | | }) |
| | | |
| | | // 监听错误 |
| | | socketTask.onError((err) => { |
| | | currentSocketTask.onError((err) => { |
| | | if (socketTask !== currentSocketTask) return |
| | | console.error('WebSocket发生错误:', err) |
| | | isSocketOpen = false |
| | | stopHeartbeat() |
| | | }) |
| | | } |
| | | |
| | | function startHeartbeat() { |
| | | stopHeartbeat() |
| | | heartbeatTimer = setInterval(() => { |
| | | if (socketTask && socketTask.readyState === 1) { |
| | | // 尝试以 JSON 格式发送,并降低频率(30秒一次)以减少服务器负担 |
| | | if (socketTask && isSocketOpen) { |
| | | // 保持和 PC 端一致,按 JSON ping 包保活。 |
| | | socketTask.send({ |
| | | data: JSON.stringify({ type: 'ping', timestamp: Date.now() }) |
| | | data: JSON.stringify({ type: 'ping', timestamp: Date.now() }), |
| | | fail: (err) => { |
| | | console.error('ws心跳发送失败:', err) |
| | | isSocketOpen = false |
| | | stopHeartbeat() |
| | | } |
| | | }); |
| | | } |
| | | }, 30000) |
| | | }, HEARTBEAT_INTERVAL) |
| | | } |
| | | |
| | | function stopHeartbeat() { |