From 406f9b77b4bb326951a949bcab2efaa5f65b458f Mon Sep 17 00:00:00 2001 From: Peanut Date: Tue, 21 Jul 2026 23:28:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20SSE=20=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BD=BF=E7=94=A8=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E8=A1=8C=E8=AF=BB=E5=8F=96=E5=92=8C=E6=98=BE=E5=BC=8F?= =?UTF-8?q?=20flush?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/ShortNovelServiceImpl.java | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java b/server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java index bf07488..136647c 100644 --- a/server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java +++ b/server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import java.io.EOFException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -100,7 +101,7 @@ public class ShortNovelServiceImpl implements ShortNovelService { upstreamBody.put("action", request.getAction()); upstreamBody.put("payload", request.getPayload()); - return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId, null); + return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId, request.getOriginalQuery()); } /** @@ -139,12 +140,18 @@ public class ShortNovelServiceImpl implements ShortNovelService { // source() 返回的 BufferedSource 逐行从网络 socket 读取,不会缓冲整个响应 BufferedSource source = responseBody.source(); + log.info("[ShortNovel SSE] 开始读取上游响应: path={}, userId={}", path, currentUserId); StringBuilder dataBuffer = new StringBuilder(); while (!source.exhausted()) { - String line = source.readUtf8Line(); - if (line == null) break; - + String line; + try { + line = source.readUtf8LineStrict(); + } catch (EOFException e) { + break; + } + log.debug("[ShortNovel SSE] 读取到行: length={}, timestamp={}", + line.length(), System.currentTimeMillis()); if (line.startsWith("data:")) { dataBuffer.append(line.substring(5).trim()); } else if (line.isEmpty() && dataBuffer.length() > 0) { @@ -158,17 +165,23 @@ public class ShortNovelServiceImpl implements ShortNovelService { try { JSONObject event = JSON.parseObject(dataStr); String type = event.getString("type"); + long eventTimestamp = System.currentTimeMillis(); + log.info("[ShortNovel SSE] 处理事件: type={}, timestamp={}", type, eventTimestamp); // 拦截 novel_done 事件,保存到数据库 if ("novel_done".equals(type)) { JSONObject payload = event.getJSONObject("payload"); - if (payload != null && originalQuery != null) { + log.info("[ShortNovel SSE] novel_done 事件: originalQuery={}, payload={}", + originalQuery, payload != null); + if (payload != null && originalQuery != null && !originalQuery.trim().isEmpty()) { String fullText = payload.getString("full_text"); if (fullText != null) { Map metadata = new HashMap<>(); if (payload.get("title") != null) { metadata.put("title", payload.get("title")); } + log.info("[ShortNovel SSE] 开始保存小说: userId={}, queryLength={}, textLength={}", + currentUserId, originalQuery.length(), fullText.length()); Map saveResult = epicScriptDialogueServiceImpl.saveNovelResult( currentUserId, originalQuery, @@ -179,18 +192,26 @@ public class ShortNovelServiceImpl implements ShortNovelService { payload.put("scriptId", saveResult.get("scriptId")); payload.put("conversationId", saveResult.get("conversationId")); payload.put("currentVersionMessageId", saveResult.get("currentVersionMessageId")); + log.info("[ShortNovel SSE] 小说保存成功: scriptId={}", saveResult.get("scriptId")); + } else { + log.warn("[ShortNovel SSE] novel_done 事件缺少 full_text"); } + } else { + log.warn("[ShortNovel SSE] novel_done 事件跳过保存: originalQuery={}, payload={}", + originalQuery, payload); } } // 逐事件转发给前端(OkHttp 每读到一个完整 SSE 事件就立即转发) emitter.send(SseEmitter.event().name(type).data(event.toJSONString())); + emitter.send(SseEmitter.event().comment("")); // 触发 flush } catch (Exception parseEx) { log.warn("SSE 事件解析失败: {}", parseEx.getMessage()); } } } + log.info("[ShortNovel SSE] 完成读取上游响应"); emitter.complete(); } } catch (Exception e) {