From 30264a7c67d44989eb9f1de1f01f0081bd298e6c Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期日, 30 八月 2026 21:48:44 +0800
Subject: [PATCH] 修复实时流客户端断连异常

---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java |   23 ++++++++++++++++++++---
 1 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
index 1765131..0671ed2 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
@@ -98,12 +98,23 @@
 
     @Override public SseEmitter stream(Long testId)
     {
-        SseEmitter emitter = new SseEmitter(0L);
+        SseEmitter emitter = createEmitter();
         try { sendAll(emitter, testId); }
-        catch (RuntimeException | IOException exception) { emitter.completeWithError(exception); return emitter; }
+        catch (IOException exception)
+        {
+            // 客户端刷新或离开页面会断开连接,不能再交给全局异常处理器写 SSE JSON。
+            emitter.complete();
+            return emitter;
+        }
+        catch (RuntimeException exception) { emitter.completeWithError(exception); return emitter; }
         ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
             try { sendAll(emitter, testId); }
-            catch (RuntimeException | IOException exception) { emitter.completeWithError(exception); }
+            catch (IOException exception)
+            {
+                // 写出失败表示 SSE 客户端已断开,静默结束连接。
+                emitter.complete();
+            }
+            catch (RuntimeException exception) { emitter.completeWithError(exception); }
         }, 3, 3, TimeUnit.SECONDS);
         emitter.onCompletion(() -> future.cancel(false));
         emitter.onTimeout(() -> future.cancel(false));
@@ -111,6 +122,12 @@
         return emitter;
     }
 
+    /** 创建实时流发射器,供断连生命周期测试替换。 */
+    protected SseEmitter createEmitter()
+    {
+        return new SseEmitter(0L);
+    }
+
     private void sendAll(SseEmitter emitter, Long testId) throws IOException
     {
         LiveState state = current(testId);

--
Gitblit v1.7.1