# 详情页澄清选项卡显示不全修复 实施计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 修复后端 `forwardSse` 首次 stream 中第一个 clarification_card 因 sessionId 为 null 不累积的 bug,使详情页澄清选项卡数量与生成页完全一致。 **Architecture:** 在 `ShortNovelServiceImpl.forwardSse` 的事件循环中,每次解析 SSE 事件后,从事件**顶层**取 `session_id` 同步 `currentSessionId[0]`(与前端 `ScriptView.vue:1805` 取 sessionId 的方式对齐)。这样无论 status 事件是否在 payload 内携带 session_id,只要事件顶层有 session_id,首次 stream 的 `currentSessionId[0]` 就能被正确设置,首次 stream 里的 `clarification_card` 就能被累积。 **Tech Stack:** Java 17 / Spring Boot 2.7.18 / fastjson2 / OkHttp / MySQL / Maven / Python 部署脚本 / H5 端到端验收 ## Global Constraints - 编译命令:`mvn clean install -DskipTests`(禁止 `mvn clean compile`) - 部署命令:`python deploy.py backend`(根目录脚本) - 部署前必须本地编译通过 - 部署后必须通过 H5 端到端验收(Console 0 新增错误 + Network 接口正常) - 注释必须使用中文 - 禁止任何形式的 mock、兜底、默认值掩盖错误 - 只改后端 1 个方法(`forwardSse`),零前端改动,零数据库变更 --- ## Task 1: 后端修复 forwardSse 顶层取 session_id **Files:** - Modify: `server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java:304-308`(`forwardSse` 方法的事件循环内,status 处理之前) **Interfaces:** - Consumes: 上游 SSE 事件 JSON 结构(事件顶层 `session_id` 字段与 `type` 同级) - Produces: `currentSessionId[0]` 在首次 stream 里也能被正确设置(不再是 null) ### 背景 - **根因**:上游 SSE 事件的 `session_id` 在事件**顶层**(与 `type` 同级),但后端 `forwardSse` 行 312-314 从 `payload.getString("session_id")` 取,取不到。导致首次 stream 的 `currentSessionId[0]` 保持 null(`knownSessionId=null`)。 - **数据证据**:DB 中 3 个有 clarification 的 conversation 全部是 `clarification_question=2 + clarification_answer=3`。第一个 `clarification_question` 因 `currentSessionId[0]==null` 不累积。 - **修复思路**:在 status 处理之前,从事件顶层取 `session_id` 同步 `currentSessionId[0]`(与前端 `ScriptView.vue:1805-1806` 逻辑对齐)。 ### 步骤 - [ ] **Step 1: 在 forwardSse 事件循环中插入从顶层取 session_id 的代码** 打开 `server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java`,定位到 `forwardSse` 方法内的事件循环。当前代码(行 304-311): ```java try { JSONObject event = JSON.parseObject(dataStr); String type = event.getString("type"); long eventTimestamp = System.currentTimeMillis(); log.info("[ShortNovel SSE] 处理事件: type={}, timestamp={}", type, eventTimestamp); // 拦截 status 事件,缓存 sessionId → originalQuery 映射(供 followup 兜底) if ("status".equals(type)) { ``` 在 `log.info(...)` 之后、`// 拦截 status 事件` 注释之前,**插入以下 5 行代码**: ```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; } ``` 插入后整体结构(行 304-320): ```java try { JSONObject event = JSON.parseObject(dataStr); String type = event.getString("type"); long eventTimestamp = System.currentTimeMillis(); log.info("[ShortNovel SSE] 处理事件: type={}, timestamp={}", type, eventTimestamp); // 从事件顶层取 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; } // 拦截 status 事件,缓存 sessionId → originalQuery 映射(供 followup 兜底) if ("status".equals(type)) { ``` - [ ] **Step 2: 本地编译验证** Run: `cd server && mvn clean install -DskipTests` Expected: `BUILD SUCCESS`,零错误。 - [ ] **Step 3: 部署到远程服务器** Run: `python deploy.py backend` Expected: 部署脚本输出"部署成功",服务重启完成。 如果部署脚本在 Windows 报 PATH 问题,手动执行: ```bash scp server/target/emotion-single-1.0.0.jar root@101.200.208.45:/data/programs/emotion-museum/ ssh root@101.200.208.45 "cd /data/programs/emotion-museum && ./restart.sh emotion-museum-single" ``` - [ ] **Step 4: H5 端到端验收** 1. 启动 mini-program H5:`python dev-services.py start mini-program`(端口 5180) 2. 浏览器访问 `http://localhost:5180/#/pages/main/index?tab=script` 3. 手动完成一次新的小说生成(包含至少 2 轮澄清) 4. 生成过程中**记录生成页显示的选项卡数量 N** 5. 生成完成后,进入该剧本的详情页(通过列表页点"历史"进入 ScriptLibraryView,再点击对应剧本) 6. 验证详情页显示 **N 个澄清选项卡**(与生成页完全一致) 7. 浏览器 Console 检查:0 新增错误(既有的 `uni.getRecorderManager` 错误除外) 8. Network 面板:`listByConversation` 接口响应中 `clarification_question` 类型消息数量 = 生成页澄清轮数 - [ ] **Step 5: 提交代码** ```bash cd G:\IdeaProjects\emotion-museun git add server/src/main/java/com/emotion/service/impl/ShortNovelServiceImpl.java git commit -m "fix(server): 修复 forwardSse 首次 stream 第一个 clarification_card 不累积的 bug 上游 SSE 事件的 session_id 在事件顶层(与 type 同级),后端原从 payload.session_id 取导致首次 stream 取不到, currentSessionId 保持 null,第一个 clarification_card 不累积。 改为从事件顶层取 session_id 同步 currentSessionId(与前端 ScriptView.vue:1805 逻辑对齐)。 修复后新剧本详情页澄清选项卡数量与生成页完全一致。" ``` ### 验收标准 - ✅ 本地 `mvn clean install` 编译通过 - ✅ 部署到远程服务器成功 - ✅ 新剧本详情页澄清选项卡数量 = 生成页澄清选项卡数量 - ✅ Console 0 新增错误 - ✅ 老剧本(3 个已有 conversation)保持现状,仍显示 2 个 card(数据已永久丢失,符合预期)