Browse Source

refactor: 简化WebSocketProvider的消息处理和连接逻辑

重构了CustomWebsocketProvider.ts中的onmessage处理,移除了多层嵌套的try-catch错误捕获,简化了Blob数据处理流程。同时删除了不再需要的requestSync和forceReconnect方法。

在index.vue中,移除了全局错误监控、页面可见性变化处理、bfcache恢复处理等冗余代码,优化了幻灯片切换消息的处理逻辑,只在跟随模式下处理slideIndex消息。还删除了连接超时提醒、重复错误过滤等冗余逻辑,简化了WebSocket连接检查定时器的实现。
lsc 5 days ago
parent
commit
52cdded3d9
2 changed files with 60 additions and 267 deletions
  1. 7 60
      src/views/Student/CustomWebsocketProvider.ts
  2. 53 207
      src/views/Student/index.vue

+ 7 - 60
src/views/Student/CustomWebsocketProvider.ts

@@ -206,29 +206,14 @@ export class YjsRedisProvider extends Observable<string> {
     }
     }
 
 
     ws.onmessage = (event: MessageEvent) => {
     ws.onmessage = (event: MessageEvent) => {
-      // ★ 前后全程错误捕获:任何消息处理异常都抛给上层(index.vue 监听后 message.warning)
-      try {
-        if (event.data instanceof ArrayBuffer) {
-          this._readMessage(new Uint8Array(event.data))
-        }
-        else if (event.data instanceof Blob) {
-          event.data.arrayBuffer()
-            .then(buf => {
-              try {
-                this._readMessage(new Uint8Array(buf))
-              }
-              catch (err) {
-                this.emit('ws-error', [{ stage: 'onmessage-blob', error: err }])
-              }
-            })
-            .catch(err => this.emit('ws-error', [{ stage: 'onmessage-blob-read', error: err }]))
-        }
-        else if (typeof event.data === 'string') {
-          this._readMessage(new TextEncoder().encode(event.data))
-        }
+      if (event.data instanceof ArrayBuffer) {
+        this._readMessage(new Uint8Array(event.data))
       }
       }
-      catch (err) {
-        this.emit('ws-error', [{ stage: 'onmessage', error: err }])
+      else if (event.data instanceof Blob) {
+        event.data.arrayBuffer().then(buf => this._readMessage(new Uint8Array(buf)))
+      }
+      else if (typeof event.data === 'string') {
+        this._readMessage(new TextEncoder().encode(event.data))
       }
       }
     }
     }
 
 
@@ -312,44 +297,6 @@ export class YjsRedisProvider extends Observable<string> {
     if (this.ws?.readyState === WebSocket.OPEN) {
     if (this.ws?.readyState === WebSocket.OPEN) {
       this.ws.send(data)
       this.ws.send(data)
     }
     }
-  }
-
-  /** 主动请求一次全量同步(iPad Safari 从后台恢复后补回错过的状态) */
-  public requestSync(): void {
-    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
-      const encoder = encoding.createEncoder()
-      encoding.writeVarUint(encoder, messageSync)
-      syncProtocol.writeSyncStep1(encoder, this.doc)
-      this.ws.send(encoding.toUint8Array(encoder))
-    }
-  }
-
-  /**
-   * 强制重建连接
-   * iPad Safari 回到前台时,socket 可能处于"假 OPEN"状态(readyState 正常但收不到任何数据),
-   * 仅靠 onclose 不会触发重连,必须主动关闭后重建
-   */
-  public forceReconnect(): void {
-    this.synced = false
-    this.wsconnected = false
-    this.wsconnecting = false
-    if (this._resyncIntervalId) {
-      clearInterval(this._resyncIntervalId)
-      this._resyncIntervalId = null
-    }
-    const oldWs = this.ws
-    this.ws = null
-    if (oldWs) {
-      // 必须先解绑旧连接的处理器:否则旧连接异步触发 onclose 时会把 this.ws 置为 null,
-      // 覆盖掉即将建立的新连接
-      oldWs.onopen = null
-      oldWs.onmessage = null
-      oldWs.onclose = null
-      oldWs.onerror = null
-      try {
-        oldWs.close() 
-      }
-      catch (e) { /* ignore */ }
     }
     }
     this.shouldConnect = true
     this.shouldConnect = true
     this.connect()
     this.connect()

+ 53 - 207
src/views/Student/index.vue

