diff --git a/docs/superpowers/specs/2026-07-26-detail-clarification-card-count-fix-design.md b/docs/superpowers/specs/2026-07-26-detail-clarification-card-count-fix-design.md new file mode 100644 index 0000000..da87fb3 --- /dev/null +++ b/docs/superpowers/specs/2026-07-26-detail-clarification-card-count-fix-design.md @@ -0,0 +1,75 @@ +--- +author: AI Assistant +created_at: 2026-07-26 +purpose: 修复详情页只显示 2 个澄清选项卡(应为 N 个)的后端累积丢失 bug +--- + +# 详情页澄清选项卡显示不全修复设计 + +## 问题 + +生成小说时,生成页显示 N 个澄清选项卡(clarification cards),但进入详情页后只显示前 2 个。必须保证详情页选项卡数量与生成页完全一致。 + +## 根因分析 + +通过数据库查询(t_message 按 conversation_id 分组统计)确认:3 个有 clarification 的 conversation 全部是 **clarification_question=2 + clarification_answer=3**。即用户回答了 3 次(3 个 answer),但后端只保存了 2 个 question。**丢失的是第一个 clarification_card(Q1)**。 + +DB 中 conversation `339751895901937665` 的 messageOrder 分布: + +| order | type | content | +|---|---|---| +| 1 | clarification_answer | 同事/上司 | +| 2 | clarification_question | 是什么具体事件让你决定不再讨好? | +| 3 | clarification_answer | 功劳被抢 | +| 4 | clarification_question | 你希望主角如何反击? | +| 5 | clarification_answer | 暗中布局 | +| 6 | outline | ... | + +**根因链**: + +1. **上游 SSE 事件结构**:`session_id` 字段在事件**顶层**(与 `type` 同级),不在 `payload` 内。前端从顶层取(`ScriptView.vue:1800` `const { type, session_id, payload } = event`)且工作正常。 +2. **后端 status 事件处理**:`ShortNovelServiceImpl.forwardSse` 行 312-314 从 `payload.getString("session_id")` 取,**取不到**。 +3. **首次 stream 的 currentSessionId[0] 初始为 null**:`forwardSse` 行 253 `final String[] currentSessionId = {knownSessionId}`,首次 stream 调用时 `knownSessionId=null`。 +4. **首次 stream 的 Q1 不累积**:首次 stream 里第一个 `clarification_card` 事件到达时 `currentSessionId[0]` 仍为 null,行 346 的 `if (currentSessionId[0] != null)` 为 false,Q1 不被 `accumulateStage` 累积。 +5. **followup 流正常**:followup 调用 `forwardSse` 时 `knownSessionId = request.getSessionId()`(前端传的,非 null),后续 Q2/Q3 都正确累积。 +6. **drainStages 取到不完整的 stagesHistory**:novel_done 时 drainStages 取到 `[A1, Q2, A2, Q3, A3, outline]`(缺 Q1),saveNovelResult 创建 2 question + 3 answer。 +7. **详情页只能渲染 2 个 card**:从 DB 读到的 clarification_question 只有 2 条。 + +## 修复方案 + +**修改文件**:`server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java` + +**修改点**:`forwardSse` 方法的事件循环(行 305 附近),在解析 event 后、status 处理之前,**从事件顶层取 `session_id` 同步 `currentSessionId[0]`**(与前端 `ScriptView.vue:1805-1806` 取 sessionId 的方式对齐)。 + +**新增代码**(放在 `String type = event.getString("type");` 之后、status 事件 `if ("status".equals(type))` 之前): + +```java +// 从事件顶层取 session_id 同步 sessionId +// 上游事件结构与前端对齐:session_id 与 type 同级,不在 payload 内 +// 修复:首次 stream 里 status 处理从 payload 取不到 session_id,导致 currentSessionId 保持 null, +// 第一个 clarification_card 因 sessionId 为 null 不累积,详情页少显示第一个选项卡 +String topSessionId = event.getString("session_id"); +if (topSessionId != null && !topSessionId.isEmpty()) { + currentSessionId[0] = topSessionId; +} +``` + +## 影响范围 + +- 仅 `forwardSse` 方法新增 5 行代码 +- 零现有逻辑改动(status 处理保留原样作为次选,顶层取 sessionId 优先覆盖) +- 零数据库变更 +- 零前端改动 + +## 预期效果 + +- **新剧本**:首次 stream 的 Q1 不再丢失,详情页显示全部 N 个选项卡(与生成页完全一致) +- **老剧本**(3 个已有 conversation):保持现状,仍显示 2 个 card。DB 中 stagesHistory 缺失的 Q1 已永久丢失,无法真实恢复。 + +## 验收标准 + +1. 在 H5 手动完成一次新的小说生成(包含至少 2 轮澄清) +2. 进入该剧本的详情页 +3. 验证选项卡数量与生成页完全一致(生成 N 个,详情显示 N 个) +4. Console 无新增错误 +5. 本地 `mvn clean install` 编译通过