Files
happy-life-star/docs/superpowers/specs/2026-06-28-rollback-scriptview-temp-id-protection-design.md
T

85 lines
3.9 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.
---
author: Peanut
created_at: 2026-06-28
purpose: 回滚 ScriptView 的临时 conversationId 正则短路保护,修复历史剧本消息卡片不显示的回归
---
# 回滚 ScriptView 临时 ID 正则短路保护
## 背景
commit 19337b9"ScriptView loadMessages 对临时 conversationId 正则短路保护")引入了一个回归 bug:
用户从"历史剧本"列表点进详情再点"继续"进入 ScriptView**看不到完整的消息卡片**。
## 根因
DB 中历史剧本的 `conversation_id` 字段值就是 `conv-<ts>-<rand>` 格式(前端在早期版本自造后写入):
```
conv-1782552967659-615gen
conv-1782228787632-fkifia
conv-1781497026473-7l8jco
...
```
这些 conversationId 完全匹配 commit 19337b9 加的正则 `^conv-\d+-[a-z0-9]{6}$`
并且这些 conversation 在 `t_conversation` 表中大多没有对应记录(`current_version_message_id` 字段全为 NULL,佐证了当时 conversation 没有完整落库)。
用户打开历史剧本的 ScriptView 时:
1. 从 store 拿到 script`conversationId = script.conversationId` = `conv-1782552967659-615gen`
2. 进入 `loadMessages()`
3. 正则匹配 + `backedByScript=false`script 上没有 conversationId 背书?错,有背书但被误判)
4. 实际上 `currentResult.value.conversationId``openScriptChat` 里被赋值为 `script.conversationId`,但 `openScriptChat` 是在 ScriptView 页内调用的,详情页 "继续" 按钮走的是 `continueCurrent``uni.reLaunch({ url: '/pages/main/index?tab=script' })`ScriptView 重新初始化时 `currentResult.value` 还未被赋值
5. 正则保护触发 → `messages.value = []`**所有消息卡片不显示**
## 修复方案
**删除** commit 19337b9 在 `mini-program/src/pages/main/ScriptView.vue``loadMessages` 中添加的正则短路保护代码。恢复为:
```javascript
const loadMessages = async () => {
if (!conversationId.value) return
try {
const res = await listMessagesByConversation({ conversationId: conversationId.value, includeVersions: false })
messages.value = res.data || []
const scriptMessages = messages.value.filter(m => m.type === 'script')
if (scriptMessages.length > 0) {
const rootMessage = scriptMessages[0]
const versionRes = await listMessageVersions(rootMessage.id)
versions.value = versionRes.data || []
}
} catch (error) {
uni.showToast({ title: error.message || '加载消息失败', icon: 'none' })
}
}
```
**为什么可以直接回滚**
- commit e3eb1af 已让后端 `listByConversation` 在 conversation 不存在时返回空列表(不抛 500)
- 所以前端调 API 时即使传了"不存在"的 conversationId,也只会得到空数据,不会再 500
- 5xx 噪音问题已由后端修复兜底,前端的正则保护属于过度防御
## 验证步骤
1. 修改 `mini-program/src/pages/main/ScriptView.vue`,删除正则短路保护
2. 启动 H5`python dev-services.py start mini-program`(端口 5180
3. 浏览器访问 `http://localhost:5180`,登录一个有历史剧本的账号
4. 进入任一历史剧本的 ScriptView
5. 验证:
- 不再看到正则保护导致的"消息全空"
- Network 面板有 `/message/listByConversation` 请求
- 响应状态 200body 为 `{"code":200,"data":[]}`conversation 不存在)或有数据
- Console 无报错
## 风险与回退
- **回退方案**:如果回滚后出现 500 噪音,可以再在前端加判断(但后端已兜底,不应再出现)
- **影响面**:仅影响 ScriptView.vue 的 loadMessages 函数,其他页面不受影响
## 长期改进(不在本次范围)
历史剧本的 conversation_id 是前端自造的临时 ID,但被当作"真实 conversationId"写入了 `t_epic_script.conversation_id`。这是数据模型上的不一致:
- 要么在写入 epic_script 时同步创建 t_conversation 记录
- 要么 epic_script.conversation_id 字段只存真实落库的 ID
- 本次不处理,单独跟踪