@@ -723,23 +723,6 @@ const authToken = ref<string | null>(null)
 const authTokenUpdateTimer = ref<NodeJS.Timeout | null>(null)
 const authTokenUpdateTimer = ref<NodeJS.Timeout | null>(null)
 const heartbeatTimer = ref<NodeJS.Timeout | null>(null)
 const heartbeatTimer = ref<NodeJS.Timeout | null>(null)
 const socketCheckTimer = ref<NodeJS.Timeout | null>(null)
 const socketCheckTimer = ref<NodeJS.Timeout | null>(null)
-// 连接超时提醒定时器 / 是否已提示过连接异常(避免重复弹窗)
-const connectionWarnTimer = ref<NodeJS.Timeout | null>(null)
-const socketWarned = ref(false)
-
-// 全局报错提示去重:同一条错误只弹一次,避免刷屏(iPad 上无法方便看控制台,直接弹窗暴露问题)
-const warnedErrorMessages = new Set<string>()
-const warnOnce = (msg: string) => {
-  if (!msg || warnedErrorMessages.has(msg)) return
-  warnedErrorMessages.add(msg)
-  message.warning(msg)
-}
-// 常见无害错误过滤(Safari 高频且不影响功能)
-const isHarmlessError = (msg: string) => {
-  return msg.includes('ResizeObserver loop')
-    || msg.includes('Script error')
-    || msg.includes('Load failed') // Safari fetch 失败的泛化提示,业务请求已有自己的错误提示
-}
 
 
 // 同步数据最大保留时间(40分钟)
 // 同步数据最大保留时间(40分钟)
 const SYNC_DATA_MAX_AGE = 40 * 60 * 1000 // 40分钟 = 40 * 60 * 1000毫秒
 const SYNC_DATA_MAX_AGE = 40 * 60 * 1000 // 40分钟 = 40 * 60 * 1000毫秒
@@ -1157,10 +1140,6 @@ const goToSlide = (index: number) => {
   }
   }
   else {
   else {
     console.warn('goToSlide: 无效的索引:', index)
     console.warn('goToSlide: 无效的索引:', index)
-    // 跟随模式下老师页数与本地页数不一致时,明确提示而不是静默失败
-    if (props.type == '2' && isFollowModeActive.value) {
-      message.warning(`未找到第 ${Number(index) + 1} 页,请刷新页面重新同步`)
-    }
   }
   }
 }
 }
 
 
