From a034f10a19efaa558a460dba1c631c8e208d0cf7 Mon Sep 17 00:00:00 2001 From: MoeSnowyFox Date: Thu, 11 Sep 2025 23:25:08 +0800 Subject: [PATCH 1/2] =?UTF-8?q?:wastebasket:=20=E7=A7=BB=E9=99=A4ws?= =?UTF-8?q?=E6=97=A7=E7=89=88=E6=9C=AC=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/composables/useWebSocket.ts | 141 +++++------------------ frontend/src/views/GeneralUserEdit.vue | 56 ++------- frontend/src/views/MAAUserEdit.vue | 74 +++++------- frontend/src/views/Scripts.vue | 14 +-- 4 files changed, 71 insertions(+), 214 deletions(-) diff --git a/frontend/src/composables/useWebSocket.ts b/frontend/src/composables/useWebSocket.ts index dac6815..255f1db 100644 --- a/frontend/src/composables/useWebSocket.ts +++ b/frontend/src/composables/useWebSocket.ts @@ -45,22 +45,6 @@ export interface WebSocketSubscriber { onResult?: (data: ResultMessage) => void onError?: (err: ErrorMessage) => void onNotify?: (n: NotifyMessage) => void - // 兼容旧版 API - onMessage?: (raw: WebSocketBaseMessage) => void - onStatusChange?: (status: WebSocketStatus) => void -} - -// 兼容旧版 connect(config) 接口 -export interface WebSocketConfig { - taskId: string - mode?: string - showNotifications?: boolean - onProgress?: (data: ProgressMessage) => void - onResult?: (data: ResultMessage) => void - onError?: (err: ErrorMessage | string) => void - onNotify?: (n: NotifyMessage) => void - onMessage?: (raw: WebSocketBaseMessage) => void - onStatusChange?: (status: WebSocketStatus) => void } // 后端状态类型 @@ -136,11 +120,6 @@ const getGlobalStorage = (): GlobalWSStorage => { const setGlobalStatus = (status: WebSocketStatus) => { const global = getGlobalStorage() global.status.value = status - - // 广播状态变化给所有订阅者(兼容 onStatusChange) - global.subscribers.value.forEach(sub => { - sub.onStatusChange?.(status) - }) } // 设置后端状态 @@ -295,25 +274,9 @@ const startGlobalHeartbeat = (ws: WebSocket) => { try { const pingTime = Date.now() global.lastPingTime = pingTime - const pingData = { Ping: pingTime, connectionId: global.connectionId } - - const pingMessage = JSON.stringify({ - type: 'Signal', - data: pingData - }) - - ws.send(pingMessage) - - // 心跳超时检测 - 但不主动断开连接 - setTimeout(() => { - if (global.lastPingTime === pingTime && ws.readyState === WebSocket.OPEN) { - // 心跳超时但保持连接,等待网络层或服务端处理 - } - }, HEARTBEAT_TIMEOUT) - - } catch (e) { - // 心跳发送失败,静默处理 - } + ws.send(JSON.stringify({ type: 'Signal', data: { Ping: pingTime, connectionId: global.connectionId } })) + setTimeout(() => { /* 心跳超时不主动断开 */ }, HEARTBEAT_TIMEOUT) + } catch { /* ignore */ } } }, HEARTBEAT_INTERVAL) } @@ -336,12 +299,7 @@ const handleMessage = (raw: WebSocketBaseMessage) => { const ws = global.wsRef if (ws && ws.readyState === WebSocket.OPEN) { try { - const pongMessage = { - type: 'Signal', - data: { Pong: raw.data.Ping, connectionId: global.connectionId } - } - const pongJson = JSON.stringify(pongMessage) - ws.send(pongJson) + ws.send(JSON.stringify({ type: 'Signal', data: { Pong: raw.data.Ping, connectionId: global.connectionId } })) } catch (e) { // Pong发送失败,静默处理 } @@ -353,9 +311,6 @@ const handleMessage = (raw: WebSocketBaseMessage) => { const dispatch = (sub: WebSocketSubscriber) => { if (msgType === 'Signal') return - // 兼容旧版:先调用通用 onMessage 回调 - sub.onMessage?.(raw) - if (msgType === 'Progress') return sub.onProgress?.(raw.data as ProgressMessage) if (msgType === 'Result') return sub.onResult?.(raw.data as ResultMessage) if (msgType === 'Error') { @@ -408,7 +363,7 @@ export const connectAfterBackendStart = async (): Promise => { } } -// 创建 WebSocket 连接 - 移除销毁检查,确保永不放弃连接 +// 创建 WebSocket 连接 const createGlobalWebSocket = (): WebSocket => { const global = getGlobalStorage() @@ -428,7 +383,7 @@ const createGlobalWebSocket = (): WebSocket => { ws.onopen = () => { global.isConnecting = false global.hasEverConnected = true - global.reconnectAttempts = 0 // 重置重连计数 + global.reconnectAttempts = 0 setGlobalStatus('已连接') startGlobalHeartbeat(ws) @@ -438,23 +393,9 @@ const createGlobalWebSocket = (): WebSocket => { // 发送连接确认和初始pong try { - const connectData = { Connect: true, connectionId: global.connectionId } - const connectMessage = JSON.stringify({ - type: 'Signal', - data: connectData - }) - - ws.send(connectMessage) - - // 发送初始pong以重置后端last_pong时间 - const initialPongMessage = JSON.stringify({ - type: 'Signal', - data: { Pong: Date.now(), connectionId: global.connectionId } - }) - ws.send(initialPongMessage) - } catch (e) { - // 连接确认发送失败,静默处理 - } + ws.send(JSON.stringify({ type: 'Signal', data: { Connect: true, connectionId: global.connectionId } })) + ws.send(JSON.stringify({ type: 'Signal', data: { Pong: Date.now(), connectionId: global.connectionId } })) + } catch { /* ignore */ } } ws.onmessage = (ev) => { @@ -490,7 +431,7 @@ const createGlobalWebSocket = (): WebSocket => { return ws } -// 连接全局 WebSocket - 确保单一连接 +// 连接全局 WebSocket const connectGlobalWebSocket = async (reason: string = '未指定原因'): Promise => { const global = getGlobalStorage() @@ -562,8 +503,6 @@ const connectGlobalWebSocket = async (reason: string = '未指定原因'): Promi } } -// 移除未使用的函数,已改为外部调用 connectAfterBackendStart - // 连接权限控制函数 const setConnectionPermission = (allow: boolean, reason: string) => { const global = getGlobalStorage() @@ -573,10 +512,7 @@ const setConnectionPermission = (allow: boolean, reason: string) => { const checkConnectionPermission = (): boolean => { const global = getGlobalStorage() - if (!global.allowNewConnection) { - return false - } - return true + return !!global.allowNewConnection } // 只在后端启动/重启时允许创建连接 @@ -585,9 +521,7 @@ const allowedConnectionReasons = [ '后端重启后重连' ] -const isValidConnectionReason = (reason: string): boolean => { - return allowedConnectionReasons.includes(reason) -} +const isValidConnectionReason = (reason: string): boolean => allowedConnectionReasons.includes(reason) // 全局连接锁 - 防止多个模块实例同时连接 let isGlobalConnectingLock = false @@ -636,65 +570,44 @@ export function useWebSocket() { if (ws && ws.readyState === WebSocket.OPEN) { try { - const messageData = { id, type, data } - ws.send(JSON.stringify(messageData)) + ws.send(JSON.stringify({ id, type, data })) } catch (e) { // 发送失败,静默处理 } } } - // 移除 forceReconnect 功能,现在只能通过后端重启建立连接 - const ensureConnection = () => { - return Promise.resolve(false) - } + const getConnectionInfo = () => ({ + connectionId: global.connectionId, + status: global.status.value, + subscriberCount: global.subscribers.value.size, + moduleLoadCount: global.moduleLoadCount, + wsReadyState: global.wsRef ? global.wsRef.readyState : null, + isConnecting: global.isConnecting, + hasHeartbeat: !!global.heartbeatTimer, + hasEverConnected: global.hasEverConnected, + reconnectAttempts: global.reconnectAttempts, + isPersistentMode: true // 标识为永久连接模式 + }) - const getConnectionInfo = () => { - const info = { - connectionId: global.connectionId, - status: global.status.value, - subscriberCount: global.subscribers.value.size, - moduleLoadCount: global.moduleLoadCount, - wsReadyState: global.wsRef ? global.wsRef.readyState : null, - isConnecting: global.isConnecting, - hasHeartbeat: !!global.heartbeatTimer, - hasEverConnected: global.hasEverConnected, - reconnectAttempts: global.reconnectAttempts, - isPersistentMode: true // 标识为永久连接模式 - } - return info - } - - // 手动重启后端 const restartBackendManually = async () => { const global = getGlobalStorage() - global.backendRestartAttempts = 0 // 重置重启计数 + global.backendRestartAttempts = 0 return await restartBackend() } - // 获取后端状态 const getBackendStatus = () => { const global = getGlobalStorage() - return { - status: global.backendStatus.value, - restartAttempts: global.backendRestartAttempts, - isRestarting: global.isRestartingBackend, - lastCheck: global.lastBackendCheck - } + return { status: global.backendStatus.value, restartAttempts: global.backendRestartAttempts, isRestarting: global.isRestartingBackend, lastCheck: global.lastBackendCheck } } return { - // 新的订阅 API subscribe, unsubscribe, sendRaw, - // 连接管理 - ensureConnection, getConnectionInfo, - // 状态 status: global.status, subscribers: global.subscribers, - // 后端管理 backendStatus: global.backendStatus, restartBackend: restartBackendManually, getBackendStatus diff --git a/frontend/src/views/GeneralUserEdit.vue b/frontend/src/views/GeneralUserEdit.vue index bd708bc..9e0cdf6 100644 --- a/frontend/src/views/GeneralUserEdit.vue +++ b/frontend/src/views/GeneralUserEdit.vue @@ -360,7 +360,7 @@ const router = useRouter() const route = useRoute() const { addUser, updateUser, getUsers, loading: userLoading } = useUserApi() const { getScript } = useScriptApi() -const { connect, disconnect } = useWebSocket() +const { subscribe, unsubscribe } = useWebSocket() const formRef = ref() const loading = computed(() => userLoading.value) @@ -513,47 +513,28 @@ const loadUserData = async () => { const handleSubmit = async () => { try { await formRef.value?.validate() - - // 确保扁平化字段同步到嵌套数据 formData.Info.Name = formData.userName - - console.log('提交前的表单数据:', { - userName: formData.userName, - InfoName: formData.Info.Name, - isEdit: isEdit.value, - }) - - // 构建提交数据,移除通用脚本不需要的MAA专用字段 - const { IfSendSixStar, ...generalNotify } = formData.Notify - const userData = { Info: { ...formData.Info }, - Notify: generalNotify, + Notify: { ...formData.Notify }, Data: { ...formData.Data }, } - if (isEdit.value) { - // 编辑模式 const result = await updateUser(scriptId, userId, userData) if (result) { message.success('用户更新成功') handleCancel() } } else { - // 添加模式 const result = await addUser(scriptId) if (result) { - // 创建成功后立即更新用户数据 try { const updateResult = await updateUser(scriptId, result.userId, userData) - console.log('用户数据更新结果:', updateResult) - if (updateResult) { message.success('用户创建成功') handleCancel() } else { message.error('用户创建成功,但数据更新失败,请手动编辑用户信息') - // 不跳转,让用户可以重新保存 } } catch (updateError) { console.error('更新用户数据时发生错误:', updateError) @@ -575,35 +556,23 @@ const handleGeneralConfig = async () => { try { generalConfigLoading.value = true - // 如果已有连接,先断开 if (generalWebsocketId.value) { - disconnect(generalWebsocketId.value) + unsubscribe(generalWebsocketId.value) generalWebsocketId.value = null } - // 建立WebSocket连接进行通用配置 - const websocketId = await connect({ - taskId: userId, // 使用用户ID进行配置 - mode: '设置脚本', - showNotifications: true, - onStatusChange: status => { - console.log(`用户 ${formData.userName} 通用配置状态: ${status}`) - }, - onMessage: data => { - console.log(`用户 ${formData.userName} 通用配置消息:`, data) - // 这里可以根据需要处理特定的消息 - }, + const subId = userId + + subscribe(subId, { onError: error => { console.error(`用户 ${formData.userName} 通用配置错误:`, error) message.error(`通用配置连接失败: ${error}`) generalWebsocketId.value = null - }, + } }) - if (websocketId) { - generalWebsocketId.value = websocketId - message.success(`已开始配置用户 ${formData.userName} 的通用设置`) - } + generalWebsocketId.value = subId + message.success(`已开始配置用户 ${formData.userName} 的通用设置`) } catch (error) { console.error('通用配置失败:', error) message.error('通用配置失败') @@ -650,9 +619,8 @@ const selectScriptAfterTask = async () => { } const handleCancel = () => { - // 清理WebSocket连接 if (generalWebsocketId.value) { - disconnect(generalWebsocketId.value) + unsubscribe(generalWebsocketId.value) generalWebsocketId.value = null } router.push('/scripts') @@ -945,8 +913,8 @@ onMounted(() => { } .path-button:disabled { - background: var(--ant-color-bg-container-disabled); - color: var(--ant-color-text-disabled); + background: var(--ant-color-bg-container); + color: var(--ant-color-text-tertiary); cursor: not-allowed; } \ No newline at end of file diff --git a/frontend/src/views/MAAUserEdit.vue b/frontend/src/views/MAAUserEdit.vue index 7f42b60..713a463 100644 --- a/frontend/src/views/MAAUserEdit.vue +++ b/frontend/src/views/MAAUserEdit.vue @@ -898,12 +898,14 @@ import { useUserApi } from '@/composables/useUserApi' import { useScriptApi } from '@/composables/useScriptApi' import { useWebSocket } from '@/composables/useWebSocket' import { Service } from '@/api' +import { GetStageIn } from '@/api/models/GetStageIn' +import { defineComponent } from 'vue' const router = useRouter() const route = useRoute() const { addUser, updateUser, getUsers, loading: userLoading } = useUserApi() const { getScript } = useScriptApi() -const { connect, disconnect } = useWebSocket() +const { subscribe, unsubscribe } = useWebSocket() const formRef = ref() const loading = computed(() => userLoading.value) @@ -1129,19 +1131,17 @@ const loadUserData = async () => { const loadStageOptions = async () => { try { const response = await Service.getStageComboxApiInfoComboxStagePost({ - type: 'Today', + type: GetStageIn.type.TODAY }) if (response && response.code === 200 && response.data) { - const sorted = [...response.data].sort((a, b) => { + stageOptions.value = [...response.data].sort((a, b) => { if (a.value === '-') return -1 if (b.value === '-') return 1 return 0 }) - stageOptions.value = sorted } } catch (error) { console.error('加载关卡选项失败:', error) - // 保持默认选项 } } @@ -1157,14 +1157,21 @@ const loadStageModeOptions = async () => { } } +// 替换 VNodes 组件定义 +const VNodes = defineComponent({ + props: { vnodes: { type: Object, required: true } }, + setup(props) { + return () => props.vnodes as any + } +}) + // 选择基建配置文件 const selectInfrastructureConfig = async () => { try { - const path = await window.electronAPI?.selectFile([ + const path = await (window as any).electronAPI?.selectFile([ { name: 'JSON 文件', extensions: ['json'] }, { name: '所有文件', extensions: ['*'] }, ]) - if (path && path.length > 0) { infrastructureConfigPath.value = path formData.Info.InfrastPath = path[0] @@ -1182,28 +1189,22 @@ const importInfrastructureConfig = async () => { message.warning('请先选择配置文件') return } - if (!isEdit.value) { message.warning('请先保存用户后再导入配置') return } - try { infrastructureImporting.value = true - - // 调用API导入基建配置 const result = await Service.importInfrastructureApiScriptsUserInfrastructurePost({ scriptId: scriptId, userId: userId, jsonFile: infrastructureConfigPath.value[0], }) - if (result && result.code === 200) { message.success('基建配置导入成功') - // 清空文件路径 infrastructureConfigPath.value = '' } else { - message.error(result?.msg || '基建配置导入失败') + message.error('基建配置导入失败') } } catch (error) { console.error('基建配置导入失败:', error) @@ -1285,33 +1286,22 @@ const handleMAAConfig = async () => { // 如果已有连接,先断开 if (maaWebsocketId.value) { - disconnect(maaWebsocketId.value) + unsubscribe(maaWebsocketId.value) maaWebsocketId.value = null } - // 建立WebSocket连接进行MAA配置 - const websocketId = await connect({ - taskId: userId, // 使用用户ID进行配置 - mode: '设置脚本', - showNotifications: true, - onStatusChange: status => { - console.log(`用户 ${formData.userName} MAA配置状态: ${status}`) - }, - onMessage: data => { - console.log(`用户 ${formData.userName} MAA配置消息:`, data) - // 这里可以根据需要处理特定的消息 - }, + // 直接订阅(旧 connect 参数移除) + const subId = userId + subscribe(subId, { onError: error => { console.error(`用户 ${formData.userName} MAA配置错误:`, error) message.error(`MAA配置连接失败: ${error}`) maaWebsocketId.value = null - }, + } }) - if (websocketId) { - maaWebsocketId.value = websocketId - message.success(`已开始配置用户 ${formData.userName} 的MAA设置`) - } + maaWebsocketId.value = subId + message.success(`已开始配置用户 ${formData.userName} 的MAA设置`) } catch (error) { console.error('MAA配置失败:', error) message.error('MAA配置失败') @@ -1335,17 +1325,12 @@ const stage3InputRef = ref() const stageRemainInputRef = ref() // VNodes 组件,用于渲染下拉菜单内容 -const VNodes = { - props: { - vnodes: { - type: Object, - required: true, - }, - }, - render() { - return this.vnodes - }, -} +const VNodes = defineComponent({ + props: { vnodes: { type: Object, required: true } }, + setup(props) { + return () => props.vnodes as any + } +}) // 验证关卡名称格式 const validateStageName = (stageName: string): boolean => { @@ -1465,9 +1450,8 @@ const addCustomStageRemain = () => { } const handleCancel = () => { - // 清理WebSocket连接 if (maaWebsocketId.value) { - disconnect(maaWebsocketId.value) + unsubscribe(maaWebsocketId.value) maaWebsocketId.value = null } router.push('/scripts') diff --git a/frontend/src/views/Scripts.vue b/frontend/src/views/Scripts.vue index 867c025..717b5f5 100644 --- a/frontend/src/views/Scripts.vue +++ b/frontend/src/views/Scripts.vue @@ -236,9 +236,9 @@ import MarkdownIt from 'markdown-it' const router = useRouter() const { addScript, deleteScript, getScriptsWithUsers, loading } = useScriptApi() -const { addUser, updateUser, deleteUser, loading: userLoading } = useUserApi() +const { updateUser, deleteUser } = useUserApi() const { subscribe, unsubscribe } = useWebSocket() -const { getWebConfigTemplates, importScriptFromWeb, loading: templateApiLoading } = useTemplateApi() +const { getWebConfigTemplates, importScriptFromWeb } = useTemplateApi() // 初始化markdown解析器 const md = new MarkdownIt({ @@ -503,19 +503,11 @@ const handleMAAConfig = async (script: Script) => { return } - // 建立WebSocket订阅进行MAA配置 + // 新订阅 subscribe(script.id, { - onStatusChange: status => { - console.log(`脚本 ${script.name} 连接状态: ${status}`) - }, - onMessage: data => { - console.log(`脚本 ${script.name} 收到消息:`, data) - // 这里可以根据需要处理特定的消息 - }, onError: error => { console.error(`脚本 ${script.name} 连接错误:`, error) message.error(`MAA配置连接失败: ${error}`) - // 清理连接记录 activeConnections.value.delete(script.id) }, }) From 47e72918858415e220a7f28de14a91b16e0ff9b4 Mon Sep 17 00:00:00 2001 From: MoeSnowyFox Date: Thu, 11 Sep 2025 23:35:42 +0800 Subject: [PATCH 2/2] =?UTF-8?q?:wastebasket:=20=E4=BF=AE=E5=A4=8D=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/composables/useWebSocket.ts | 115 ++++++++++++++--------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/frontend/src/composables/useWebSocket.ts b/frontend/src/composables/useWebSocket.ts index 255f1db..97173fd 100644 --- a/frontend/src/composables/useWebSocket.ts +++ b/frontend/src/composables/useWebSocket.ts @@ -1,5 +1,5 @@ import { ref, type Ref } from 'vue' -import { message, notification, Modal } from 'ant-design-vue' +import { message, Modal, notification } from 'ant-design-vue' // 基础配置 const BASE_WS_URL = 'ws://localhost:36163/api/core/ws' @@ -87,7 +87,7 @@ const initGlobalStorage = (): GlobalWSStorage => { heartbeatTimer: undefined, isConnecting: false, lastPingTime: 0, - connectionId: Math.random().toString(36).substr(2, 9), + connectionId: Math.random().toString(36).substring(2, 9), moduleLoadCount: 0, createdAt: Date.now(), hasEverConnected: false, @@ -102,7 +102,7 @@ const initGlobalStorage = (): GlobalWSStorage => { lastConnectAttempt: 0, // 连接权限控制 allowNewConnection: true, // 初始化时允许创建连接 - connectionReason: '系统初始化' + connectionReason: '系统初始化', } } @@ -112,8 +112,7 @@ const getGlobalStorage = (): GlobalWSStorage => { ;(window as any)[WS_STORAGE_KEY] = initGlobalStorage() } - const storage = (window as any)[WS_STORAGE_KEY] as GlobalWSStorage - return storage + return (window as any)[WS_STORAGE_KEY] as GlobalWSStorage } // 设置全局状态 @@ -131,12 +130,12 @@ const setBackendStatus = (status: BackendStatus) => { // 检查后端是否运行(通过WebSocket连接状态判断) const checkBackendStatus = (): boolean => { const global = getGlobalStorage() - + // 如果WebSocket存在且状态为OPEN,说明后端运行正常 if (global.wsRef && global.wsRef.readyState === WebSocket.OPEN) { return true } - + // 如果WebSocket不存在或状态不是OPEN,说明后端可能有问题 return false } @@ -144,7 +143,7 @@ const checkBackendStatus = (): boolean => { // 重启后端 const restartBackend = async (): Promise => { const global = getGlobalStorage() - + if (global.isRestartingBackend) { return false } @@ -152,7 +151,7 @@ const restartBackend = async (): Promise => { try { global.isRestartingBackend = true global.backendRestartAttempts++ - + setBackendStatus('starting') // 调用 Electron API 重启后端 @@ -181,7 +180,7 @@ const restartBackend = async (): Promise => { // 后端监控和重启逻辑 const handleBackendFailure = async () => { const global = getGlobalStorage() - + if (global.backendRestartAttempts >= MAX_RESTART_ATTEMPTS) { // 弹窗提示用户重启整个应用 Modal.error({ @@ -190,11 +189,11 @@ const handleBackendFailure = async () => { okText: '重启应用', onOk: () => { if ((window.electronAPI as any)?.windowClose) { - (window.electronAPI as any).windowClose() + ;(window.electronAPI as any).windowClose() } else { window.location.reload() } - } + }, }) return } @@ -221,16 +220,16 @@ const handleBackendFailure = async () => { // 启动后端监控(仅基于WebSocket状态) const startBackendMonitoring = () => { const global = getGlobalStorage() - + if (global.backendCheckTimer) { clearInterval(global.backendCheckTimer) } - + global.backendCheckTimer = window.setInterval(() => { const isRunning = checkBackendStatus() const now = Date.now() global.lastBackendCheck = now - + // 基于 WebSocket 状态判断后端运行状态 if (isRunning) { // WebSocket连接正常 @@ -245,9 +244,9 @@ const startBackendMonitoring = () => { setBackendStatus('stopped') } } - + // 仅在必要时检查心跳超时 - if (global.lastPingTime > 0 && (now - global.lastPingTime) > HEARTBEAT_TIMEOUT * 2) { + if (global.lastPingTime > 0 && now - global.lastPingTime > HEARTBEAT_TIMEOUT * 2) { if (global.wsRef && global.wsRef.readyState === WebSocket.OPEN) { setBackendStatus('error') } @@ -274,9 +273,18 @@ const startGlobalHeartbeat = (ws: WebSocket) => { try { const pingTime = Date.now() global.lastPingTime = pingTime - ws.send(JSON.stringify({ type: 'Signal', data: { Ping: pingTime, connectionId: global.connectionId } })) - setTimeout(() => { /* 心跳超时不主动断开 */ }, HEARTBEAT_TIMEOUT) - } catch { /* ignore */ } + ws.send( + JSON.stringify({ + type: 'Signal', + data: { Ping: pingTime, connectionId: global.connectionId }, + }) + ) + setTimeout(() => { + /* 心跳超时不主动断开 */ + }, HEARTBEAT_TIMEOUT) + } catch { + /* ignore */ + } } }, HEARTBEAT_INTERVAL) } @@ -299,7 +307,12 @@ const handleMessage = (raw: WebSocketBaseMessage) => { const ws = global.wsRef if (ws && ws.readyState === WebSocket.OPEN) { try { - ws.send(JSON.stringify({ type: 'Signal', data: { Pong: raw.data.Ping, connectionId: global.connectionId } })) + ws.send( + JSON.stringify({ + type: 'Signal', + data: { Pong: raw.data.Ping, connectionId: global.connectionId }, + }) + ) } catch (e) { // Pong发送失败,静默处理 } @@ -325,7 +338,7 @@ const handleMessage = (raw: WebSocketBaseMessage) => { if (raw.data && (raw.data as NotifyMessage).title) { notification.info({ message: (raw.data as NotifyMessage).title, - description: (raw.data as NotifyMessage).content + description: (raw.data as NotifyMessage).content, }) } return @@ -347,7 +360,7 @@ const handleMessage = (raw: WebSocketBaseMessage) => { // 后端启动后建立连接的公开函数 export const connectAfterBackendStart = async (): Promise => { setConnectionPermission(true, '后端启动后连接') - + try { const connected = await connectGlobalWebSocket('后端启动后连接') if (connected) { @@ -385,7 +398,7 @@ const createGlobalWebSocket = (): WebSocket => { global.hasEverConnected = true global.reconnectAttempts = 0 setGlobalStatus('已连接') - + startGlobalHeartbeat(ws) // 连接成功后禁止新连接 @@ -393,12 +406,24 @@ const createGlobalWebSocket = (): WebSocket => { // 发送连接确认和初始pong try { - ws.send(JSON.stringify({ type: 'Signal', data: { Connect: true, connectionId: global.connectionId } })) - ws.send(JSON.stringify({ type: 'Signal', data: { Pong: Date.now(), connectionId: global.connectionId } })) - } catch { /* ignore */ } + ws.send( + JSON.stringify({ + type: 'Signal', + data: { Connect: true, connectionId: global.connectionId }, + }) + ) + ws.send( + JSON.stringify({ + type: 'Signal', + data: { Pong: Date.now(), connectionId: global.connectionId }, + }) + ) + } catch { + /* ignore */ + } } - ws.onmessage = (ev) => { + ws.onmessage = ev => { try { const raw = JSON.parse(ev.data) as WebSocketBaseMessage handleMessage(raw) @@ -411,14 +436,17 @@ const createGlobalWebSocket = (): WebSocket => { setGlobalStatus('连接错误') } - ws.onclose = (event) => { + ws.onclose = event => { setGlobalStatus('已断开') stopGlobalHeartbeat() global.isConnecting = false // 检查是否是后端自杀导致的关闭 if (event.code === 1000 && event.reason === 'Ping超时') { - handleBackendFailure() + handleBackendFailure().catch(error => { + // 忽略错误,或者可以添加适当的错误处理 + console.warn('handleBackendFailure error:', error) + }) } else { // 连接断开,不自动重连,等待后端重启 setGlobalStatus('已断开') @@ -478,18 +506,18 @@ const connectGlobalWebSocket = async (reason: string = '未指定原因'): Promi // 额外保护:检查最近连接尝试时间,避免过于频繁的连接 const now = Date.now() const MIN_CONNECT_INTERVAL = 2000 // 最小连接间隔2秒 - if (global.lastConnectAttempt && (now - global.lastConnectAttempt) < MIN_CONNECT_INTERVAL) { + if (global.lastConnectAttempt && now - global.lastConnectAttempt < MIN_CONNECT_INTERVAL) { return false } global.isConnecting = true global.lastConnectAttempt = now - + // 清理旧连接引用(如果存在且已关闭) if (global.wsRef && global.wsRef.readyState === WebSocket.CLOSED) { global.wsRef = null } - + global.wsRef = createGlobalWebSocket() setGlobalStatus('连接中') return true @@ -512,16 +540,14 @@ const setConnectionPermission = (allow: boolean, reason: string) => { const checkConnectionPermission = (): boolean => { const global = getGlobalStorage() - return !!global.allowNewConnection + return global.allowNewConnection } // 只在后端启动/重启时允许创建连接 -const allowedConnectionReasons = [ - '后端启动后连接', - '后端重启后重连' -] +const allowedConnectionReasons = ['后端启动后连接', '后端重启后重连'] -const isValidConnectionReason = (reason: string): boolean => allowedConnectionReasons.includes(reason) +const isValidConnectionReason = (reason: string): boolean => + allowedConnectionReasons.includes(reason) // 全局连接锁 - 防止多个模块实例同时连接 let isGlobalConnectingLock = false @@ -587,7 +613,7 @@ export function useWebSocket() { hasHeartbeat: !!global.heartbeatTimer, hasEverConnected: global.hasEverConnected, reconnectAttempts: global.reconnectAttempts, - isPersistentMode: true // 标识为永久连接模式 + isPersistentMode: true, // 标识为永久连接模式 }) const restartBackendManually = async () => { @@ -598,7 +624,12 @@ export function useWebSocket() { const getBackendStatus = () => { const global = getGlobalStorage() - return { status: global.backendStatus.value, restartAttempts: global.backendRestartAttempts, isRestarting: global.isRestartingBackend, lastCheck: global.lastBackendCheck } + return { + status: global.backendStatus.value, + restartAttempts: global.backendRestartAttempts, + isRestarting: global.isRestartingBackend, + lastCheck: global.lastBackendCheck, + } } return { @@ -610,6 +641,6 @@ export function useWebSocket() { subscribers: global.subscribers, backendStatus: global.backendStatus, restartBackend: restartBackendManually, - getBackendStatus + getBackendStatus, } }