feat:clarification_card 与 outline_created 事件累积到全局缓存

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-26 15:01:26 +08:00
parent 7de91dbbb4
commit fcf4bcd9e4
@@ -179,7 +179,8 @@ public class ShortNovelServiceImpl implements ShortNovelService {
upstreamBody.put("message_id", "web_" + System.currentTimeMillis());
upstreamBody.put("query", request.getQuery());
return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId, request.getQuery());
return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId,
request.getQuery(), null);
}
@Override
@@ -207,7 +208,8 @@ public class ShortNovelServiceImpl implements ShortNovelService {
upstreamBody.put("action", request.getAction());
upstreamBody.put("payload", request.getPayload());
return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId, effectiveOriginalQuery);
return forwardSse("/api/novels/daily/conversation/stream", upstreamBody, currentUserId,
effectiveOriginalQuery, request.getSessionId());
}
/**
@@ -218,7 +220,8 @@ public class ShortNovelServiceImpl implements ShortNovelService {
* @param currentUserId 当前用户ID(从主线程捕获,避免 ThreadLocal 在异步线程中失效)
* @param originalQuery 原始用户查询(首次请求时传入,用于保存)
*/
private SseEmitter forwardSse(String path, Map<String, Object> body, String currentUserId, String originalQuery) {
private SseEmitter forwardSse(String path, Map<String, Object> body, String currentUserId,
String originalQuery, String knownSessionId) {
SseEmitter emitter = new SseEmitter(config.getReadTimeout().longValue());
EXECUTOR.execute(() -> {
@@ -229,6 +232,9 @@ public class ShortNovelServiceImpl implements ShortNovelService {
final java.util.concurrent.atomic.AtomicReference<JSONObject> stageMetaRef =
new java.util.concurrent.atomic.AtomicReference<>(new JSONObject());
// 当前流的 sessionIdstream 首次从 status 事件获取,followup 从请求参数获取)
final String[] currentSessionId = {knownSessionId};
// 累积 novel_delta 事件的文本内容
// 上游服务通过 novel_delta 流式发送小说内容,novel_done 事件可能不含 full_text
// 因此需要在后端累积所有 delta,在 novel_done 时使用累积的完整文本保存到数据库
@@ -289,8 +295,8 @@ public class ShortNovelServiceImpl implements ShortNovelService {
JSONObject payload = event.getJSONObject("payload");
if (payload != null) {
String sessionId = payload.getString("session_id");
if (sessionId != null && !sessionId.isEmpty()
&& originalQuery != null && !originalQuery.isEmpty()) {
if (sessionId != null && !sessionId.isEmpty()) {
currentSessionId[0] = sessionId;
// 缓存原始参数:originalQuery/style/length
String style = payload.getString("style");
String length = payload.getString("length");
@@ -307,11 +313,25 @@ public class ShortNovelServiceImpl implements ShortNovelService {
JSONObject outline = stagePayload.getJSONObject("outline");
if (outline != null) {
stageMetaRef.get().put("outline", outline);
// 累积到全局 session 缓存,供详情页还原大纲步骤
if (currentSessionId[0] != null) {
JSONObject entry = new JSONObject();
entry.put("kind", "outline");
entry.put("outline", outline);
accumulateStage(currentSessionId[0], entry);
}
}
} else if ("clarification_card".equals(type)) {
JSONObject card = stagePayload.getJSONObject("card");
if (card != null) {
stageMetaRef.get().put("clarification", card);
// 累积到全局 session 缓存,供详情页还原完整澄清流程
if (currentSessionId[0] != null) {
JSONObject entry = new JSONObject();
entry.put("kind", "clarification_question");
entry.put("card", card);
accumulateStage(currentSessionId[0], entry);
}
}
} else if ("novel_delta".equals(type)) {
// 累积小说正文片段:上游通过 novel_delta 逐段发送内容