facc04aa65
本提交为 .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-*/
167 lines
6.4 KiB
Plaintext
167 lines
6.4 KiB
Plaintext
# 业务路由推荐 — 时序图
|
||
|
||
> 核心流程:员工发送非IT消息 → Dify识别 → 后端发送名片 → 前端渲染 → 点击跳转企微
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant E as 员工(H5)
|
||
participant H5 as H5前端
|
||
participant BE as 后端API<br/>(h5.py)
|
||
participant TASK as 后台AI任务<br/>(h5_ai_task.py)
|
||
participant RS as 路由服务<br/>(routing_service.py)
|
||
participant DIFY as Dify API<br/>(统一意图识别)
|
||
participant DB as PostgreSQL
|
||
participant WS as WebSocket<br/>(ws_manager)
|
||
participant AG as 坐席端
|
||
participant WECOM as 企微
|
||
|
||
%% ===== 第1阶段:员工发送消息 =====
|
||
rect rgb(240, 248, 255)
|
||
E->>H5: 发送非IT消息 "打印机坏了"
|
||
H5->>BE: POST /h5/conversations/current/messages
|
||
BE->>DB: 存储员工消息(msg_type=text)
|
||
BE->>WS: broadcast(new_message → 坐席)
|
||
BE-->>H5: 即时返回(user_message, ai_reply=null)
|
||
BE->>TASK: asyncio.create_task(process_h5_ai_reply)
|
||
end
|
||
|
||
%% ===== 第2阶段:后台任务 - 路由检测 =====
|
||
rect rgb(255, 248, 240)
|
||
Note over TASK: ① BYOD关键词检查(未命中,跳过)
|
||
|
||
TASK->>RS: 路由关键词预过滤("打印机")
|
||
RS-->>TASK: 命中 → 需调Dify
|
||
|
||
TASK->>RS: detect_routing_intent(text, employee_id)
|
||
RS->>DIFY: POST /v1/chat-messages<br/>{query: "打印机坏了"}
|
||
DIFY-->>RS: {intent_type: "non_it_routing",<br/>business_category: "行政",<br/>routing_confidence: 0.85}
|
||
RS-->>TASK: 路由结果(非IT, 行政, 0.85)
|
||
|
||
Note over TASK: routing_confidence ≥ 0.7 → 触发名片推荐
|
||
end
|
||
|
||
%% ===== 第3阶段:查询联系人 + 发送名片 =====
|
||
rect rgb(240, 255, 240)
|
||
TASK->>RS: get_contact_by_category("行政")
|
||
RS->>DB: SELECT * FROM business_contacts<br/>WHERE category='行政' AND is_active=true LIMIT 1
|
||
DB-->>RS: 返回联系人(王芳, wecom_userid=WangFang)
|
||
RS-->>TASK: BusinessContact对象
|
||
|
||
TASK->>RS: send_contact_card(db, conversation, employee_id, contact, reason)
|
||
|
||
%% 消息1: 路由说明文本
|
||
RS->>DB: 存Message(ai, text, "打印机问题属于行政...")
|
||
RS->>WS: broadcast_to_employees([employee_id], ai_reply)
|
||
WS-->>H5: ai_reply(路由文本)
|
||
RS->>WS: broadcast(new_message → 坐席)
|
||
WS-->>AG: new_message(路由文本)
|
||
|
||
%% 消息2: 名片卡片
|
||
RS->>DB: 存Message(ai, contact_card, extra_data={contact, reason})
|
||
RS->>WS: broadcast_to_employees([employee_id], ai_reply)
|
||
WS-->>H5: ai_reply(msg_type=contact_card, extra_data)
|
||
RS->>WS: broadcast(new_message → 坐席)
|
||
WS-->>AG: new_message(msg_type=contact_card, extra_data)
|
||
|
||
%% 消息3: 系统提示
|
||
RS->>DB: 存Message(system, "以上为AI自动推荐...")
|
||
RS->>WS: broadcast_to_employees([employee_id], ai_reply)
|
||
WS-->>H5: ai_reply(系统提示)
|
||
RS->>WS: broadcast(new_message → 坐席)
|
||
WS-->>AG: new_message(系统提示)
|
||
|
||
%% P1: 记录路由事件
|
||
RS->>DB: INSERT routing_event(P1统计)
|
||
end
|
||
|
||
%% ===== 第4阶段:前端渲染 =====
|
||
rect rgb(255, 245, 238)
|
||
H5->>H5: MessageBubble渲染<br/>路由文本(AI气泡)
|
||
H5->>H5: MessageBubble渲染<br/>ContactCard名片卡片
|
||
H5->>E: 显示名片(头像/姓名/部门/负责业务/联系TA)
|
||
H5->>H5: MessageBubble渲染<br/>系统消息(居中灰字)
|
||
|
||
AG->>AG: MessageBubble渲染<br/>名片卡片(坐席端, 无联系TA按钮)
|
||
end
|
||
|
||
%% ===== 第5阶段:点击跳转 =====
|
||
rect rgb(248, 240, 255)
|
||
E->>H5: 点击「联系TA」
|
||
H5->>H5: 读取 extra_data.contact.wecom_userid
|
||
alt userid 非空 && SDK就绪
|
||
H5->>WECOM: wx.invoke('openEnterpriseChat',<br/>{userids: 'WangFang', chatTitle: '王芳'})
|
||
WECOM-->>E: 打开企微单聊窗口
|
||
else userid为空 || SDK未就绪
|
||
H5->>E: Toast("暂时无法发起聊天,请联系管理员")
|
||
end
|
||
|
||
Note over E,BE: 当前IT服务台会话不自动结束<br/>员工可返回继续咨询
|
||
end
|
||
```
|
||
|
||
## 降级兜底流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant TASK as 后台AI任务
|
||
participant RS as 路由服务
|
||
participant DIFY as Dify API
|
||
participant DB as PostgreSQL
|
||
|
||
TASK->>RS: 路由关键词预过滤("工牌丢了")
|
||
RS-->>TASK: 命中("工牌" → 人力资源)
|
||
|
||
TASK->>RS: detect_routing_intent(text)
|
||
RS->>DIFY: 调用统一意图识别
|
||
|
||
alt Dify 调用成功
|
||
DIFY-->>RS: {intent_type: "non_it_routing", business_category: "人力资源", routing_confidence: 0.88}
|
||
RS-->>TASK: 使用Dify结果
|
||
else Dify 调用失败/超时
|
||
DIFY--xRS: 异常/超时
|
||
RS->>RS: 降级兜底:ROUTING_KEYWORD_TO_CATEGORY["工牌"] = "人力资源"
|
||
RS-->>TASK: 降级结果(business_category="人力资源", routing_confidence=0.6, source="fallback")
|
||
Note over RS: confidence < 0.7 → 不触发名片推荐
|
||
TASK->>TASK: 继续走正常AI回复流程
|
||
end
|
||
```
|
||
|
||
## 坐席手动发名片流程(P1)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant AG as 坐席端
|
||
participant BE as 后端API<br/>(routing.py)
|
||
participant RS as 路由服务
|
||
participant DB as PostgreSQL
|
||
participant WS as WebSocket
|
||
participant H5 as H5前端
|
||
|
||
AG->>AG: 点击「发名片」按钮
|
||
AG->>BE: GET /routing/contacts?business_category=财务
|
||
BE->>RS: get_contacts(category="财务")
|
||
RS->>DB: SELECT * FROM business_contacts WHERE category='财务'
|
||
DB-->>RS: 联系人列表
|
||
RS-->>BE: 联系人列表
|
||
BE-->>AG: 返回联系人列表
|
||
|
||
AG->>AG: 坐席选择联系人(李明)
|
||
AG->>BE: POST /conversations/{id}/send-contact-card<br/>{contact_id: 5, reason: "员工咨询报销问题"}
|
||
|
||
BE->>RS: send_contact_card(db, conversation, agent_id, contact, reason)
|
||
RS->>DB: 查询 BusinessContact by id=5
|
||
DB-->>RS: 返回联系人(李明)
|
||
|
||
RS->>DB: 创建 contact_card 消息<br/>(sender_type=ai, msg_type=contact_card,<br/>extra_data={contact, reason})
|
||
RS->>WS: broadcast_to_employees([employee_id], ai_reply)
|
||
WS-->>H5: ai_reply(contact_card)
|
||
RS->>WS: broadcast(new_message → 坐席)
|
||
WS-->>AG: new_message(contact_card)
|
||
|
||
BE-->>AG: 返回成功
|
||
H5->>H5: 渲染名片卡片
|
||
```
|