WIP-CHECKPOINT[auth-refactor]: 固化工程师崩溃前部分成果 + 同树其他未提交WIP(仅源码,不含密钥/二进制)-- 待重激活工程师续作

This commit is contained in:
Simon
2026-07-07 21:52:11 +08:00
parent 242c1967ff
commit fab75760e0
203 changed files with 21504 additions and 3345 deletions
@@ -552,6 +552,422 @@ ALTER TABLE agents ADD COLUMN otp_bound_at TIMESTAMP DEFAULT NULL;
---
## 15. 阶段5 自动化闭环
> **新增日期**: 2026-07-05 | **架构师**: 高见远 (Gao) | **状态**: 设计完成(待实现)
> **范围**: 在阶段1-4 基础上新增自动化闭环能力——意图识别与场景路由、员工↔终端映射、知识库自助应答、自动化处置执行(双模式)、审批与审计、转人工兜底、自动关单、管理后台配置、实时进度推送、指标看板。
> **技术栈**: FastAPI + SQLAlchemy + PostgreSQL + Redis / Vue3Element Plus / Vant4 / Element+Tailwind)三端。
### 15.1 实现方案与框架选型
#### 15.1.1 核心难点
| 难点 | 说明 | 对策 |
|------|------|------|
| 多外部系统集成 | 火绒(HMAC-SHA1)/联软(三层认证)/Dify/RAGFlow/北森eHR 认证与协议各异 | 抽象 `BaseClient` 统一超时/重试/审计;`ActionRegistry` 按动作类型注册适配器 |
| 风险分级执行 | 只读/低风险自动执行,写/高危需审批或员工二次确认 | 执行引擎 `Executor` 双模式(plan-only / real-exec),`risk_level` 驱动分支 |
| 员工↔终端映射 | 多源、需优先级与兜底 | `MappingResolver`:联软(主) > aTrust(VPN辅,后置) > eHR(静态),结果缓存 `MappingCache` |
| 实时进度 | H5/坐席需秒级看到处置进展 | 复用阶段2 WebSocket,新增 `automation.*` 事件族,由 `ProgressPublisher` 统一发布 |
| 自动关单 | 成功+员工已解决 或 静默10min 无异议 | Redis TTL + 后台任务触发,复用阶段2满意度 |
#### 15.1.2 选型(沿用现有栈,仅新增必要依赖)
- **后端**FastAPI 路由 + Pydantic Schema + SQLAlchemy 模型 + Alembic 迁移;异步 HTTP 用 `httpx`(若未引入);重试用 `tenacity`
- **外部客户端**:自研 `app/core/clients/*`,统一封装 HMAC 签名与三层认证,**不引入重型 SDK**。
- **前端三端**:沿用 Vue3 组合式 API + Pinia + 现有 axios/WebSocket 封装,**不新增 npm 包**。
- **可视化编排引擎**:本期用管理后台**结构化简易配置**(场景开关+触发条件+动作+审批策略),编排引擎列 P2。
- **aTrust VPN 自动化**:密钥未到,列 P2-04 后置,不影响首期。
#### 15.1.3 架构分层
```
[三端前端] ──HTTP/WS──> [FastAPI /itportal/automation]
┌───────────────┼───────────────────────┐
[api/automation] [services/automation] [core/clients]
(路由+WS端点) (会话/意图/映射/执行/ (火绒/联软/Dify/
审批/进度/回滚/异常) RAGFlow/eHR)
[models/automation] ──SQLAlchemy──> PostgreSQL
[Redis] 会话态/映射缓存/静默TTL
```
### 15.2 文件列表及相对路径(标注 新增/修改 + 职责)
#### 15.2.1 后端 `backend/`
| 路径 | 状态 | 职责 |
|------|------|------|
| `app/core/config.py` | 修改 | 新增自动化配置键(Dify/RAGFlow/火绒/联软/eHR 基址、密钥占位、阈值默认) |
| `app/core/constants.py` | 修改 | 新增 WS 事件名 `AUTOMATION_*`、错误码段 `AUT-*` |
| `app/core/clients/__init__.py` | 新增 | 客户端包导出 |
| `app/core/clients/base.py` | 新增 | 带超时/重试/审计的异步 `BaseClient` |
| `app/core/clients/huorong.py` | 新增 | 火绒 HMAC-SHA1`_leak` / `_virus_events` / 病毒隔离(写) |
| `app/core/clients/lianruan.py` | 新增 | 联软 LV7000 三层认证,`strusername` 员工↔终端映射(读) |
| `app/core/clients/dify.py` | 新增 | Dify 意图识别 / AI 编排 |
| `app/core/clients/ragflow.py` | 新增 | RAGFlow 知识库检索(`:9380`) |
| `app/core/clients/ehr.py` | 新增 | 北森 eHR 静态映射兜底 |
| `app/models/automation.py` | 新增 | AutoSession / AutoAction / ApprovalTicket / ScenarioConfig / ActionLog / RuleVersion / MappingCache |
| `migrations/versions/xxxx_automation.py` | 新增 | Alembic 迁移建表 |
| `app/schemas/automation.py` | 新增 | 请求/响应 Pydantic Schema |
| `app/dependencies/automation.py` | 新增 | 场景配置加载、WS 连接鉴权、审批权限(OTP仅admin配置) |
| `app/services/automation/__init__.py` | 新增 | 服务包导出 |
| `app/services/automation/session_manager.py` | 新增 | 会话生命周期(创建/状态机/关单判定) |
| `app/services/automation/intent_router.py` | 新增 | 意图识别 + 场景路由(Dify+RAGFlow |
| `app/services/automation/mapping_resolver.py` | 新增 | 员工↔终端映射解析(联软>eHR) |
| `app/services/automation/executor.py` | 新增 | 处置执行引擎(双模式、风险分级、动作编排) |
| `app/services/automation/action_registry.py` | 新增 | 动作适配器注册(火绒/联软;aTrust 占位) |
| `app/services/automation/approval.py` | 新增 | 审批单创建/流转/审计 |
| `app/services/automation/progress_publisher.py` | 新增 | WS 进度统一发布 |
| `app/services/automation/rollback.py` | 新增(P1) | 处置失败回滚/补偿 |
| `app/services/automation/exception_handler.py` | 新增(P1) | 异常自动转人工 + 通知 |
| `app/api/automation.py` | 新增 | REST 路由 + WS 端点 |
| `app/main.py` | 修改 | 注册 `automation` router 与 WS 路由 |
#### 15.2.2 前端 H5(员工端)`frontend-h5/src/`
| 路径 | 状态 | 职责 |
|------|------|------|
| `api/automation.js` | 新增 | 自动化会话/确认/已解决接口 |
| `views/AutomationProgress.vue` | 新增 | 自动化进度页(WS 实时进展) |
| `components/ActionConfirmDialog.vue` | 新增(P1) | 员工侧高危动作二次确认 |
| `components/ResolveFeedback.vue` | 新增 | 「已解决」反馈 / 静默关单提示 |
| `store/automation.js` | 新增 | Pinia 自动化状态 |
#### 15.2.3 前端 坐席端 `frontend-agent/src/`
| 路径 | 状态 | 职责 |
|------|------|------|
| `api/automation.js` | 新增 | 会话/审批/接管接口 |
| `views/automation/SessionWorkbench.vue` | 新增 | 自动化会话工作台 |
| `components/automation/ActionApprovalCard.vue` | 新增 | 坐席审批卡片 |
| `components/automation/TakeoverPanel.vue` | 新增 | 转人工/接管面板 |
| `store/automation.js` | 新增 | Pinia 状态 |
#### 15.2.4 前端 管理后台 `frontend-admin/src/`
| 路径 | 状态 | 职责 |
|------|------|------|
| `api/automation.js` | 新增 | 配置/版本/指标接口 |
| `views/automation/ScenarioConfig.vue` | 新增 | 场景开关+触发条件+动作+审批策略 |
| `views/automation/RuleVersion.vue` | 新增(P1) | 规则版本管理/灰度 |
| `views/dashboard/AutoMetrics.vue` | 新增 | 指标看板(扩展阶段4) |
| `store/automation.js` | 新增 | Pinia 状态 |
### 15.3 数据结构和接口(Mermaid 类图)
```mermaid
classDiagram
class ScenarioConfig {
+int id
+str name
+bool enabled
+dict trigger_conditions
+dict actions
+dict approval_policy
+int version
+int gray_pct
+datetime created_at
+datetime updated_at
+int created_by
}
class AutoSession {
+int id
+int ticket_id
+str employee_id
+str intent
+float intent_confidence
+int scenario_config_id
+str status
+str mode
+str current_step
+bool takeover_flag
+bool auto_close_flag
+datetime created_at
+datetime updated_at
}
class AutoAction {
+int id
+int session_id
+str type
+str target
+dict params
+str mode
+str risk_level
+str status
+bool is_approved
+dict result
+str error_msg
+bool rolled_back
+datetime executed_at
}
class ApprovalTicket {
+int id
+int action_id
+int session_id
+int approver_id
+str employee_id
+str type
+str status
+datetime requested_at
+datetime resolved_at
+str resolution
}
class ActionLog {
+int id
+int session_id
+int action_id
+str actor
+str event
+dict detail
+datetime created_at
}
class RuleVersion {
+int id
+int scenario_config_id
+int version
+dict snapshot
+int gray_pct
+str status
+datetime created_at
}
class MappingCache {
+int id
+str employee_id
+str terminal_id
+str source
+float confidence
+datetime updated_at
}
ScenarioConfig "1" --> "0..*" RuleVersion : has versions
ScenarioConfig "1" --> "0..*" AutoSession : routes
AutoSession "1" --> "0..*" AutoAction : produces
AutoSession "1" --> "0..*" ActionLog : logs
AutoAction "1" --> "0..1" ApprovalTicket : requires
MappingCache "1" --> "0..*" AutoSession : used by
```
#### 15.3.1 核心 API 端点(前缀 `/itportal/automation`
| 方法 | 路径 | 说明 | 角色 |
|------|------|------|------|
| POST | `/sessions/start` | 员工提交意图,创建自动化会话 | employee |
| GET | `/sessions/{id}` | 会话状态/进度快照 | employee/agent |
| POST | `/sessions/{id}/takeover` | 转人工/接管(命中阈值或主动) | agent |
| GET | `/actions/{id}` | 动作状态 | employee/agent |
| POST | `/actions/{id}/approve` | 坐席审批(写/高危) | agent |
| POST | `/actions/{id}/confirm` | 员工二次确认(P1 高危) | employee |
| POST | `/sessions/{id}/resolved` | 员工标记已解决(触发关单) | employee |
| GET | `/configs` | 场景配置列表 | admin(OTP) |
| POST | `/configs` | 新建场景配置 | admin(OTP) |
| PUT | `/configs/{id}` | 修改场景配置 | admin(OTP) |
| POST | `/configs/{id}/version` | 版本快照/灰度发布(P1) | admin(OTP) |
| GET | `/metrics` | 自动化指标(扩展阶段4看板) | admin |
| WS | `/ws/{session_id}` | 实时进度推送 | employee/agent |
### 15.4 程序调用流程(Mermaid 时序图,全链路 + WS 推送)
```mermaid
sequenceDiagram
participant H5 as 员工H5
participant WS as WebSocket网关
participant API as Automation API
participant IR as IntentRouter(Dify+RAGFlow)
participant MR as MappingResolver(联软/eHR)
participant EX as Executor(执行引擎)
participant AP as Approval(审批)
participant PP as ProgressPublisher
participant T2 as 阶段2工单/满意度
H5->>API: POST /sessions/start {intent_text, employee_id}
API->>IR: recognize(intent_text)
IR->>IR: Dify意图识别 + RAGFlow检索
IR-->>API: {intent, confidence, knowledge}
API->>MR: resolve(employee_id)
MR->>MR: 联软(主)>eHR(兜底) 映射
MR-->>API: {terminal_id, source}
API->>EX: plan(scenario_config, intent, mapping)
alt 自助应答(密码重置/软件安装指引)
EX-->>API: knowledge answer
API->>PP: publish(progress=answered)
PP-->>WS: automation.progress
WS-->>H5: 展示方案
H5->>API: POST /sessions/{id}/resolved
else 自动处置(病毒隔离/终端定位)
EX->>EX: 生成AutoAction + 风险分级
alt 低风险(仅出方案/读操作)
EX->>EX: 执行 action
EX->>PP: publish(progress=executed)
else 高风险(写操作/高危)
EX->>AP: create ApprovalTicket
AP->>PP: publish(action_required)
alt 坐席审批
PP-->>WS: automation.action_required
WS-->>Agent: 通知
Agent->>API: POST /actions/{id}/approve
else 员工二次确认(P1)
PP-->>WS: automation.action_required
WS-->>H5: 弹窗
H5->>API: POST /actions/{id}/confirm
end
AP->>EX: execute approved action
end
EX->>PP: publish(progress=result)
end
PP-->>WS: automation.progress / resolved
WS-->>H5: 进度/结果
API->>API: 关单判定(成功+已解决 或 静默10min)
API->>T2: 复用满意度收集(阶段2)
T2-->>H5: 满意度推送
```
> 阈值转人工(Q3):意图置信度<0.6 / 处置超时60s / 命中高危必转 / 员工主动转 / 连续「未解决」≥2次 → `exception_handler` / `session_manager` 触发 `automation.takeover` 事件并落入坐席队列。
### 15.5 有序任务列表(依赖关系 + 实现顺序,对应 P0/P1,P2 标注后置)
> 任务上限 5 个、每任务≥3 文件、T01 为基础设施;T03/T04/T05 平行依赖 T02,减少线性链。
#### T01 项目基础设施与公共能力(无依赖,P0)
- 源文件:`app/core/config.py`(改)、`app/core/constants.py`(改)、`app/core/clients/{__init__,base,huorong,lianruan,dify,ragflow,ehr}.py`(新)、`app/models/automation.py`(新)、`migrations/versions/xxxx_automation.py`(新)
- 依赖:无 | 优先级:P0
- 交付:配置键、WS 事件/错误码常量、5 个外部客户端封装、7 张表模型与迁移
#### T02 自动化核心服务(依赖 T01P0/P1)
- 源文件:`app/schemas/automation.py`(新)、`app/dependencies/automation.py`(新)、`app/services/automation/{__init__,session_manager,intent_router,mapping_resolver,executor,action_registry,approval,progress_publisher,rollback,exception_handler}.py`(新)
- 依赖:T01 | 优先级:P0rollback/exception_handler 为 P1
- 交付:意图路由、映射解析、双模式执行引擎、审批、进度发布、回滚补偿(P1)、异常转人工(P1)
#### T03 后端 API + 坐席端工作台(依赖 T02,P0)
- 源文件:`app/api/automation.py`(新)、`app/main.py`(改)、`frontend-agent/src/{api/automation.js, views/automation/SessionWorkbench.vue, components/automation/ActionApprovalCard.vue, components/automation/TakeoverPanel.vue, store/automation.js}`(新)
- 依赖:T02 | 优先级:P0
- 交付:REST+WS 端点、坐席审批/接管/工作台
#### T04 H5 员工端交互(依赖 T02P0/P1)
- 源文件:`frontend-h5/src/{api/automation.js, views/AutomationProgress.vue, components/ActionConfirmDialog.vue, components/ResolveFeedback.vue, store/automation.js}`(新)
- 依赖:T02 | 优先级:P0ActionConfirmDialog 二次确认为 P1
- 交付:进度页、员工二次确认(P1)、已解决反馈、静默关单
#### T05 管理后台配置 + 指标看板(依赖 T02,P0/P1
- 源文件:`frontend-admin/src/{api/automation.js, views/automation/ScenarioConfig.vue, views/automation/RuleVersion.vue, views/dashboard/AutoMetrics.vue, store/automation.js}`(新)
- 依赖:T02 | 优先级:P0RuleVersion 灰度为 P1
- 交付:场景开关/触发条件/动作/审批策略配置、规则版本灰度(P1)、指标看板(扩展阶段4)
#### P2 后置任务(本期不排期,预留接口)
- P2-01 可视化工作流编排引擎(替代结构化简易配置)
- P2-02 自学习场景优化
- P2-03 权限申请自动化
- P2-04 aTrust VPN 自动化(密钥到位后;`action_registry` 已留占位)
#### 15.5.1 任务依赖图
```mermaid
graph TD
T01[T01 基础设施与公共能力] --> T02[T02 自动化核心服务]
T02 --> T03[T03 后端API+坐席端]
T02 --> T04[T04 H5员工端交互]
T02 --> T05[T05 管理后台配置+看板]
```
### 15.6 依赖包列表(新增)
**后端 pip**(若尚未引入):
```
- httpx>=0.27.0 # 异步 HTTP 客户端(调外部系统)
- tenacity>=8.2.0 # 重试/退避(外部调用健壮性)
- pydantic>=2.0 # 已有,Schema 校验(确认版本一致)
```
> HMAC 用标准库 `hmac`/`hashlib`Redis/PostgreSQL/SQLAlchemy 阶段1-4 已具备,无需新增。
**前端 npm**:三端复用现有 `axios` + WebSocket 封装 + `vant`/`element-plus`**本期无强制新增包**。
### 15.7 共享知识(跨文件约定)
- **统一响应**`{code, msg, data}`,成功 `code=0`;自动化错误码段 `AUT-001`~`AUT-0xx`(意图识别失败/映射缺失/执行超时/审批拒绝等)。
- **WS 事件名**(前缀 `automation.`):`automation.progress`(进度)、`automation.action_required`(需审批/确认)、`automation.resolved`(已解决/关单)、`automation.takeover`(转人工)、`automation.error`
- **配置键**(前缀 `AUTOMATION_`):`DIFY_BASE_URL`/`DIFY_KEY``RAGFLOW_BASE_URL``HUORONG_*`(HMAC-SHA1)、`LIANRUAN_*`(三层认证)、`EHR_*``AUTOMATION_THRESHOLDS`(置信度0.6/超时60s/未解决≥2)。
- **映射源常量**`MAPPING_SOURCES = ["lianruan", "atrust", "ehr"]`,优先级顺序固定。
- **表/路由命名**:表前缀 `auto_`API 前缀 `/itportal/automation`;服务类后缀 `Service`/函数式模块。
- **日志规范**:结构化日志含 `session_id`/`action_id`/`employee_id`/`event`;所有外部调用出入参落 `ActionLog`(审计可追溯)。
- **风险分级**`risk_level ∈ {read, low, high}``read/low` 默认可自动执行,`high` 必走审批或员工二次确认。
- **OTP 适用范围**:仅 admin 配置类接口(新建/修改/版本)需 OTP 双因素;坐席审批与普通会话不需 OTP。
- **静默关单**`AutoSession` 成功后写 Redis TTL=600s,到期无 `resolved` 异议则自动关单;员工主动 `resolved` 立即关单。
### 15.8 待明确事项(仅技术层面,业务决策已确认)
1. **Dify 返回结构**:意图字段名与置信度字段名需联调确认(影响 `IntentRouter` 解析)。
2. **RAGFlow 检索策略**:结果分页/截断/Top-K 与引用来源展示方式。
3. **火绒写操作细节**:HMAC-SHA1 构造、沙箱环境、病毒隔离接口字段与回执。
4. **联软 LV7000**:三层认证具体字段、超时与并发限制。
5. **AutoSession 与阶段2 工单(Ticket)关系**:建议**弱关联**(session 可独立存在,`ticket_id` 可空;关单时复用阶段2满意度),需确认是否强制绑定。
6. **静默10分钟关单机制**Redis TTL + 后台任务 vs 轮询,确认后台任务调度方式(APScheduler / FastAPI BackgroundTasks / Redis 键空间通知)。
7. **规则灰度(P1)**:按比例灰度还是白名单灰度,发布回滚流程。
8. **审批并发**:同一 `AutoAction` 坐席审批与员工二次确认是否互斥、超时未处理如何降级转人工。
### 15.9 建议文件变更清单(落盘指引)
**文档(合并进已有文件,不新建独立文档)**
| 路径 | 状态 | 落盘建议 | 已有文件 |
|------|------|----------|----------|
| `docs/03-技术架构/00-系统架构设计文档-v1.3.md` | 修改 | 文末新增「阶段5 自动化闭环」章节(即本章) | 是 |
**后端**
| 路径 | 状态 | 落盘建议 | 已有文件 |
|------|------|----------|----------|
| `app/core/config.py` | 修改 | 追加自动化配置键 | 是 |
| `app/core/constants.py` | 修改 | 追加 WS 事件/错误码常量 | 是 |
| `app/core/clients/{__init__,base,huorong,lianruan,dify,ragflow,ehr}.py` | 新增 | 整组新建 | 否 |
| `app/models/automation.py` | 新增 | 整文件新建 | 否 |
| `migrations/versions/xxxx_automation.py` | 新增 | Alembic 生成并落地 | 否 |
| `app/schemas/automation.py` | 新增 | 整文件新建 | 否 |
| `app/dependencies/automation.py` | 新增 | 整文件新建 | 否 |
| `app/services/automation/*.py`(10个) | 新增 | 整组新建 | 否 |
| `app/api/automation.py` | 新增 | 整文件新建 | 否 |
| `app/main.py` | 修改 | 注册 router/WS | 是 |
**前端 H5**
| 路径 | 状态 | 落盘建议 | 已有文件 |
|------|------|----------|----------|
| `frontend-h5/src/api/automation.js` | 新增 | 新建 | 否 |
| `frontend-h5/src/views/AutomationProgress.vue` | 新增 | 新建 | 否 |
| `frontend-h5/src/components/ActionConfirmDialog.vue` | 新增 | 新建 | 否 |
| `frontend-h5/src/components/ResolveFeedback.vue` | 新增 | 新建 | 否 |
| `frontend-h5/src/store/automation.js` | 新增 | 新建 | 否 |
**前端 坐席端**
| 路径 | 状态 | 落盘建议 | 已有文件 |
|------|------|----------|----------|
| `frontend-agent/src/api/automation.js` | 新增 | 新建 | 否 |
| `frontend-agent/src/views/automation/SessionWorkbench.vue` | 新增 | 新建 | 否 |
| `frontend-agent/src/components/automation/ActionApprovalCard.vue` | 新增 | 新建 | 否 |
| `frontend-agent/src/components/automation/TakeoverPanel.vue` | 新增 | 新建 | 否 |
| `frontend-agent/src/store/automation.js` | 新增 | 新建 | 否 |
**前端 管理后台**
| 路径 | 状态 | 落盘建议 | 已有文件 |
|------|------|----------|----------|
| `frontend-admin/src/api/automation.js` | 新增 | 新建 | 否 |
| `frontend-admin/src/views/automation/ScenarioConfig.vue` | 新增 | 新建 | 否 |
| `frontend-admin/src/views/automation/RuleVersion.vue` | 新增 | 新建 | 否 |
| `frontend-admin/src/views/dashboard/AutoMetrics.vue` | 新增 | 新建(扩展阶段4看板) | 否 |
| `frontend-admin/src/store/automation.js` | 新增 | 新建 | 否 |
> 汇总:文档 1 处合并修改(本章);代码新增约 35 个文件(后端 21 + 三前端 14),修改 4 个已有文件(config/constants/main + 设计文档)。代码文件按此清单在后续实现阶段落地。
---
## 附录
### A. 项目阶段规划
@@ -0,0 +1,232 @@
# 技术方案:消息推送策略优化与超时提醒
> **需求来源**2026-07-05 产品讨论
> **版本**v1.0
> **状态**:✅ 已开发完成
---
## 一、需求概述
### 1.1 业务背景
当前坐席回复用户消息时,会同时走两个通道:
1. **WebSocket** → 推送到 H5 页面
2. **企微应用消息** → 推送到"IT支持服务"应用的消息列表
这导致两种场景混在一起:
- 场景A:员工找坐席(一对一私密对话)→ 期望只走 H5
- 场景B:IT支持组群发通知 → 期望走企微消息
### 1.2 产品需求
| 需求 | 描述 |
|------|------|
| R1 | 正常对话:坐席回复仅推送到 H5 页面(WebSocket |
| R2 | 坐席回复后员工 3 分钟(可配置)未回复,发送企微提醒消息 |
| R3 | 提醒消息只发 1 次 |
| R4 | 10 分钟后自动标记会话为"待关闭"状态 |
| R5 | 提醒文案固定(见 1.3) |
### 1.3 提醒文案
```
IT服务提醒:您有新的消息未查看,咨询将在10分钟后标记为待关闭,请尽快点击处理 👉 https://itsupport.servyou.com.cn/itdesk/
```
---
## 二、技术方案
### 2.1 架构设计
```
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ 坐席发送 │────▶│ WebSocket │────▶│ H5页面 │
│ 消息 │ │ (仅推送H5) │ │ (实时可见) │
└─────────────┘ └──────────────┘ └─────────────────┘
▼ (触发条件)
┌─────────────────────────────────────────┐
│ 后台定时任务 (每30秒) │
│ • 检查超时未回复会话 │
│ • 发送企微提醒消息 │
│ • 标记会话状态 │
└─────────────────────────────────────────┘
```
### 2.2 数据库改动
#### 2.2.1 conversations 表新增字段
```sql
ALTER TABLE conversations
ADD COLUMN IF NOT EXISTS last_agent_reply_at TIMESTAMP DEFAULT NULL,
ADD COLUMN IF NOT EXISTS reminder_sent BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS reminder_sent_at TIMESTAMP DEFAULT NULL,
ADD COLUMN IF NOT EXISTS pending_close_at TIMESTAMP DEFAULT NULL;
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `last_agent_reply_at` | TIMESTAMP | 坐席最后回复时间 |
| `reminder_sent` | BOOLEAN | 是否已发送提醒 |
| `reminder_sent_at` | TIMESTAMP | 提醒发送时间 |
| `pending_close_at` | TIMESTAMP | 待关闭时间(最后回复+10分钟) |
#### 2.2.2 配置表(可选)
在系统配置表中添加:
| key | default | 说明 |
|-----|---------|-----|
| `reminder.timeout_minutes` | 3 | 未回复超时时间(分钟) |
| `reminder.close_minutes` | 10 | 自动待关闭时间(分钟) |
| `reminder.enabled` | true | 是否启用提醒功能 |
### 2.3 后端改动
#### 2.3.1 消息发送逻辑修改
**文件**`backend/app/api/messages.py`
```python
# 坐席发送消息时
async def send_message(...):
# 1. 仅通过 WebSocket 推送到 H5(不再调用企微 API)
await manager.send_to_employee(conversation.employee_id, ws_event)
# 2. 更新会话的最后坐席回复时间
conversation.last_agent_reply_at = datetime.now()
conversation.reminder_sent = False # 重置提醒标记
conversation.pending_close_at = datetime.now() + timedelta(minutes=10)
```
#### 2.3.2 新增定时任务
**文件**`backend/app/tasks/reminder_task.py`(新建)
```python
# 每 30 秒执行一次
@scheduler.scheduled_job('interval', seconds=30)
async def check_unreplied_sessions():
"""检查超时未回复的会话,发送提醒"""
# 1. 查找需要处理的会话
sessions = await db.execute(select(Conversation).where(
Conversation.status == 'active',
Conversation.last_agent_reply_at.isnot(None),
Conversation.reminder_sent == False,
Conversation.last_agent_reply_at < (datetime.now() - timedelta(minutes=3))
))
for session in sessions:
# 2. 发送企微提醒消息
await send_reminder_message(session)
# 3. 标记已发送
session.reminder_sent = True
session.reminder_sent_at = datetime.now()
# 4. 处理待关闭会话
pending = await db.execute(select(Conversation).where(
Conversation.status == 'active',
Conversation.pending_close_at < datetime.now()
))
for session in pending:
session.status = 'pending_close' # 待关闭状态
await db.commit()
```
#### 2.3.3 提醒消息发送函数
**文件**`backend/app/services/reminder_service.py`(新建)
```python
async def send_reminder_message(conversation: Conversation):
"""发送超时提醒企微消息"""
message = "IT服务提醒:您有新的消息未查看,咨询将在10分钟后标记为待关闭,请尽快点击处理 👉 https://itsupport.servyou.com.cn/itdesk/"
redis_client = settings.create_redis_client()
wecom_service = WecomService(redis_client)
try:
await wecom_service.send_text_message(
conversation.employee_id,
message
)
finally:
await wecom_service.close()
await redis_client.close()
```
### 2.4 前端改动
#### 2.4.1 坐席端(无需改动)
当前坐席发送消息功能保持不变,后端会自动处理推送逻辑。
#### 2.4.2 H5 端(无需改动)
WebSocket 接收消息逻辑保持不变。
### 2.5 部署配置
#### 2.5.1 后端定时任务启动
`backend/app/main.py` 中注册定时任务:
```python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
scheduler = AsyncIOScheduler()
scheduler.add_job(check_unreplied_sessions, 'interval', seconds=30)
scheduler.start()
```
---
## 三、实现情况
| 任务 | 状态 | 文件位置 |
|------|------|----------|
| 数据库迁移 | ✅ 已完成 | `backend/migrations/versions/001_add_reminder_fields.sql` |
| 数据模型更新 | ✅ 已完成 | `backend/app/models/conversation.py` |
| 消息发送逻辑修改 | ✅ 已完成 | `backend/app/api/messages.py` |
| 新建提醒服务 | ✅ 已完成 | `backend/app/services/reminder_service.py` |
| 新建定时任务 | ✅ 已完成 | `backend/app/tasks/reminder_task.py` |
| 定时任务注册 | ✅ 已完成 | `backend/app/main.py` |
---
## 四、上线前置条件
1. 在生产数据库执行迁移脚本 `001_add_reminder_fields.sql`
2. 重启后端服务以加载定时任务
---
## 五、风险与注意事项
1. **定时任务并发**:多实例部署时需确保任务不重复执行(建议加分布式锁)
2. **历史数据**:已存在的会话不受影响,新逻辑仅对新增会话生效
3. **配置灵活性**:当前为固定值,后续可扩展为可配置
---
## 六、相关文件清单
| 文件 | 操作 |
|------|------|
| `backend/app/api/messages.py` | 修改 |
| `backend/app/services/reminder_service.py` | 已存在 |
| `backend/app/tasks/reminder_task.py` | 已存在 |
| `backend/app/main.py` | 修改 |
| `docs/03-技术架构/02-技术方案/技术方案-消息推送策略优化与超时提醒.md` | 本文档 |
---
*最后更新:2026-07-05 18:20*