@@ -2889,7 +2868,7 @@ const getCourseDetail = async () => {
   try {
   try {
     const res = await api.getCourseDetail(props.courseid as string)
     const res = await api.getCourseDetail(props.courseid as string)
     const opRes1 = await api.selectPPTOp({
     const opRes1 = await api.selectPPTOp({
-      id: props.courseid, //  + '-' + props.cid
+      id: props.courseid + '-' + props.cid,
       type: '1',
       type: '1',
       index: 0
       index: 0
     })
     })
@@ -3745,20 +3724,8 @@ const getMessages = (msgObj: any) => {
   console.log('message', msgObj)
   console.log('message', msgObj)
   
   
   // 处理幻灯片切换消息
   // 处理幻灯片切换消息
-  if (props.type == '2' && msgObj.type === 'slideIndex') {
-    if (isFollowModeActive.value) {
-      goToSlide(msgObj.slideIndex)
-    }
-    else {
-      // sopen → selectCourseSLook 是异步请求,重放时 slideIndex 可能先到,
-      // 此刻跟随模式还没打开,延迟补一次,避免刷新后第一页不跟随
-      const targetIndex = msgObj.slideIndex
-      setTimeout(() => {
-        if (props.type == '2' && isFollowModeActive.value) {
-          goToSlide(targetIndex)
-        }
-      }, 1000)
-    }
+  if (props.type == '2' && msgObj.type === 'slideIndex' && isFollowModeActive.value) {
+    goToSlide(msgObj.slideIndex)
   }
   }
   
   
   // 处理跟随模式状态变化
   // 处理跟随模式状态变化
@@ -4257,60 +4224,15 @@ onMounted(() => {
   
   
   // visibilitychange 事件(适用于 iframe 嵌套场景,当外层页面返回时触发)
   // visibilitychange 事件(适用于 iframe 嵌套场景,当外层页面返回时触发)
   const handleVisibilityChange = () => {
   const handleVisibilityChange = () => {
-    const provider = providerSocket.value
-    if (document.hidden) {
-      if (isCreator.value) {
-        // 页面被隐藏时,清空所有同步状态
-        clearAllSyncStates()
-        if (timerIndicator.value.visible) {
-          sendMessage({ type: 'timer_stop', courseid: props.courseid, isCreator: isCreator.value ? 'T' : 'F' })
-        }
-      }
-      return
-    }
-
-    // ★ iPad Safari 从后台切回前台:socket 常处于"假 OPEN"(readyState 正常但收不到数据)
-    //   或不满足权限导致挂起,这里主动补一次同步,必要时强制重建连接
-    if (provider) {
-      if (provider.ws && provider.ws.readyState === WebSocket.OPEN) {
-        console.log('🔁 页面恢复前台,主动请求同步')
-        message.warning('🔁 页面恢复前台,主动请求同步')
-        provider.requestSync()
-      }
-      else {
-        console.log('🔁 页面恢复前台,socket 已断开,强制重连')
-        message.warning('🔁 页面恢复前台,socket 已断开,强制重连')
-        provider.forceReconnect()
+    if (document.hidden && isCreator.value) {
+      // 页面被隐藏时,清空所有同步状态
+      clearAllSyncStates()
+      if (timerIndicator.value.visible && isCreator.value) {
+        sendMessage({ type: 'timer_stop', courseid: props.courseid, isCreator: isCreator.value ? 'T' : 'F' })
       }
       }
     }
     }
   }
   }
   document.addEventListener('visibilitychange', handleVisibilityChange)
   document.addEventListener('visibilitychange', handleVisibilityChange)
-
-  // bfcache 恢复(iPad Safari 后退/切换应用后从缓存恢复页面时触发)
-  const handlePageShow = (event: PageTransitionEvent) => {
-    if (!event.persisted) return
-    console.log('🔁 bfcache 恢复,强制重建 WebSocket')
-    providerSocket.value?.forceReconnect()
-  }
-  window.addEventListener('pageshow', handlePageShow)
-
-  // ★ 全局错误监控:页面内所有脚本错误 / 未捕获的 Promise 异常都弹窗提示
-  //   (iPad Safari 上看不到控制台,报错只能以这种方式暴露)
-  const handleGlobalError = (event: ErrorEvent) => {
-    const msg = event.message || '未知脚本错误'
-    if (isHarmlessError(msg)) return
-    console.error('🌐 全局错误:', event.message, event.filename, event.lineno)
-    warnOnce(`页面报错: ${msg}(第${event.lineno || '?'}行)`)
-  }
-  const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
-    const reason: any = event.reason
-    const msg = reason?.message || String(reason) || '未处理的 Promise 异常'
-    if (isHarmlessError(msg)) return
-    console.error('🌐 未捕获的 Promise 异常:', reason)
-    warnOnce(`异步报错: ${msg}`)
-  }
-  window.addEventListener('error', handleGlobalError)
-  window.addEventListener('unhandledrejection', handleUnhandledRejection)
   
   
   // pagehide 事件(作为补充,某些浏览器中比 beforeunload 更可靠)
   // pagehide 事件(作为补充,某些浏览器中比 beforeunload 更可靠)
   window.addEventListener('pagehide', () => {
   window.addEventListener('pagehide', () => {
@@ -4322,9 +4244,6 @@ onMounted(() => {
   ;(window as any).__pptistStudentUnloadHandlers = {
   ;(window as any).__pptistStudentUnloadHandlers = {
     // beforeunload: handlePageUnload,
     // beforeunload: handlePageUnload,
     visibilitychange: handleVisibilityChange,
     visibilitychange: handleVisibilityChange,
-    pageshow: handlePageShow,
-    globalError: handleGlobalError,
-    unhandledrejection: handleUnhandledRejection,
     pagehide: handlePageUnload
     pagehide: handlePageUnload
   }
   }
 })
 })
@@ -4365,12 +4284,6 @@ onUnmounted(() => {
     socketCheckTimer.value = null
     socketCheckTimer.value = null
   }
   }
 
 
-  // 清理连接超时提醒定时器
-  if (connectionWarnTimer.value) {
-    clearTimeout(connectionWarnTimer.value)
-    connectionWarnTimer.value = null
-  }
-
   // if (providerSocket.value) {
   // if (providerSocket.value) {
   //   providerSocket.value.destroy()
   //   providerSocket.value.destroy()
   //   providerSocket.value = null
   //   providerSocket.value = null
@@ -4387,9 +4300,6 @@ onUnmounted(() => {
     const handlers = (window as any).__pptistStudentUnloadHandlers
     const handlers = (window as any).__pptistStudentUnloadHandlers
     // window.removeEventListener('beforeunload', handlers.beforeunload)
     // window.removeEventListener('beforeunload', handlers.beforeunload)
     document.removeEventListener('visibilitychange', handlers.visibilitychange)
     document.removeEventListener('visibilitychange', handlers.visibilitychange)
-    window.removeEventListener('pageshow', handlers.pageshow)
-    window.removeEventListener('error', handlers.globalError)
-    window.removeEventListener('unhandledrejection', handlers.unhandledrejection)
     window.removeEventListener('pagehide', handlers.pagehide)
     window.removeEventListener('pagehide', handlers.pagehide)
     delete (window as any).__pptistStudentUnloadHandlers
     delete (window as any).__pptistStudentUnloadHandlers
   }
   }
@@ -4608,25 +4518,6 @@ const createWebSocketConnection = async (type = 1) => {
     authTokenUpdateTimer.value = setTimeout(updateAuthToken, 30 * 60 * 1000) as unknown as NodeJS.Timeout
     authTokenUpdateTimer.value = setTimeout(updateAuthToken, 30 * 60 * 1000) as unknown as NodeJS.Timeout
     // authTokenUpdateTimer.value = setTimeout(updateAuthToken, 10 * 1000) as unknown as NodeJS.Timeout
     // authTokenUpdateTimer.value = setTimeout(updateAuthToken, 10 * 1000) as unknown as NodeJS.Timeout
 
 
-    // ★ iPad Safari 兼容:首次建连可能被系统推迟或静默挂起,超时后主动提示并重建
-    if (connectionWarnTimer.value) clearTimeout(connectionWarnTimer.value)
-    connectionWarnTimer.value = setTimeout(() => {
-      if (connectionStatus.value !== 'connected') {
-        console.warn('👉 WebSocket 建连超时,强制重连')
-        message.warning('实时连接超时,正在自动重试…')
-        providerSocket.value?.forceReconnect()
-      }
-    }, 12000) as unknown as NodeJS.Timeout
-
-    // ★ 同步超时提醒:连接成功但服务器状态迟迟未同步,学生端会表现为"不跟随"
-    setTimeout(() => {
-      const p = providerSocket.value
-      if (p && p.ws && p.ws.readyState === WebSocket.OPEN && !p.synced) {
-        console.warn('👉 已连接但未收到同步状态,主动请求同步')
-        p.requestSync()
-      }
-    }, 15000)
-
     providerSocket.value.on('status', (event: any) => {
     providerSocket.value.on('status', (event: any) => {
       console.log('👉 WebSocket状态:', event.status)
       console.log('👉 WebSocket状态:', event.status)
       
       
@@ -4635,11 +4526,6 @@ const createWebSocketConnection = async (type = 1) => {
         connectionStatus.value = 'connected'
         connectionStatus.value = 'connected'
         isConnecting.value = false
         isConnecting.value = false
         reconnectAttempts.value = 0 // 重置重连次数
         reconnectAttempts.value = 0 // 重置重连次数
-        socketWarned.value = false
-        if (connectionWarnTimer.value) {
-          clearTimeout(connectionWarnTimer.value)
-          connectionWarnTimer.value = null
-        }
         
         
         // 清理重连定时器
         // 清理重连定时器
         if (reconnectTimer.value) {
         if (reconnectTimer.value) {
@@ -4720,79 +4606,53 @@ const createWebSocketConnection = async (type = 1) => {
       console.error('👉 WebSocket 连接错误(将自动重连):', error)
       console.error('👉 WebSocket 连接错误(将自动重连):', error)
     })
     })
 
 
-    // ★ onmessage 处理过程中抛出的异常(provider 内部捕获后转发)
-    providerSocket.value.on('ws-error', (info: any) => {
-      const errMsg = info?.error?.message || String(info?.error) || '未知错误'
-      console.error(`👉 WS onmessage 处理出错(${info?.stage}):`, info?.error)
-      warnOnce(`消息接收出错(${info?.stage}): ${errMsg}`)
-    })
-
     providerSocket.value.on('connection-close', (event: any) => {
     providerSocket.value.on('connection-close', (event: any) => {
       console.log('👉 WebSocket 连接关闭(将自动重连):', event)
       console.log('👉 WebSocket 连接关闭(将自动重连):', event)
     })
     })
     
     
     // ★ 所有消息处理走 raw-message,不再依赖 observe(observe 因服务器累积 DELETE 标记不触发)
     // ★ 所有消息处理走 raw-message,不再依赖 observe(observe 因服务器累积 DELETE 标记不触发)
     providerSocket.value.on('raw-message', (data: Uint8Array) => {
     providerSocket.value.on('raw-message', (data: Uint8Array) => {
-      // ★ 前后全程错误捕获:解析/业务处理任何一步异常都弹窗提示(iPad 上看不到控制台)
+      const parsed = parseYjsMessage(data)
+      if (!parsed || parsed.objs.length === 0) return
+      const { subType, objs } = parsed
+      // subType===1 是 syncStep2(服务器历史全量重放),其中的 timer_* 是旧消息,不能执行也不能打日志
+      const isReplay = subType === 1
+      const timerTypes = ['timer_start', 'timer_pause', 'timer_reset', 'timer_stop', 'timer_finish', 'timer_update']
+      const writingBoardTypes = ['writing_board_update', 'writing_board_close', 'writing_board_blackboard']
+      // 按类型去重:同一类消息只保留最后一条(latest 与 slideIndex 是两条独立通道,
+      // 一次 syncStep2 重放会同时带上它们,不能只留全局最后一条,否则 sopen 被丢弃,
+      // 学生 isFollowModeActive 一直为 false → 之后所有 slideIndex 都被忽略,表现为"不跟随")
+      const lastByType = new Map<string, Record<string, any>>()
+      for (const obj of validObjs) {
+        const key = obj.type === 'slideIndex' ? 'slideIndex' : (obj.type || 'other')
+        lastByType.set(key, obj)
+      }
+      // slideIndex 必须最后执行(依赖 sopen 先打开跟随模式)
+      const orderedObjs = [...lastByType.entries()]
+        .sort(([a], [b]) => (a === 'slideIndex' ? 1 : 0) - (b === 'slideIndex' ? 1 : 0))
+        .map(([, v]) => v)
+      for (const obj of orderedObjs) {
+        console.log('📨 解析业务消息:', obj)
+        getMessages(obj)
+      }
+
+      // ★ 消息处理后检查特殊消息数组长度,超出则清理(替代 observe 里的清理逻辑)
       try {
       try {
-        const parsed = parseYjsMessage(data)
-        if (!parsed || parsed.objs.length === 0) return
-        const { subType, objs } = parsed
-        // subType===1 是 syncStep2(服务器历史全量重放),其中的 timer_* 是旧消息,不能执行也不能打日志
-        const isReplay = subType === 1
-        // 过滤出有效业务消息:不带业务字段的、自己发的、历史重放的 timer_* 全部剔除
-        const validObjs = objs.filter((obj: Record<string, any>) => {
-          if (!obj.type && obj.slideIndex === undefined) return false
-          if (obj.mId && obj.mId === mId.value) return false
-          if (isReplay && typeof obj.type === 'string' && obj.type.startsWith('timer_')) return false
-          return true
-        })
-        // 按类型去重:同一类消息只保留最后一条(latest 与 slideIndex 是两条独立通道,
-        // 一次 syncStep2 重放会同时带上它们,不能只留全局最后一条,否则 sopen 被丢弃,
-        // 学生 isFollowModeActive 一直为 false → 之后所有 slideIndex 都被忽略,表现为"不跟随")
-        const lastByType = new Map<string, Record<string, any>>()
-        for (const obj of validObjs) {
-          const key = obj.type === 'slideIndex' ? 'slideIndex' : (obj.type || 'other')
-          lastByType.set(key, obj)
+        if (yTimerMessages.value && yTimerMessages.value.length > 500 && isCreator.value) {
+          smartCleanupMessages(yTimerMessages.value, 500, 'timer', docSocket.value!)
         }
         }
-        // slideIndex 必须最后执行(依赖 sopen 先打开跟随模式)
-        const orderedObjs = [...lastByType.entries()]
-          .sort(([a], [b]) => (a === 'slideIndex' ? 1 : 0) - (b === 'slideIndex' ? 1 : 0))
-          .map(([, v]) => v)
-        for (const obj of orderedObjs) {
-          console.log('📨 解析业务消息:', obj)
-          try {
-            getMessages(obj)
-          }
-          catch (msgErr: any) {
-            // 单条消息处理失败不影响后续消息,但要提示出来
-            console.error('📨 消息处理出错:', msgErr, obj)
-            warnOnce(`消息处理出错(${obj?.type || 'unknown'}): ${msgErr?.message || msgErr}`)
-          }
+        if (yLaserMessages.value && yLaserMessages.value.length > 500 && isCreator.value) {
+          smartCleanupMessages(yLaserMessages.value, 500, 'laser', docSocket.value!)
         }
         }
-
-        // ★ 消息处理后检查特殊消息数组长度,超出则清理(替代 observe 里的清理逻辑)
-        try {
-          if (yTimerMessages.value && yTimerMessages.value.length > 500 && isCreator.value) {
-            smartCleanupMessages(yTimerMessages.value, 500, 'timer', docSocket.value!)
-          }
-          if (yLaserMessages.value && yLaserMessages.value.length > 500 && isCreator.value) {
-            smartCleanupMessages(yLaserMessages.value, 500, 'laser', docSocket.value!)
-          }
-          if (yWritingBoardMessages.value && yWritingBoardMessages.value.length > 500 && isCreator.value) {
-            smartCleanupMessages(yWritingBoardMessages.value, 500, 'writingBoard', docSocket.value!)
-          }
-          if (yIsResultArrayMessages.value && yIsResultArrayMessages.value.length > 1 && isCreator.value) {
-            smartCleanupMessages(yIsResultArrayMessages.value, 1, 'isResultArray', docSocket.value!)
-          }
+        if (yWritingBoardMessages.value && yWritingBoardMessages.value.length > 500 && isCreator.value) {
+          smartCleanupMessages(yWritingBoardMessages.value, 500, 'writingBoard', docSocket.value!)
         }
         }
-        catch (cleanupErr) {
-          // 清理失败不影响消息处理
+        if (yIsResultArrayMessages.value && yIsResultArrayMessages.value.length > 1 && isCreator.value) {
+          smartCleanupMessages(yIsResultArrayMessages.value, 1, 'isResultArray', docSocket.value!)
         }
         }
       }
       }
-      catch (err: any) {
-        console.error('📨 raw-message 处理失败:', err)
-        warnOnce(`消息解析出错: ${err?.message || err}`)
+      catch (cleanupErr) {
+        // 清理失败不影响消息处理
       }
       }
     })
     })
 
 
@@ -4830,7 +4690,7 @@ const createWebSocketConnection = async (type = 1) => {
   }
   }
 
 
   // 启动 socket 连接检查定时器
   // 启动 socket 连接检查定时器
-  startSocketCheckTimer()
+  // startSocketCheckTimer()
 }
 }
 
 
 // ✅ 提取业务初始化逻辑
 // ✅ 提取业务初始化逻辑
@@ -4910,31 +4770,17 @@ const startSocketCheckTimer = () => {
     socketCheckTimer.value = null
     socketCheckTimer.value = null
   }
   }
   
   
-  // 每10秒检查一次连接状态
-  // ★ iPad Safari 的 socket 可能"假 OPEN"(readyState=1 但收不到任何数据),
-  //   仅看 readyState 不够,还要看同步状态 synced
+  // 每10秒检查一次 socket 连接状态
   socketCheckTimer.value = setInterval(() => {
   socketCheckTimer.value = setInterval(() => {
-    const provider = providerSocket.value
-    if (!provider || !docSocket.value) return
-
-    const readyState = provider.ws ? provider.ws.readyState : WebSocket.CLOSED
-    console.log('🔍 定时器检查 socket 连接状态:', readyState, 'synced:', provider.synced)
-
-    if (readyState !== WebSocket.OPEN) {
-      console.log('🔍 定时器检查发现 socket 未连接,执行重连')
-      provider.forceReconnect()
-      // 首次异常时提示,避免反复弹窗
-      if (!socketWarned.value) {
-        socketWarned.value = true
-        message.warning('实时连接已断开,正在自动重连…')
+    if (providerSocket.value) {
+      // 直接检查 providerSocket 的连接状态
+      // WebsocketProvider 有一个 connected 属性来表示连接状态
+      const isConnected = (providerSocket.value as any).ws.readyState
+      console.log('🔍 定时器检查 socket 连接状态:', isConnected)
+      if (isConnected !== 1) {
+        console.log('🔍 定时器检查发现 socket 未连接,执行重连')
+        createWebSocketConnection(2)
       }
       }
-      return
-    }
-
-    if (!provider.synced) {
-      console.log('🔍 定时器检查发现未完成同步,主动请求同步')
-      message.warning('🔍定时器检查发现未完成同步,主动请求同步')
-      provider.requestSync()
     }
     }
   }, 10000) as unknown as NodeJS.Timeout
   }, 10000) as unknown as NodeJS.Timeout
 }
 }