chore: docs 结构整改 + compose 双目录对齐(合并重建提交)

本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
This commit is contained in:
Simon
2026-08-07 22:31:32 +08:00
parent 5a77a89ab1
commit facc04aa65
573 changed files with 129347 additions and 909 deletions
@@ -0,0 +1,129 @@
%% ====================================================================
%% 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