chore: initial baseline with P0-safety .gitignore

This commit is contained in:
Simon
2026-06-14 16:49:18 +08:00
commit 63262292d7
510 changed files with 146008 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
classDiagram
class Agent {
+str id
+str user_id
+str name
+str status
+str role
+list skill_tags
+int current_load
+int max_load
+datetime created_at
+datetime updated_at
}
class SystemConfig {
+str id
+str config_key
+str config_value
+str description
+datetime updated_at
}
class ConfigChangeLog {
+str id
+str config_key
+str old_value
+str new_value
+str changed_by
+datetime changed_at
}
class QuickReplyTemplate {
+str id
+str category
+str title
+str content
+list variables
+int sort_order
+str status
+int version
+str submitted_by
+datetime created_at
+datetime updated_at
}
class Conversation {
+str id
+str employee_id
+str employee_name
+str status
+int urgency_score
+str assigned_agent_id
+datetime created_at
}
ConfigChangeLog --> SystemConfig : tracks changes to
ConfigChangeLog --> Agent : changed_by
QuickReplyTemplate --> Agent : submitted_by
Conversation --> Agent : assigned_agent_id
@@ -0,0 +1,19 @@
sequenceDiagram
participant U as 管理员
participant FE as frontend-admin
participant API as /api/admin/configs/{key}
participant SVC as admin_service
participant DB as PostgreSQL
U->>FE: 切换应急模式开关
FE->>API: PUT /api/admin/configs/emergency_mode
API->>API: require_admin 校验权限
API->>SVC: update_config(key, value, agent_id)
SVC->>DB: SELECT SystemConfig WHERE key=emergency_mode
DB-->>SVC: 当前值 "false"
SVC->>DB: INSERT ConfigChangeLog(old="false", new="true", by=agent_id)
SVC->>DB: UPDATE SystemConfig SET value="true"
DB-->>SVC: 更新成功
SVC-->>API: {key, old_value, new_value, changed_at}
API-->>FE: 返回变更结果
FE->>FE: 显示变更成功提示
@@ -0,0 +1,16 @@
sequenceDiagram
participant U as 管理员(组长)
participant FE as frontend-admin
participant API as /api/agents/login
participant Redis as Redis
participant DB as PostgreSQL
U->>FE: 输入 user_id + name 登录
FE->>API: POST /api/agents/login
API->>DB: 查询 Agent (user_id)
DB-->>API: Agent 记录(含 role 字段)
API->>Redis: 存储 token → user_id 映射
API-->>FE: {agent_info, token, role: "admin"}
FE->>FE: 检查 role === "admin"
FE->>FE: 存储 admin_token 到 localStorage
FE->>FE: 跳转到 /admin/dashboard
@@ -0,0 +1,23 @@
sequenceDiagram
participant A as 坐席(王丽)
participant AG as /api/quick-replies
participant U as 管理员(宋献)
participant ADM as /api/admin/quick-replies
participant DB as PostgreSQL
A->>AG: POST /api/quick-replies (创建模板, status=draft)
A->>AG: PUT /api/quick-replies/{id} (提交审核, status→pending_review)
AG->>DB: UPDATE QuickReplyTemplate SET status='pending_review', submitted_by='wang_li'
U->>ADM: GET /api/admin/quick-replies/pending
ADM->>DB: SELECT WHERE status='pending_review'
DB-->>ADM: 待审核列表
ADM-->>U: 显示待审核模板
U->>ADM: PUT /api/admin/quick-replies/{id}/review (action=approve)
ADM->>DB: UPDATE SET status='approved', version=version+1
DB-->>ADM: 更新成功
A->>AG: GET /api/quick-replies (获取可见模板)
AG->>DB: SELECT WHERE status='approved' OR (status='pending_review' AND submitted_by=自己)
DB-->>AG: 全员可见(approved) + 仅自己(pending_review)
+77
View File
@@ -0,0 +1,77 @@
classDiagram
direction TB
class Employee {
+str id
+str corp_id
+str employee_id
+str name
+str department
+str position
+str mobile
+str email
+str avatar
+int status
+str it_level ★NEW
+str it_level_source ★NEW
+dict notes ★NEW
+datetime last_login_at
+datetime created_at
+datetime updated_at
}
class Conversation {
+str id
+str corp_id
+str employee_id
+str employee_name
+str department
+str status
+bool is_vip
+bool is_pinned
+bool is_todo
+int urgency_score
+dict tags
+str assigned_agent_id
+list collaborating_agent_ids
+int impact_scope ★NEW
+bool is_blocking ★NEW
+str emotion_state ★NEW
+datetime last_message_at
+str last_message_summary
+datetime created_at
+datetime updated_at
}
class TodoItem {
+str id
+str type
+str title
+str priority
+dict description
+str status
+str assigned_agent_id
+str corp_id
+datetime created_at
+datetime updated_at
}
class TroubleshootingTemplate {
+str id
+str name
+str category
+list path_steps
+dict flowchart
+bool is_active
+datetime created_at
+datetime updated_at
}
Employee "1" --> "*" Conversation : has
Conversation "1" --> "*" TodoItem : may generate
TroubleshootingTemplate "1" --> "0..1" Conversation : applied to
note for Employee "it_level: bronze|silver|gold|platinum|diamond|star|king\nit_level_source: system|manual"
note for Conversation "impact_scope: 受影响人数\nis_blocking: 是否阻断性\nemotion_state: normal|anxious|angry|urgent"
note for TodoItem "type: ticket|approval|device\npriority: urgent|high|normal\nstatus: pending|processing|resolved"
note for TroubleshootingTemplate "category: vpn|email|system|account\npath_steps: [{label, status}]\nflowchart: 递归树结构"
+22
View File
@@ -0,0 +1,22 @@
sequenceDiagram
participant U as 坐席
participant TB as TopBar.vue
participant TS as useThemeStore
participant UT as useTheme.ts
participant DOM as document.documentElement
participant LS as localStorage
U->>TB: 点击 ☀️/🌙 切换开关
TB->>TS: toggleTheme()
TS->>TS: currentTheme = currentTheme === 'light' ? 'dark' : 'light'
TS->>UT: applyTheme(currentTheme)
UT->>DOM: setAttribute('data-theme', theme)
UT->>LS: setItem('theme', theme)
DOM-->>DOM: CSS 变量自动切换(:root / [data-theme="dark"]
DOM-->>U: 300ms 过渡动画,界面变色
Note over U,LS: 页面加载时
U->>UT: 首次进入页面
UT->>LS: getItem('theme')
LS-->>UT: 'dark' | 'light' | null
UT->>DOM: setAttribute('data-theme', theme || 'light')
+62
View File
@@ -0,0 +1,62 @@
sequenceDiagram
participant Browser as 坐席浏览器
participant App as Vue3 App
participant Store as Pinia Store
participant API as Backend API
participant DB as PostgreSQL
Note over Browser,App: 页面加载
Browser->>App: 挂载 Workspace.vue
App->>Store: 初始化 conversationStore
Store->>API: GET /api/conversations
API->>DB: SELECT * FROM conversations WHERE status IN ('queued','serving') ORDER BY ...
DB-->>API: 会话列表
API-->>Store: 会话数据
Store-->>App: 渲染会话列表
loop 每3秒 setInterval
App->>Store: pollConversations()
Store->>API: GET /api/conversations?page=1&page_size=50
API->>DB: SELECT ... (同上)
DB-->>API: 最新会话列表
API-->>Store: 最新数据
alt 数据有变化
Store->>Store: diff 比较,更新变化的会话
Store-->>App: 触发响应式更新
App->>App: 更新列表项标签/排序/未读数
else 数据无变化
Store-->>App: 无需更新
end
end
Note over App: 用户点击某个会话
App->>Store: selectConversation(id)
Store->>API: GET /api/conversations/{id}/messages?limit=50
API->>DB: SELECT * FROM messages WHERE conversation_id=... ORDER BY created_at
DB-->>API: 消息列表
API-->>Store: messages
Store-->>App: 渲染对话区
loop 选中会话的消息轮询
App->>Store: pollMessages(conv_id)
Store->>API: GET /api/conversations/{id}/messages?limit=20&before=latest_id
API->>DB: SELECT ... WHERE created_at > latest
DB-->>API: 新消息
API-->>Store: 新消息列表
alt 有新消息
Store->>Store: 追加消息到列表
Store-->>App: 滚动到底部,显示新消息
end
end
Note over App: 坐席发送回复
App->>Store: sendMessage(conv_id, content)
Store->>API: POST /api/conversations/{id}/messages {content}
API->>DB: INSERT INTO messages ...
API-->>Store: 发送的消息对象
Store->>Store: 追加到消息列表
Store-->>App: 显示在对话区
+69
View File
@@ -0,0 +1,69 @@
sequenceDiagram
participant WX as 企微消息
participant API as FastAPI
participant Router as MessageRouter
participant Score as ScoringService
participant Vip as VipService
participant DB as PostgreSQL
participant Redis as Redis
WX->>API: 员工消息回调
API->>Router: route_message(msg)
rect rgb(255, 240, 240)
Note over Router,Score: Step 1: VIP检测
Router->>Vip: is_vip(employee_id)
Vip->>Redis: GET vip_cache:{employee_id}
alt 缓存命中
Redis-->>Vip: is_vip=True/False
else 缓存未命中
Vip->>Vip: get_user_info(employee_id)
Vip->>Vip: _check_vip_rules(user_info)
Note over Vip: 规则: 总监及以上 或 关键部门
Vip->>Redis: SET vip_cache:{employee_id} EX 3600
end
Vip-->>Router: is_vip=True/False
end
rect rgb(255, 255, 220)
Note over Router,Score: Step 2: 情绪关键词检测
Router->>Score: detect_emotion(msg)
Score->>DB: SELECT FROM system_configs WHERE key LIKE 'emotion_keywords_%'
DB-->>Score: 关键词列表
Score->>Score: 遍历关键词匹配消息内容
Score-->>Router: emotion="urgent"
end
rect rgb(220, 255, 220)
Note over Router,Score: Step 3: 举手检测
Router->>Score: detect_hand_raise(msg)
Score->>DB: SELECT FROM system_configs WHERE key='hand_raise_keywords'
DB-->>Score: ["转人工","人工",...]
Score->>Score: 遍历关键词匹配
Score-->>Router: hand_raise=True
end
rect rgb(220, 220, 255)
Note over Router,Score: Step 4: 需介入检测
Router->>Score: detect_need_intervene(conv)
Score->>DB: SELECT COUNT FROM messages WHERE conversation_id=... AND sender_type='employee'
DB-->>Score: 员工消息数
Score->>DB: SELECT FROM system_configs WHERE key='intervene_round_threshold'
DB-->>Score: 3
Score->>Score: 员工连续追问 > 3轮?
Score-->>Router: need_intervene=True
end
rect rgb(255, 220, 255)
Note over Router,Score: Step 5: 紧急度计算
Router->>Score: calculate_urgency(conv, msg)
Score->>Score: base = keyword_score(1)
Score->>Score: + emotion_bonus(1)
Score->>Score: + vip_bonus(1)
Score->>Score: + repeat_bonus(1)
Score->>Score: total = min(5, base+emotion+vip+repeat)
Score-->>Router: urgency_score=4
end
Router->>DB: UPDATE conversations SET tags=..., urgency_score=4, is_vip=...
Router-->>API: 更新后的会话
+43
View File
@@ -0,0 +1,43 @@
sequenceDiagram
participant Emp as 员工(H5页面)
participant API as FastAPI
participant ConvSvc as ConversationService
participant Score as ScoringService
participant WXSvc as WecomService
participant Agent as 坐席工作台
participant DB as PostgreSQL
Emp->>API: POST /api/h5/conversation/shake
API->>ConvSvc: find_or_create_conversation(employee_id)
ConvSvc->>DB: 查询/创建 conversation
DB-->>ConvSvc: conversation
API->>Score: detect_hand_raise("摇人")
Score-->>API: hand_raise=True
API->>ConvSvc: update_conversation(tags={hand_raise:true})
ConvSvc->>DB: UPDATE conversations SET tags=...
API->>ConvSvc: get_funny_phrase(scene='shake')
ConvSvc->>DB: SELECT FROM funny_phrases WHERE scene='shake'
DB-->>ConvSvc: "大哥,俺这就去摇人,稍等..."
ConvSvc->>ConvSvc: send_message(conv_id, 'system', 趣味话术)
ConvSvc->>WXSvc: send_text_message(employee_id, 趣味话术)
API-->>Emp: {conversation, funny_phrase}
Note over Emp: H5显示摇人动画 + 趣味话术
loop 坐席轮询
Agent->>API: GET /api/conversations
API->>ConvSvc: get_conversations()
ConvSvc->>DB: SELECT ... ORDER BY urgency DESC
DB-->>ConvSvc: 列表(举手会话靠前)
API-->>Agent: 举手标记的会话(黄色标签)
end
Agent->>API: POST /api/conversations/{id}/assign {agent_id}
API->>ConvSvc: update_conversation(status='serving', assigned_agent_id)
ConvSvc->>DB: UPDATE conversations SET status='serving'
ConvSvc->>ConvSvc: send_message(conv_id, 'system', '人摇来了!IT坐席为您服务')
ConvSvc->>WXSvc: send_text_message(employee_id, 接入话术)
ConvSvc-->>API: updated conversation
API-->>Agent: 接单成功
WXSvc-->>Emp: 企微推送"人摇来了!IT坐席为您服务"