Files
happy-life-star/docs/superpowers/plans/2026-07-26-detail-clarification-card-count-fix.md
T

136 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 详情页澄清选项卡显示不全修复 实施计划
> **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(数据已永久丢失,符合预期)