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 行
130 lines
5.5 KiB
Plaintext
130 lines
5.5 KiB
Plaintext
%% ====================================================================
|
|
%% v1.1 正式技术方案 — 4 个时序图
|
|
%% ====================================================================
|
|
|
|
%% ========== 时序 ①:点选-广播-落库 ==========
|
|
sequenceDiagram
|
|
autonumber
|
|
actor Emp as 员工
|
|
participant MB as MessageBubble
|
|
participant Store as ConversationStore
|
|
participant WS as ws._handle_option_select
|
|
participant DB as PostgreSQL
|
|
participant WSM as ws_manager
|
|
participant Obs as BackendObserver
|
|
participant Task as process_h5_ai_reply
|
|
|
|
Emp->>MB: 点击 option
|
|
MB->>Store: sendOptionSelect(value, label, sourceId, qId, oId)
|
|
Note over Store: lastSentOptionContent=label<br/>optionSubmitting=true<br/>5 重 UUID 守卫判定
|
|
Store->>WS: option_select(6 字段 + client_msg_id)
|
|
WS->>WS: UUID 校验
|
|
WS->>DB: BEGIN + pg_advisory_xact_lock
|
|
WS->>DB: SELECT 5s 内同 UUID
|
|
alt 5s 内重复命中
|
|
DB-->>WS: dup row returned
|
|
WS-->>Store: 无副作用,结束
|
|
else 首次受理
|
|
WS->>DB: INSERT Message(msg_type=option_select)
|
|
DB-->>WS: commit_ts = T1
|
|
Note over WS,WSM: ★ v1.1 T03: 同步推员工端
|
|
WS->>WSM: broadcast_to_employees([employee_id], new_message)
|
|
WSM-->>Store: new_message (employee 端)
|
|
Store->>Store: trackProcessedMessageId + push message
|
|
Note over Store: 员工立即看到 ✓ 气泡
|
|
WS->>WSM: broadcast(new_message) - 坐席
|
|
WSM-->>Obs: 记录 broadcast_ts = T2
|
|
Obs-->>Obs: option_select_broadcast_latency_ms = T2 - T1
|
|
WS->>Task: create_task(process_h5_ai_reply)
|
|
end
|
|
|
|
%% ========== 时序 ②:AI 回复回流 ==========
|
|
|
|
sequenceDiagram
|
|
autonumber
|
|
participant Dify as Dify Workflow
|
|
participant Task as process_h5_ai_reply
|
|
participant DB as PostgreSQL
|
|
participant WSM as ws_manager
|
|
participant WS as H5 useH5WebSocket
|
|
participant Store as ConversationStore
|
|
participant MB as MessageBubble
|
|
|
|
Dify-->>Task: 返回结构化 reply (ai_text 或 still_thinking)
|
|
alt Dify 正常返回
|
|
Task->>DB: INSERT Message(ai_text)
|
|
DB-->>Task: commit
|
|
else Dify 超时 ≥ 15s
|
|
Note over Task: ★ v1.1 T03: still_thinking 兜底<br/>主动补发 option_select_confirm
|
|
Task->>WSM: broadcast_to_employees(option_select_confirm)
|
|
end
|
|
Task->>WSM: broadcast_to_employees(ai_reply)
|
|
WSM->>WS: ai_reply { type: "ai_reply", data: {...} }
|
|
WS->>Store: handleAiReply(data)
|
|
Note over Store: ★ v1.1 Bug 4 软校验:<br/>conversation_id 不匹配 → console.warn<br/>但不再 return<br/>仅 message_id 已去重才 return
|
|
Store->>Store: trackProcessedMessageId(message_id)
|
|
Store->>Store: messages.value.push(finalMessage)
|
|
Store-->>MB: reactive 触发重渲
|
|
MB->>MB: 显示 AI 答案气泡(打字机逐字)
|
|
Note over MB: ★ v1.1 T02 派生:<br/>isOptionSelected 从 messages 过滤<br/>不再依赖内存 ref
|
|
|
|
%% ========== 时序 ③:视觉分组 ==========
|
|
|
|
sequenceDiagram
|
|
autonumber
|
|
actor Emp as 员工
|
|
participant Store as ConversationStore
|
|
participant Panel as ChatPanel
|
|
participant MB as MessageBubble
|
|
|
|
Note over Store: messages 序列示例:<br/>[ai_structured Q1]<br/>[option_select Q1.optA] 第 1 次<br/>[ai_text 回复1]<br/>[option_select Q1.optB] 第 2 次(重选)<br/>[ai_text 回复2]
|
|
|
|
Emp->>Store: 选 Q1.optB
|
|
Store->>Store: sendOptionSelect → WS send
|
|
Note over Store: groupedMessagesByQuestion (v1.1 ★)<br/>按 question_id 相邻聚合<br/>折叠 Q1.optA,激活 Q1.optB
|
|
|
|
Panel->>MB: v-for msg in groupedMessages
|
|
MB->>MB: option_select 模板加 data-question-id
|
|
alt collapsed (历史同题选项)
|
|
MB->>MB: class=message-bubble--option-select collapsed<br/>背景灰底 + 折叠图标 ▾
|
|
else active (本次选项)
|
|
MB->>MB: class=message-bubble--option-select active<br/>蓝底 + ✓
|
|
end
|
|
MB->>MB: ai_text 紧随其后,按时间顺序独立出现
|
|
Note over MB: 3 个 UI 元素顺序独立:<br/>① ✓当前 ② AI 思考占位 ③ AI 答案
|
|
|
|
%% ========== 时序 ④:时延监控 ==========
|
|
|
|
sequenceDiagram
|
|
autonumber
|
|
actor Emp as 员工 (Chromium A)
|
|
actor Agent as 坐席 (Chromium B)
|
|
participant H5Emp as H5 useH5WebSocket
|
|
participant H5Agt as H5 useH5WebSocket
|
|
participant WS as ws._handle_option_select
|
|
participant WSM as ws_manager
|
|
participant Obs as BackendObserver
|
|
participant Script as measure-option-latency.mjs
|
|
|
|
Script->>Script: 启动双 context + login 双方
|
|
Script->>Script: t0_start = performance.now()
|
|
|
|
Emp->>H5Emp: click option
|
|
H5Emp->>WS: option_select payload (含 client_msg_id UUID)
|
|
H5Emp->>H5Emp: 记录 client_send_ts = T0
|
|
|
|
WS->>WSM: broadcast_to_employees + broadcast
|
|
WSM->>H5Agt: new_message(option_select)
|
|
H5Agt->>H5Agt: 渲染 ✓ 气泡
|
|
H5Agt->>Script: postMessage { event: 'rendered', ts: T_agent }
|
|
|
|
H5Emp->>H5Emp: 收到自己的 new_message (员工端)
|
|
H5Emp->>Obs: POST /api/backend-observer/record<br/>{metric: 'option_select_e2e_latency_ms',<br/>value: T_agent - T0}
|
|
WSM->>Obs: 记录 option_select_persist_latency_ms (commit_ts)<br/>记录 option_select_broadcast_latency_ms (broadcast_ts)
|
|
|
|
Script->>Script: t1_end = performance.now() (员工点击 → 坐席渲染)
|
|
Script->>Obs: GET /api/backend-observer/metrics?name=option_select_*
|
|
Obs-->>Script: 4 个指标样本
|
|
Script->>Script: p50/p95 计算 + 告警 (>100ms warning)
|
|
Script->>Script: 输出 report.json
|