44e77dcb0e
**重构前**(旧编号 02-11): - docs/02-产品需求/ → 00 产品规划/PRD - docs/03-技术架构/ → 01-05 子目录散落 - docs/04-原型设计/ → 01-02 产品设计(HTML 原型) - docs/05-原型设计/ → screens/ - docs/06-测试素材/ → 02-E2E / 03-功能 / 04-版本测试 - docs/07-项目管理/ → 任务说明书/日报/计划 - docs/08-安全审计/ → 审计报告 - docs/09-堡垒运维/ → toolbox / deploy - docs/10-项目管理/ → 任务说明书(重复) - docs/11-历史归档/ → deploy-nas-archived **重构后**(新编号 00-07,语义化): - docs/00-产品开发流程与文档管理规范.md - docs/00-版本迭代总览.md - docs/01-产品文档/ (PRD/原型/认证/会话/AI 服务/坐席/集成) - docs/02-技术文档/ (技术方案/架构图/重构记录/前端改造/实现配置) - docs/03-测试文档/ (E2E/功能用例/版本报告/缺陷单) - docs/04-运维文档/ (部署运维/运维指南) - docs/05-运营文档/ (品牌推广/用户手册) - docs/06-安全审计/ (审计报告) - docs/07-项目管理/ (任务说明书/日报/计划/看板) **净收益**: - 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号) - 消除 02-产品需求 与 10-项目管理 的编号重叠 - 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录) - 把运维/安全/项目管理从 0X 散落改为 04/06/07 合计 494 文件 + 78495 行 / - 14076 行
13 KiB
13 KiB
PRD(增量草案) — 选项选择持久化 v1.1
REQ 编号: REQ-通用-005 版本: v1.1-DRAFT(草案,待用户确认范围,不替代 v1.0) 日期: 2026-08-02 作者: 许清楚(PM) 状态: 🟡 草案(v1.0 已批准,v1.1 仅增不删) 基线: v1.0 PRD(179 行,9 章节,已批准)+ v1.0 技术方案(523 行,11 章节)+ v1.0 测试用例(35 条)+ Dify v3 DRAFT 关联:
- 基线 PRD:
docs/01-产品文档/00-产品规划/PRD-REQ-通用-005-选项选择持久化-v1.0.md- 基线技术方案:
docs/02-技术文档/技术架构/技术方案-REQ-通用-005-选项选择持久化-v1.0.md- 基线测试用例:
docs/03-测试文档/03-功能测试用例/TC-REQ-通用-005-选项选择持久化-v1.0.md- Dify v3 DRAFT:
docs/02-技术文档/实现配置/dify_dsl/itdesk_main_v3_feedback-vars_DRAFT.yml- 原型:无(v1.0 暂无独立原型;UX 调整需重开)
1. 增量背景(Why v1.1)
v1.0 已部署并通过 35 条测试用例验收,但 2026-08-02 用户实测暴露 3 个持续 Bug + 1 个新需求,Dify v3 工作流(用户尚未导入) 仍未落地,导致"AI 跟进选项"语义无法端到端验证。
| 触发事件 | 观察 | 业务影响 |
|---|---|---|
| 2026-08-02 17:45 用户反馈 | Bug 4:员工提问后看到 AI 回答,坐席端实时收单,但员工端需刷新才显示 | 员工体感卡顿,怀疑"卡死" |
| 2026-08-02 17:45 用户反馈 | Bug 5:选选项后,先看到 AI 思考占位 → 后同时出现"答案 + 上一个选择" | 时序错位,破坏确定性信任 |
| 2026-08-02 17:45 用户问询 | Bug 6:曾发生"页面刷新后选择消失",询问是否真解决 | 会话连续性 + 审计可追溯 |
| 2026-08-02 17:45 用户强调 | Req 7:员工选选项后,效果实时同步到坐席端(< 100ms) | 紧急报修场景坐席分诊效率 |
v1.1 目标:在不动 v1.0 6 项产品决策(前缀:撤回/重选、幂等键、跨卡归属、敏感词、Dify 语义、转人工快照)的前提下,修复时序与持久化体感问题,并验证 v1.0 的"刷新保留"是否真达成。
2. 与 v1.0 的差异(What Changed)
| 编号 | v1.0 内容 | v1.1 增量 | 优先级 |
|---|---|---|---|
| Bug 4 | (v1.0 无) | 员工端 AI 回答延迟显示:消息已落库 + WS 已广播坐席端,但员工端需刷新才显示 | P0 |
| Bug 5 | (v1.0 无) | 选选项时序错位:先 AI 思考占位 → 后同时出现"答案 + 上一个选择" | P1 |
| Bug 6 | v1.0 §4.1 / AC-01 已要求"落库 option_select + REST 仍可见" | 显式写入"刷新后必须保留选择历史"作为P0 验收;消除 v1.0 隐含歧义 | P0 |
| Req 7 | v1.0 §4.2 + §3.1 #2 仅要求"坐席可实时可见" | 新增显式时延指标:坐席端 < 100ms 同步;坐席不在线时恢复后立即可见 | P0(用户强调) |
v1.0 的 6 项产品决策(撤回/重选、5 秒 UUID 幂等、跨卡归属、敏感词 4 位中间 mask、Dify 5 字段 inputs、转人工 selected_options 快照)全部不变。
3. 增量 PRD:详细功能需求
3.1 Bug 4 — 员工端 AI 回答延迟显示(P0)
| 字段 | 内容 |
|---|---|
| 现象 | 员工提问 → AI 思考 → 答案已落库 + WS 已广播到坐席(坐席端实时可见)→ 员工端界面不更新,刷新后才行 |
| 现状根因候选 | 员工端 message-store 内 processedMessageIds 已包含该 message_id(之前 WS 收到过 chunk 阶段 ID),导致 new_message 完整事件被 if (processedMessageIds.has(data.message_id)) return 静默丢弃(src/frontend-h5/src/stores/conversation.ts:474-477) |
| 期望 | 员工端 AI 回答 < 1 秒内显示(不需要刷新) |
| 验收 | 员工端提问 → 2 秒内看到 AI 回答气泡(不强求同步打字机,但气泡必须出现) |
| 建议修复方向(待架构师确认) | 区分"中间 chunk 的 message_id"与"最终 message_id";或把最终 new_message 事件直接接收,不去重 |
| 关联基线 | v1.0 §4.2 WS 事件;conversation.ts:474-477、conversation.ts:1404-1407 |
3.2 Bug 5 — 选选项时序错位(P1)
| 字段 | 内容 |
|---|---|
| 现象 | 员工选选项 → UI 立即显示:AI 思考占位 + 上一个选择气泡 → 几秒后同时出现"新答案 + 上一个选择"(时序错位) |
| 现状根因候选 | H5 sendOptionSelect 触发 process_h5_ai_reply → 思考占位立刻 push 到 messages.value(h5_ai_task.py:1574 WS 推送 ai_thinking)→ 但上一个 option_select 消息尚未回流到 store → 答案到达时上一条 option_select 一起渲染 |
| 期望 | 选选项后 UI 顺序:① 已选气泡(✓)→ ② AI 思考占位 → ③ AI 答案气泡 |
| 验收 | 选选项后 3 个 UI 元素按时间顺序独立出现,不同时弹出 |
| 建议修复方向(待架构师确认) | sendOptionSelect 同步把已选消息 push 到本地 store(不依赖 WS 回流),或后端先 ack 再触发 Dify |
| 关联基线 | v1.0 §4.4 H5 字段补全;conversation.ts:1640-1710 |
3.3 Bug 6 — 刷新后选择消失(P0,需确认 v1.0 是否真解决)
| 字段 | 内容 |
|---|---|
| 现象 | 员工选选项后能看到 ✓ 气泡 → 刷新页面 → ✓ 气泡消失 |
| 现状调研 | 见 §7 本节根因分析 |
| 调研结论 | H5 REST 端点 h5.py:964-1029 未实现 v1.0 §4.8 mask 要求(存安全漏洞),但未发现"消失"的代码缺陷。理论上:DB 存原值 → REST 返回原值 → mapMessage 保留 msg_type → MessageBubble.vue:174 渲染 ✓ 模板 → 选项应可见 |
| 可能的"消失"根因 | ① 默认 limit=50,长会话(>50 条)历史选项被分页(P1 修复);② 浏览器缓存被清除(H5 应有 fallback);③ DB 该行因 WS 关闭或事务回滚未落库 |
| v1.1 期望 | 显式写入"刷新后所有历史选项气泡按时间顺序显示"作为 P0 验收;不再依赖隐含理解 |
| 验收 | ① 员工选选项 → 刷新 → ✓ 气泡依然可见;② 选项按 server_timestamp 升序;③ 选项时间戳对应的 AI 题目卡片也应可见 |
| 建议修复方向 | ① 在 AC-01 加 P0 子项 AC-01-2"刷新后仍可见";② H5 端点在 limit=50 不够时支持 before 翻页验证;③ 紧急 P0 验证步骤必须包含实际操作 |
| 关联基线 | v1.0 §4.1 + AC-01;h5.py:964-1029 |
3.4 Req 7 — 实时同步到坐席端(P0,用户强调)
| 字段 | 内容 |
|---|---|
| 现象 | 员工选选项后,坐席端是否立即看到?用户担心存在 < 1s 延迟 |
| 现状 | v1.0 §4.2 已实现 ws_manager.broadcast({"type": "new_message", "data": ...})(坐席端有新事件后 MessageBubble 渲染 ✓),链路已稳定 |
| v1.1 期望 | 显式时延指标:员工点击选项 → 坐席端 < 100ms 内看到"已选:xxx ✓" |
| 验收 | ① 在线坐席端 < 1s 看到新消息事件;② 坐席不在线 → 重新加载时 REST 历史含同样 message_id;③ 弱网或 502 时降级为 3s 轮询可见 |
| 建议实施 | 无需新代码——v1.0 §4.2 已实现。建议架构师出具 E2E 验收日志(WS 广播时间戳 + 坐席端 MessageBubble 渲染时间戳差值)证明 < 100ms |
| 关联基线 | v1.0 §4.2、AC-03、AC-10;ws.py:407-421 |
4. 与 v1.0 兼容性(What Stays)
| 维度 | v1.0 决策 | v1.1 是否变动 |
|---|---|---|
| 撤回/重选语义 | 追加新行、不更新旧行(§4.1) | ❌ 不变 |
| 5 秒 UUID 幂等 | (conversation_id, client_msg_id) + 5s 窗口(§4.5) |
❌ 不变 |
| 跨卡归属 | question_id + option_id 联合,label 不作主键(§6) |
❌ 不变 |
| 敏感词 mask | 16 位数字中间 4 位 ****(§4.8) |
❌ 不变 |
| Dify 5 字段 inputs | feedback_type/question_id/option_id/option_value/option_label(§4.6) |
❌ 不变 |
| 转人工快照 | selected_options 每题最新(§4.7) |
❌ 不变 |
| 5 重 UUID 守卫(V0-C 修复) | WS 在线 + 5 题内 + 5s 内 + 同题 + 未陈旧(conversation.ts:1662-1675) |
❌ 不变,作为 v1.1 Bug 5 修复的"前提" |
5. 风险与依赖
| 风险 | 等级 | 缓解/验证 |
|---|---|---|
| Dify v3 未导入 → 修复 Bug 4/5/6 后,用户感知不到 Bug 真实修复(因 AI 回答本身没变) | 高 | v1.1 落地前提:先确认 Dify v3 导入时间表;建议 2026-08-09 前完成 |
实时同步依赖后端 ws_manager 单进程 |
中 | v1.0 已固定 --workers 1;v1.1 沿用 |
| 坐席端 before 翻页未断言 | 中 | v1.0 TC-004 已覆盖;v1.1 沿用 |
| 长会话 limit=50 可能漏显 | 中 | Bug 6 验证;若发现,v1.1 引入翻页 |
H5 端点 mask 缺失(h5.py:1025) |
高 | v1.0 §4.8 已要求,v1.1 修复(顺手) |
| 浏览器缓存丢失 | 低 | 现有 fallback getMessages API |
6. 待用户确认(5 个问题)
- Bug 4 / 5 / 6 的优先级排序是否合理?(当前:Bug 4=Bug 6=P0 / Bug 5=P1)
- Req 7 是否属于 v1.1 范围?(用户强调"如果没有技术问题,希望实现"——倾向属于 v1.1)
- 是否同意新增"刷新后必须保留选择历史"作为 v1.1 P0 验收(AC-01-2 增项)?
- Dify v3 何时导入?(影响 AI 能否"基于选项的跟进",进而影响 Bug 4/5 是否真验收)
- 是否需要拉原型?如需 UI 调整(坐席侧徽标位置、员工端选项视觉等),需 UX 重新设计
7. Bug 6 根因分析(基于代码 + Git 现状)
用户问"之前发生过页面刷新后选择消失问题,是否真解决"——下面给出基于代码证据的判断。
7.1 代码调研(5 个关键点)
| # | 调研点 | 代码位置 | 结论 |
|---|---|---|---|
| 1 | 后端持久化 | ws.py:374-385 Message(msg_type="option_select", content=option_label, ...) + db.add() + commit |
✅ 落库 |
| 2 | H5 REST 端点是否返回 option_select | h5.py:964-1029 select(Message).where(conversation_id==...)(无 msg_type 过滤) + MessageResponse.model_validate(m).model_dump() |
✅ 应返回 |
| 3 | H5 端点是否 mask option_select | h5.py:1025 直接 dump,无 mask(违反 v1.0 §4.8) |
❌ 存安全漏洞,但与"消失"无关 |
| 4 | 前端字段映射 | api/conversation.ts:234-249 mapMessage:msg_type: raw.msg_type 直接透传 |
✅ 保留 |
| 5 | UI 渲染分支 | MessageBubble.vue:174-179 <template v-else-if="msg.msg_type === 'option_select'">✓ {{ msg.content }}</template> |
✅ 渲染分支存在 |
| 6 | 缓存 + 合并 | conversation.ts:62-82 + 85-97 + 117-119 mergeMessages 按 message_id 去重 |
✅ 应保留 |
| 7 | 限分页 | h5.py:999 limit(limit) 默认 50 |
⚠️ 长会话会被分页 |
7.2 根因判定(确定性)
基于代码,`Bug 6 "刷新后选择消失" 在当前 v1.0 应不应发生?——不应发生。但有以下 3 个潜在触发场景:
| 场景 | 当前是否根因 | 概率 |
|---|---|---|
A. H5 端点未 mask(h5.py:1025) |
否(是安全 bug,不是"消失") | 高 |
| B. 默认 limit=50,长会话历史选项被分页 | 是(超过 50 条之后,刷新只显示最新 50 条) | 中 |
| C. 浏览器缓存被清除 + step 3 异步 fetch 失败 | 是(无网络时,UI 不会显示选项气泡) | 中 |
| D. 消息真正未落库(DB 事务回滚) | 否(v1.0 §3.1 #1 已 P0 验收,PG advisory lock 已避免) | 极低 |
E. 字段映射丢失(msg_type 被过滤) |
否(mapMessage 直接透传) |
极低 |
7.3 结论
Bug 6 在 v1.0 当前实现下大概率已被解决——代码层面:
- 后端 ✅ 落库
- API ✅ 返回
- 字段映射 ✅ 保留
- UI ✅ 渲染分支存在
但有 2 个潜在根因未被 v1.0 显式覆盖:
- H5 端点 limit=50 未分页验证(场景 B)
- H5 端点 mask 缺失(场景 A,与"消失"无关但是安全漏洞)
v1.1 建议:
- 把"刷新后保留"显式写入 PR v1.1 验收(AC-01-2)
- 顺手修复 H5 端点 mask 漏洞(§3.1 修复的同时)
- 增加
E2E验收步骤:选选项 → 刷新 → 截图选项气泡
8. 附录:v1.1 增量范围 vs 完整 PRD
| 范围 | 是否在 v1.1 增量草案 | 备注 |
|---|---|---|
| Bug 4 / 5 / 6 修复 | ✅ 草案 | 待用户确认优先级 |
| Req 7 实时同步验收 | ✅ 草案 | 显式时延指标 |
| v1.0 6 项决策 | ❌ 不再重复 | 见 v1.0 原 PRD |
| Dify v3 变更 | ❌ 不再重复 | 见 Dify v3 CHANGELOG |
| 测试用例增量 | ⏳ 下一步 | 待 QA 在 v1.1 范围确认后增量 |
| 完整 PRD(含组件图、时序图、API 变更) | ❌ 不出 | 用户明确"先写草案" |
9. 变更记录
| 版本 | 日期 | 变更内容 | 变更人 | 变更原因 |
|---|---|---|---|---|
| v1.0 | 2026-07-29 | 创建 PRD,固化方案 A、6 项产品决策与 MVP 验收范围 | 许清楚、宋献 | 修复选项选择不落库 |
| v1.1-DRAFT | 2026-08-02 | 增量草案:3 Bug + 1 Req + Bug 6 根因分析 + 5 待确认问题 | 许清楚 | 用户实测反馈 + 询问刷新保留是否真解决 |
下一步:等待用户对 §6 五个问题的回复 → 确认 v1.1 范围 → 由架构师出 v1.1 技术方案 → 由 QA 补 v1.1 测试用例增量。