feat(ops): update cleaner status and assignment logic
This commit is contained in:
174
docs/ops-architecture/README.md
Normal file
174
docs/ops-architecture/README.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Ops 业务运营模块架构文档
|
||||
|
||||
> **文档定位**:Ops 模块的深度架构设计与演进文档
|
||||
> **维护策略**:按架构演进版本记录,保留历史设计决策(ADR)
|
||||
> **最后更新**:2026-01-14
|
||||
> **文档版本**:v2.1.0
|
||||
|
||||
---
|
||||
|
||||
## 📖 文档说明
|
||||
|
||||
### Ops 模块简介
|
||||
|
||||
**Ops(Operations)业务运营模块**是 AIoT Platform 的核心业务模块,承载万物梁行的四大业务线:
|
||||
|
||||
| 业务线 | 代码模块 | 业务范围 |
|
||||
|--------|----------|----------|
|
||||
| **保洁** | `environment-biz` | 排班、巡检、耗材、智能调度 |
|
||||
| **安保** | `security-biz` | 巡更、报警、岗位管理 |
|
||||
| **工程** | `facilities-biz` | 维保、巡检、备件管理 |
|
||||
| **客服** | `service-biz` | 投诉、建议、满意度 |
|
||||
|
||||
**核心能力**:
|
||||
- 工单引擎(基于状态机)
|
||||
- 智能调度系统(自动派单、优先级队列)
|
||||
- IoT 设备集成(工牌、信标、传感器)
|
||||
- 绩效评价系统
|
||||
|
||||
### 文档结构
|
||||
|
||||
本文档分为 10 个部分,从架构总览到具体实现,全方位记录 Ops 模块的设计与演进。
|
||||
|
||||
```
|
||||
Part 1: [架构总览](./part1-架构总览.md) - 业务背景、系统架构、核心概念
|
||||
Part 2: [架构演进史](./part2-架构演进史.md) - 从阶段1到阶段3的演进历程
|
||||
Part 3: [核心架构设计](./part3-核心架构设计.md) - 服务职责、分层设计、技术方案
|
||||
Part 4: [关键技术方案](./part4-关键技术方案.md) - 状态机、队列、派单、事件驱动、P0打断
|
||||
Part 5: [架构重构实践](./part5-架构重构实践.md) - 问题识别、重构方案、实施过程
|
||||
Part 6: [性能与可靠性](./part6-性能与可靠性.md) - 优化、并发、可靠性保障
|
||||
Part 7: [扩展性设计](./part7-扩展性设计.md) - 如何新增业务类型、扩展策略
|
||||
Part 8: [测试指南](./part8-测试指南.md) - 单元测试、集成测试、性能测试
|
||||
Part 9: [技术决策记录(ADR)](./part9-技术决策记录.md) - 记录重要技术决策
|
||||
Part 10: [附录](./part10-附录.md) - 类图、时序图、术语表、变更日志
|
||||
```
|
||||
|
||||
### 适用人群
|
||||
|
||||
- **Ops 模块开发者**:了解架构设计,参与功能开发
|
||||
- **架构师**:理解设计决策,参与架构评审
|
||||
- **技术负责人**:掌握技术债务,规划重构方向
|
||||
- **新成员**:快速了解 Ops 模块的全貌
|
||||
|
||||
### 版本管理
|
||||
|
||||
| 版本 | 日期 | 主要内容 |
|
||||
|------|------|----------|
|
||||
| v1.0 | 2025-12 | 阶段2完成(工单引擎、状态机、事件溯源) |
|
||||
| v2.0 | 2026-01 | 阶段3规划(智能调度系统) |
|
||||
| v2.1 | 2026-01-06 | 架构重构方案(重新定义组件职责) |
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ 快速导航
|
||||
|
||||
### 我想知道...
|
||||
|
||||
**业务相关**:
|
||||
- [Ops 模块有哪些业务线?](./part1-架构总览.md#14-四大业务线)
|
||||
- [保洁工单的完整流程是什么?](./part1-架构总览.md#13-核心业务流程)
|
||||
- [如何处理紧急任务打断?](./part4-关键技术方案.md#45-p0-紧急任务打断)
|
||||
|
||||
**架构相关**:
|
||||
- [Ops 模块的整体架构是什么?](./part1-架构总览.md#12-系统架构)
|
||||
- [服务职责如何划分?](./part3-核心架构设计.md#31-服务职责划分)
|
||||
- [为什么要重构 DispatchEngine?](./part5-架构重构实践.md#51-问题识别)
|
||||
|
||||
**技术实现**:
|
||||
- [状态机如何实现?](./part4-关键技术方案.md#41-状态机实现详解)
|
||||
- [Redis + MySQL 混合队列如何设计?](./part3-核心架构设计.md#33-redis--mysql-混合队列)
|
||||
- [事件驱动架构如何实现?](./part4-关键技术方案.md#44-事件驱动架构)
|
||||
|
||||
**开发相关**:
|
||||
- [如何新增一个业务类型?](./part7-扩展性设计.md#71-新增业务条线流程)
|
||||
- [如何扩展派单策略?](./part7-扩展性设计.md#72-自定义派单策略)
|
||||
- [如何编写单元测试?](./part8-测试指南.md#81-单元测试规范)
|
||||
- [有哪些重大的技术决策?](./part9-技术决策记录.md)
|
||||
- [如何查询术语定义?](./part10-附录.md#101-核心术语表)
|
||||
|
||||
---
|
||||
|
||||
## 📚 核心概念
|
||||
|
||||
### 工单(Work Order)
|
||||
|
||||
工单是 Ops 模块的核心业务对象,代表一个需要处理的任务。
|
||||
|
||||
**工单类型**:
|
||||
- `CLEAN` - 保洁工单
|
||||
- `REPAIR` - 维修工单
|
||||
- `SECURITY` - 安保工单
|
||||
- `SERVICE` - 客服工单
|
||||
|
||||
**工单状态**:
|
||||
- `PENDING` - 待接单
|
||||
- `ASSIGNED` - 已分配
|
||||
- `ARRIVED` - 已到岗
|
||||
- `PAUSED` - 已暂停
|
||||
- `COMPLETED` - 已完成
|
||||
- `CANCELLED` - 已取消
|
||||
|
||||
### 优先级(Priority)
|
||||
|
||||
**优先级等级**:
|
||||
- `P0` - 紧急(可打断当前任务)
|
||||
- `P1` - 重要(优先处理)
|
||||
- `P2` - 普通(正常排队)
|
||||
|
||||
**关键特性**:
|
||||
- P0 任务会打断保洁员当前正在执行的任务
|
||||
- P0 任务完成后,自动恢复被中断的任务
|
||||
|
||||
### 队列(Queue)
|
||||
|
||||
每个保洁员/维修工都有一个优先级队列,存储待处理的任务。
|
||||
|
||||
**队列状态**:
|
||||
- `WAITING` - 等待中
|
||||
- `PROCESSING` - 处理中
|
||||
- `PAUSED` - 暂停中
|
||||
- `REMOVED` - 已移除
|
||||
|
||||
**技术实现**:Redis Sorted Set + MySQL 混合队列
|
||||
|
||||
### 派单引擎(Dispatch Engine)
|
||||
|
||||
根据策略自动推荐最合适的执行人员。
|
||||
|
||||
**派单策略**:
|
||||
- 区域优先策略(就近分配)
|
||||
- 负载均衡策略(考虑当前任务数)
|
||||
- 技能匹配策略(维修工单需要相应技能)
|
||||
|
||||
### 状态机(State Machine)
|
||||
|
||||
管理工单的状态转换,保证状态一致性。
|
||||
|
||||
**核心能力**:
|
||||
- 状态转换规则验证
|
||||
- 事件记录(事件溯源)
|
||||
- 监听器/事件处理器通知
|
||||
|
||||
---
|
||||
|
||||
## 🔗 相关文档
|
||||
|
||||
- [AIoT Platform 技术全景文档](../technical-overview/README.md)
|
||||
- [部署指南](../deployment-guide.md)
|
||||
- [工单调度系统技术实现汇报文档](../工单调度系统技术实现汇报文档.md)
|
||||
- [阶段2实现总结](../phase2-implementation-summary.md)
|
||||
|
||||
---
|
||||
|
||||
## 📧 贡献指南
|
||||
|
||||
本文档是 Ops 模块的官方架构文档,欢迎团队成员贡献:
|
||||
|
||||
1. **新增内容**:在对应章节添加,更新目录和版本号
|
||||
2. **修正错误**:标注修正位置和原因
|
||||
3. **补充示例**:添加代码示例和图表
|
||||
4. **记录决策**:使用 ADR 格式记录技术决策
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 1: 架构总览](./part1-架构总览.md)
|
||||
429
docs/ops-architecture/part1-架构总览.md
Normal file
429
docs/ops-architecture/part1-架构总览.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Part 1: 架构总<E69E84><E680BB>
|
||||
|
||||
## 1.1 业务背景
|
||||
|
||||
### 1.1.1 物业管理行业现状
|
||||
|
||||
传统物业管理面临的核心挑战:
|
||||
|
||||
| 问题 | 具体表现 | 影响 |
|
||||
|------|----------|------|
|
||||
| **调度低效** | 依赖人工派单,电话沟通 | 响应慢,客户体验差 |
|
||||
| **资源浪费** | 保洁员/维修工闲置或过载 | 人力成本高,效率低 |
|
||||
| **信息滞后** | 无法实时掌握现场情况 | 问题发现晚,处理慢 |
|
||||
| **质量不稳定** | 依赖人工经验,缺乏标准 | 服务质量参差不齐 |
|
||||
| **数据孤岛** | 各业务系统独立 | 难以统筹决策,数据价值低 |
|
||||
|
||||
### 1.1.2 万物梁行的数字化目标
|
||||
|
||||
**短期目标**:
|
||||
- 实<><E5AE9E>工单智能调度,提升响应速度
|
||||
- 实时监控人员状态,优化资源分配
|
||||
- 记录服务数据,支撑绩效评价
|
||||
|
||||
**中期目标**:
|
||||
- 四大业务线一体化运营
|
||||
- 数据驱动决策,优化运营策略
|
||||
- 提升客户满意度,降低运营成本
|
||||
|
||||
**长期目标**:
|
||||
- 打造行业领先的智能运营平台
|
||||
- 输出数字化解决方案
|
||||
- 支持规模化复制
|
||||
|
||||
---
|
||||
|
||||
## 1.2 系统架构
|
||||
|
||||
### 1.2.1 模块结构
|
||||
|
||||
```
|
||||
viewsh-module-ops/
|
||||
├── viewsh-module-ops-api/ # [契约层] DTO、Feign 接口
|
||||
├── viewsh-module-ops-biz/ # [核心层] 工单引擎、公共逻辑
|
||||
├── viewsh-module-environment-biz/ # [业务层] 保洁条线
|
||||
├── viewsh-module-security-biz/ # [业务层] 安保条线
|
||||
├── viewsh-module-facilities-biz/ # [业务层] 工程条线
|
||||
├── viewsh-module-service-biz/ # [业务层] 客服条线
|
||||
└── viewsh-module-ops-server/ # [宿主层] 微服务入口
|
||||
```
|
||||
|
||||
### 1.2.2 分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 宿主层 (Server) │
|
||||
│ Ops Server Application │
|
||||
│ - REST API │
|
||||
│ - 配置管理 │
|
||||
└──────────────────────────┬──────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────┼─────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌──────▼──────┐ ┌───────▼────────┐
|
||||
│ 保洁业务层 │ │ 安保业务层 │ │ 工程业务层 │
|
||||
│ (Environment) │ │ (Security) │ │(Facilities) │
|
||||
│ - 排班 │ │ - 巡更 │ │ - 维保 │
|
||||
│ - 巡检 │ │ - 报警 │ │ - 巡检 │
|
||||
│ - 耗材 │ │ - 岗位 │ │ - 备件 │
|
||||
└────────┬────────┘ └──────┬──────┘ └───────┬────────┘
|
||||
│ │ │
|
||||
└─────────────────┼─────────────────┘
|
||||
│
|
||||
┌─────────────────▼─────────────────┐
|
||||
│ 核心业务层 (Ops Biz) │
|
||||
│ - 工单引擎 │
|
||||
│ - 状态机 │
|
||||
│ - 队列管理 │
|
||||
│ - 派单引擎 │
|
||||
└─────────────────┬─────────────────┘
|
||||
│
|
||||
┌─────────────────▼─────────────────┐
|
||||
│ 契约层 (Ops API) │
|
||||
│ - DTO 定义 │
|
||||
│ - 枚举定义 │
|
||||
│ - 服务接口 │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**设计原则**:
|
||||
- **依赖倒置**:高层不依赖低层,都依赖抽象
|
||||
- **单一职责**:每层专注自己的职责
|
||||
- **开闭原则**:对扩展开放,对修改关闭
|
||||
- **接口隔离**:业务线通过接口扩展核心引擎
|
||||
|
||||
### 1.2.3 技术栈
|
||||
|
||||
| 层次 | 技术选型 |
|
||||
|------|----------|
|
||||
| 应用框架 | Spring Boot 3.5.9 |
|
||||
| ORM 框架 | MyBatis Plus 3.5+ |
|
||||
| 数据库 | MySQL 8.0+ |
|
||||
| 缓存 | Redis 6.x/7.x |
|
||||
| 消息队列 | RocketMQ 5.x |
|
||||
| 定时任务 | Spring @Scheduled / XXL-Job |
|
||||
| 单元测试 | JUnit 5 + Mockito |
|
||||
|
||||
---
|
||||
|
||||
## 1.3 核心业务流程
|
||||
|
||||
### 1.3.1 保洁工单完整流程
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 系统定时任务 │ 每5分钟生成日常保洁工单
|
||||
│ 或巡检员创建 │ 手动创建临时工单
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 智能派单引擎 │ 推荐最合适的保洁员
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 工单入队 │ 加入优先级队列
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 工牌语音播报 │ "2楼电梯厅有水渍"
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 保洁员接单 │ 确认接单,状态变更为 ARRIVED
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 到岗打卡 │ 自动记录位置和时间
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 作业进行中 │ 暂停:临时离开
|
||||
│ │ 恢复:回来继续作业
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 完成工单 │ 上传照片,记录作业时长
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 自动派发 │ 自动派发下一个任务
|
||||
│ 下一个任务 │ 或恢复被中断的任务
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### 1.3.2 P0 紧急任务打断流程
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ 巡检员创建P0 │ 2楼电梯厅有严重水渍,紧急处理!
|
||||
│ 紧急工单 │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 查找附近 │ 500米范围内
|
||||
│ 保洁员 │ 状态为 IDLE 或 BUSY
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 目标保洁员 │ 张师傅(正在执行P2任务)
|
||||
│ 当前状态 │ BUSY
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 打断当前任务 │ 暂停P2任务
|
||||
│ │ - 工单状态:ARRIVED → PAUSED
|
||||
│ │ - 队列状态:PROCESSING → PAUSED
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 紧急插队 │ P0任务加入队列首位
|
||||
│ │ 立即派单
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 高分贝警报 │ 工牌最大音量 + 震动
|
||||
│ + 语音播报 │ "紧急插队:2楼电梯厅"
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 张师傅接单 │ 立即处理P0任务
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 完成P0任务 │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ 自动恢复 │ 恢复被中断的P2任务
|
||||
│ P2任务 │ 工牌语音播报:"恢复任务"
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.4 四大业务线
|
||||
|
||||
### 1.4.1 保洁条线(环境管理)
|
||||
|
||||
**业务范围**:
|
||||
- 日常保洁(定时生成工单)
|
||||
- 临时保洁(巡检员手动创建)
|
||||
- 排班管理(保洁员班次)
|
||||
- 巡检管理(巡检计划、巡检记录)
|
||||
- 耗材管理(消耗品、库存)
|
||||
|
||||
**核心功能**:
|
||||
- ✅ 智能调度系统(自动派单、优先级队列、紧急插队)
|
||||
- ✅ 保洁员状态管理(IDLE、BUSY、PAUSED、OFFLINE)
|
||||
- ✅ 工牌语音播报(自动去重)
|
||||
- ✅ 绩效统计(响应时间、作业时长、质量评分)
|
||||
|
||||
**技术<E68A80><E69CAF><EFBFBD>现**:
|
||||
- 代码模块:`viewsh-module-environment-biz`
|
||||
- 服务类:`CleanOrderService`、`CleanerStatusService`
|
||||
- 派单策略:`CleanerAreaAssignStrategy`、`CleanerPriorityScheduleStrategy`
|
||||
|
||||
### 1.4.2 安保条线
|
||||
|
||||
**业务范围**:
|
||||
- 巡更管理(路线规划、点位打卡)
|
||||
- 报警管理(异常上报、报警联动)
|
||||
- 岗位管理(岗位值守、交接班)
|
||||
- 人员管理(保安档案、考勤)
|
||||
|
||||
**核心功能**:
|
||||
- 巡更轨迹追踪(GPS + 信标)
|
||||
- 异常报警联动(自动创建工单)
|
||||
- 实时监控(巡更进度可视化)
|
||||
|
||||
**技术实现**:
|
||||
- 代码模块:`viewsh-module-security-biz`
|
||||
- 计划实现:2026 Q2
|
||||
|
||||
### 1.4.3 工程条线(设施管理)
|
||||
|
||||
**业务范围**:
|
||||
- 维保管理(设备维保计划)
|
||||
- 巡检管理(设备巡检)
|
||||
- 备件管理(库存、领用)
|
||||
- 故障维修(报修、派单、维修、验收)
|
||||
|
||||
**核心功能**:
|
||||
- 设备档案管理
|
||||
- 维保计划自动生成
|
||||
- 备件库存预警
|
||||
- 故障统计分析
|
||||
|
||||
**技术实现**:
|
||||
- 代码模块:`viewsh-module-facilities-biz`
|
||||
- 计划实现:2026 Q3
|
||||
|
||||
### 1.4.4 客服条线
|
||||
|
||||
**业务范围**:
|
||||
- 投诉处理(客户投诉、分类、处理)
|
||||
- 建议管理(客户建议、采纳)
|
||||
- 满意度调查(服务评价、统计分析)
|
||||
|
||||
**核心功能**:
|
||||
- 多渠道接入(小程序、电话、公众号)
|
||||
- 智能分类(AI 分类)
|
||||
- 满意度评价
|
||||
- 服务数据分析
|
||||
|
||||
**技术实现**:
|
||||
- 代码模块:`viewsh-module-service-biz`
|
||||
- 计划实现:2026 Q4
|
||||
|
||||
---
|
||||
|
||||
## 1.5 核心概念
|
||||
|
||||
### 1.5.1 工单(Work Order)
|
||||
|
||||
工单是 Ops 模块的核心业务对象,代表一个需要处理的任务。
|
||||
|
||||
**工单类型(WorkOrderTypeEnum)**:
|
||||
```java
|
||||
CLEAN("CLEAN", "保洁") // 保洁工单
|
||||
REPAIR("REPAIR", "维修") // 维修工单
|
||||
SECURITY("SECURITY", "安保") // 安保工单
|
||||
SERVICE("SERVICE", "客服") // 客服工单
|
||||
```
|
||||
|
||||
**工单状态(WorkOrderStatusEnum)**:
|
||||
```java
|
||||
PENDING("待接单") // 工单已创建,等待分配
|
||||
ASSIGNED("已分配") // 已分配给执行人
|
||||
ARRIVED("已到岗") // 执行人已到岗
|
||||
PAUSED("已暂停") // 任务暂停(临时离开或被P0打断)
|
||||
COMPLETED("已完成") // 任务完成
|
||||
CANCELLED("已取消") // 工单取消
|
||||
```
|
||||
|
||||
**工单优先级(PriorityEnum)**:
|
||||
```java
|
||||
P0("P0", "紧急") // 可打断当前任务,立即处理
|
||||
P1("P1", "重要") // 优先处理
|
||||
P2("P2", "普通") // 正常排队
|
||||
```
|
||||
|
||||
### 1.5.2 保洁员状态(CleanerStatus)
|
||||
|
||||
**状态定义(CleanerStatusEnum)**:
|
||||
```java
|
||||
IDLE("空闲") // 可接新单
|
||||
BUSY("忙碌") // 正在执行工单
|
||||
PAUSED("暂停") // 临时离开
|
||||
OFFLINE("离线") // 工牌离线
|
||||
```
|
||||
|
||||
**状态转换规则**:
|
||||
```
|
||||
OFFLINE → IDLE: 工牌上线,心跳恢复
|
||||
IDLE → BUSY: 接单成功
|
||||
BUSY → PAUSED: 任务暂停(手动或被P0打断)
|
||||
PAUSED → BUSY: 任务恢复
|
||||
BUSY → IDLE: 完成所有任务
|
||||
IDLE/OFFLINE: 工牌离线超时(30分钟无心跳)
|
||||
```
|
||||
|
||||
### 1.5.3 队列状态(OrderQueueStatus)
|
||||
|
||||
**状态定义(OrderQueueStatusEnum)**:
|
||||
```java
|
||||
WAITING("等待中") // 工单已进入队列,等待派单
|
||||
PROCESSING("处理中") // 任务已下发,正在处理
|
||||
PAUSED("暂停中") // 任务被暂停
|
||||
REMOVED("已移除") // 任务已完成或已取消
|
||||
```
|
||||
|
||||
### 1.5.4 服务职责划分
|
||||
|
||||
**DispatchEngine(派单引擎)**:
|
||||
- 职责:根据策略推荐最合适的执行人员
|
||||
- 不负责:状态管理、设备通知、业务逻辑
|
||||
|
||||
**OrderLifecycleManager(生命周期管理器)**:
|
||||
- 职责:统一管理工单状态和队列状态的同步变更
|
||||
- 核心方法:`pauseOrder()`, `resumeOrder()`, `interruptOrder()`
|
||||
|
||||
**OrderStateMachine(状态机)**:
|
||||
- 职责:验证状态转换规则、记录事件
|
||||
- 不触发:任何业务逻辑
|
||||
|
||||
**OrderEventPublisher(事件发布器)**:
|
||||
- 职责:发布工单状态变更事件
|
||||
- 实现业务解耦
|
||||
|
||||
---
|
||||
|
||||
## 1.6 当前实施状态
|
||||
|
||||
### 已完成 ✅
|
||||
|
||||
**阶段1:基础数据层**(2025.12)
|
||||
- ✅ 枚举类(10个)
|
||||
- ✅ DO 实体类(10个)
|
||||
- ✅ Mapper 接口(10个)
|
||||
- ✅ 数据库表结构
|
||||
|
||||
**阶段2:工单引擎**(2026.01)
|
||||
- ✅ 状态机实现
|
||||
- ✅ 事件溯源机制
|
||||
- ✅ 监听器模式
|
||||
- ✅ 工单 CRUD
|
||||
- ✅ 工单暂停/恢复
|
||||
- ✅ REST API(11个)
|
||||
- ✅ 单元测试(26个)
|
||||
|
||||
**阶段3:智能调度(部分)**
|
||||
- ✅ 队列状态管理
|
||||
- ✅ 派单引擎实现
|
||||
- ✅ P0 紧急任务打断
|
||||
- ✅ 保洁员状态管理
|
||||
|
||||
**架构重构**(2026.01.06)
|
||||
- ✅ 重新定义组件职责
|
||||
- ✅ 引入 OrderLifecycleManager
|
||||
- ✅ 创建 OrderEventPublisher
|
||||
- ✅ 事件处理器替代监听器
|
||||
|
||||
### 进行中 ⏳
|
||||
|
||||
**阶段3:智能调度(剩余工作)**
|
||||
- ⏳ 工牌通知服务集成
|
||||
- ⏳ 消息队列集成(MQ)
|
||||
- ⏳ 定时任务(自动生成工单、绩效计算)
|
||||
|
||||
### 待实施 📋
|
||||
|
||||
**阶段4:绩效评价系统**
|
||||
- ⏳ 响应时间统计
|
||||
- ⏳ 质量评分
|
||||
- ⏳ 绩效计算
|
||||
|
||||
**阶段5:其他业务线**
|
||||
- ⏳ 安保条线(2026 Q2)
|
||||
- ⏳ 工程条线(2026 Q3)
|
||||
- ⏳ 客服条线(2026 Q4)
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 2: 架构演进史](./part2-架构演进史.md)
|
||||
22
docs/ops-architecture/part10-附录.md
Normal file
22
docs/ops-architecture/part10-附录.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Part 10: 附录
|
||||
|
||||
## 10.1 核心术语表
|
||||
|
||||
| 术语 | 定义 | 对应代码 |
|
||||
| :--- | :--- | :--- |
|
||||
| **派单引擎** | 负责“谁来接单、怎么派单”的决策组件 | `DispatchEngine` |
|
||||
| **调度上下文** | 包含区域、优先级、技能等派单因子的容器 | `DispatchContext` |
|
||||
| **打断决策** | 评估新工单是否具有打断当前任务权力的逻辑结果 | `InterruptDecision` |
|
||||
| **执行人状态** | 执行人在系统中的实时生命周期(空闲、忙碌、离线) | `CleanerStatus` |
|
||||
|
||||
## 10.2 架构变更日志
|
||||
|
||||
### v2.1.0 (2026-01-06)
|
||||
- **重构**:引入 `OrderLifecycleManager`,彻底分离状态管理与业务逻辑。
|
||||
- **新增**:实现 P0 紧急任务自动打断与完成后自动恢复机制。
|
||||
|
||||
### v1.0.0 (2025-12-15)
|
||||
- **基础**:完成工单 CRUD、基础状态机及保洁业务初步接入。
|
||||
|
||||
---
|
||||
**返回目录**:[README.md](./README.md)
|
||||
570
docs/ops-architecture/part2-架构演进史.md
Normal file
570
docs/ops-architecture/part2-架构演进史.md
Normal file
@@ -0,0 +1,570 @@
|
||||
# Part 2: 架构演进史
|
||||
|
||||
本文档记录 Ops 业务运营模块从零开始到当前的完整演进历程,包括每个阶段的目标、成果、经验教训和技术债务。
|
||||
|
||||
---
|
||||
|
||||
## 2.1 阶段1:基础数据层(2025.12)
|
||||
|
||||
### 2.1.1 实施时间
|
||||
|
||||
2025年12月 - 2026年1月初
|
||||
|
||||
### 2.1.2 阶段目标
|
||||
|
||||
搭建工单管理系统的数据基础,创建所有必要的数据模型。
|
||||
|
||||
### 2.1.3 核心成果
|
||||
|
||||
**枚举类(10个)**:
|
||||
|
||||
| 枚举类 | 说明 |
|
||||
|--------|------|
|
||||
| `WorkOrderTypeEnum` | 工单类型(CLEAN、REPAIR、SECURITY、SERVICE) |
|
||||
| `WorkOrderStatusEnum` | 工单状态(PENDING、ASSIGNED、ARRIVED、PAUSED、COMPLETED、CANCELLED) |
|
||||
| `PriorityEnum` | 优先级(P0、P1、P2) |
|
||||
| `CleanerStatusEnum` | 保洁员状态(IDLE、BUSY、PAUSED、OFFLINE) |
|
||||
| `DispatchModeEnum` | 派单模式(AUTO、MANUAL) |
|
||||
| `OperatorTypeEnum` | 操作人类型(SYSTEM、ADMIN、CLEANER、INSPECTOR) |
|
||||
| `BadgeDeviceTypeEnum` | 设备类型(BADGE、BEACON) |
|
||||
| `AreaTypeEnum` | 区域类型(PARK、BUILDING、FLOOR、FUNCTION) |
|
||||
| `FunctionTypeEnum` | 功能类型(LOBBY、ELEVATOR、CORRIDOR、RESTROOM) |
|
||||
| `EventDomainEnum` | 事件领域(DISPATCH、QUALITY、SYSTEM) |
|
||||
|
||||
**DO 实体类(10个)**:
|
||||
|
||||
| 实体类 | 说明 |
|
||||
|--------|------|
|
||||
| `OpsOrderDO` | 工单主表 |
|
||||
| `OpsOrderCleanExtDO` | 保洁工单扩展表 |
|
||||
| `OpsOrderEventDO` | 工单事件记录表 |
|
||||
| `OpsOrderDispatchDO` | 派单决策记录表 |
|
||||
| `OpsOrderQueueDO` | 工单队列表 |
|
||||
| `CleanerStatusDO` | 保洁员状态表 |
|
||||
| `CleanerPerformanceMonthlyDO` | 月度绩效汇总表 |
|
||||
| `BadgeDeviceDO` | 工牌设备表 |
|
||||
| `CleanEventLogDO` | 保洁事件日志表 |
|
||||
| `BusAreaDO` | 业务区域表 |
|
||||
|
||||
**Mapper 接口(10个)**:
|
||||
- `OpsOrderMapper`
|
||||
- `OpsOrderEventMapper`
|
||||
- `CleanerStatusMapper`
|
||||
- `BadgeDeviceMapper`
|
||||
- `OpsOrderCleanExtMapper`
|
||||
- `OpsOrderDispatchMapper`
|
||||
- `OpsOrderQueueMapper`
|
||||
- `CleanerPerformanceMonthlyMapper`
|
||||
- `CleanEventLogMapper`
|
||||
- `BusAreaMapper`
|
||||
|
||||
**SQL 表结构**:
|
||||
- 文件:`sql/mysql/aiot_ops_work_order.sql`
|
||||
- 创建了所有工单相关的数据库表
|
||||
|
||||
### 2.1.4 遗留问题
|
||||
|
||||
- 无业务逻辑实现
|
||||
- 无服务层和 Controller
|
||||
- 无单元测试
|
||||
|
||||
---
|
||||
|
||||
## 2.2 阶段2:工单引擎(2026.01)
|
||||
|
||||
### 2.2.1 实施时间
|
||||
|
||||
2026年1月 - 2026年1月中旬
|
||||
|
||||
### 2.2.2 阶段目标
|
||||
|
||||
实现工单管理系统的核心业务逻辑,包括状态机、事件溯源、REST API。
|
||||
|
||||
### 2.2.3 核心成果
|
||||
|
||||
#### DTO 数据传输对象(11个)
|
||||
|
||||
**工单基础 DTO(ops-biz 模块)**:
|
||||
- `OpsOrderCreateReqDTO` - 创建工单请求
|
||||
- `OpsOrderUpdateReqDTO` - 更新工单请求
|
||||
- `OpsOrderPageReqDTO` - 分页查询请求
|
||||
- `OpsOrderAssignReqDTO` - 分配工单请求
|
||||
- `OpsOrderCompleteReqDTO` - 完成工单请求
|
||||
- `OpsOrderRespDTO` - 工单基本信息响应
|
||||
- `OpsOrderDetailRespDTO` - 工单详细信息响应
|
||||
- `OpsOrderEventRespDTO` - 工单事件记录响应
|
||||
|
||||
**保洁业务 DTO(environment-biz 模块)**:
|
||||
- `CleanOrderAutoCreateReqDTO` - 保洁工单自动创建请求
|
||||
- `CleanOrderPauseReqDTO` - 保洁工单暂停请求
|
||||
- `CleanOrderResumeReqDTO` - 保洁工单恢复请求
|
||||
|
||||
#### 状态机核心实现
|
||||
|
||||
**`OrderStateMachine`** - 状态机引擎
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class OrderStateMachine {
|
||||
// 状态转换规则
|
||||
private static final Map<WorkOrderStatusEnum, Set<WorkOrderStatusEnum>> TRANSITIONS = Map.of(
|
||||
PENDING, Set.of(ASSIGNED, CANCELLED),
|
||||
ASSIGNED, Set.of(ARRIVED, CANCELLED),
|
||||
ARRIVED, Set.of(PAUSED, COMPLETED),
|
||||
PAUSED, Set.of(ARRIVED, CANCELLED),
|
||||
COMPLETED, Collections.emptySet(),
|
||||
CANCELLED, Collections.emptySet()
|
||||
);
|
||||
|
||||
@Transactional
|
||||
public void transition(OpsOrderDO order, WorkOrderStatusEnum newStatus,
|
||||
OperatorTypeEnum operatorType, Long operatorId, String remark);
|
||||
}
|
||||
```
|
||||
|
||||
**核心功能**:
|
||||
- ✅ 状态转换规则验证
|
||||
- ✅ 自动记录事件到 `ops_order_event` 表
|
||||
- ✅ 通知所有注册的监听器
|
||||
- ✅ 支持业务扩展(通过监听器模式)
|
||||
|
||||
**状态流转图**:
|
||||
```
|
||||
PENDING (待接单)
|
||||
↓
|
||||
ASSIGNED (已分配)
|
||||
↓
|
||||
ARRIVED (已到岗)
|
||||
↓ ↘
|
||||
PAUSED (已暂停) COMPLETED (已完成) [终态]
|
||||
↓
|
||||
ARRIVED (已恢复)
|
||||
|
||||
任何状态 → CANCELLED (已取消) [终态]
|
||||
```
|
||||
|
||||
#### 事件与监听器
|
||||
|
||||
**`OrderStateChangedEvent`** - 状态变更事件
|
||||
```java
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class OrderStateChangedEvent {
|
||||
private OpsOrderDO order;
|
||||
private WorkOrderStatusEnum fromStatus;
|
||||
private WorkOrderStatusEnum toStatus;
|
||||
private OperatorTypeEnum operatorType;
|
||||
private Long operatorId;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
**`OrderStateChangeListener`** - 监听器接口
|
||||
```java
|
||||
@FunctionalInterface
|
||||
public interface OrderStateChangeListener {
|
||||
void onStateChanged(OrderStateChangedEvent event);
|
||||
}
|
||||
```
|
||||
|
||||
#### 工单服务层
|
||||
|
||||
**`OpsOrderService` 接口** - 10个核心方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `createOrder()` | 创建工单 |
|
||||
| `updateOrder()` | 更新工单 |
|
||||
| `deleteOrder()` | 删除工单 |
|
||||
| `getOrder()` | 获取工单详情 |
|
||||
| `getOrderPage()` | 分页查询工单 |
|
||||
| `assignOrder()` | 分配工单给执行人 |
|
||||
| `acceptOrder()` | 执行人接单 |
|
||||
| `completeOrder()` | 完成工单 |
|
||||
| `pauseOrder()` | 暂停工单 |
|
||||
| `resumeOrder()` | 恢复工单 |
|
||||
| `cancelOrder()` | 取消工单 |
|
||||
|
||||
#### REST API(11个)
|
||||
|
||||
| HTTP方法 | 路径 | 功能 | 权限 |
|
||||
|---------|------|------|------|
|
||||
| POST | `/ops/order/create` | 创建工单 | `ops:order:create` |
|
||||
| PUT | `/ops/order/update` | 更新工单 | `ops:order:update` |
|
||||
| DELETE | `/ops/order/delete` | 删除工单 | `ops:order:delete` |
|
||||
| GET | `/ops/order/get` | 获取工单详情 | `ops:order:query` |
|
||||
| GET | `/ops/order/page` | 分页查询工单 | `ops:order:query` |
|
||||
| POST | `/ops/order/assign` | 分配工单 | `ops:order:assign` |
|
||||
| POST | `/ops/order/accept` | 接单 | `ops:order:accept` |
|
||||
| POST | `/ops/order/complete` | 完成工单 | `ops:order:complete` |
|
||||
| POST | `/ops/order/pause` | 暂停工单 | `ops:order:pause` |
|
||||
| POST | `/ops/order/resume` | 恢复工单 | `ops:order:resume` |
|
||||
| POST | `/ops/order/cancel` | 取消工单 | `ops:order:cancel` |
|
||||
|
||||
#### 单元测试(26个)
|
||||
|
||||
**`OrderStateMachineTest`** - 10个测试用例
|
||||
- 状态转换测试
|
||||
- 非法转换测试
|
||||
- 终态测试
|
||||
|
||||
**`OpsOrderServiceTest`** - 16个测试用例
|
||||
- CRUD 操作测试
|
||||
- 业务规则测试
|
||||
- 异常场景测试
|
||||
|
||||
**测试结果**:
|
||||
```bash
|
||||
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
**测试覆盖率**:
|
||||
- 状态机核心逻辑:100%
|
||||
- 工单服务业务规则:95%+
|
||||
- 异常场景覆盖:完整
|
||||
- 边界条件测试:完整
|
||||
|
||||
### 2.2.4 架构设计亮点
|
||||
|
||||
**1. 模块化分层设计**
|
||||
```
|
||||
viewsh-module-ops/
|
||||
├── viewsh-module-ops-api/ # 契约层
|
||||
├── viewsh-module-ops-biz/ # 核心业务层
|
||||
│ ├── dal/ # 数据访问层
|
||||
│ ├── service/ # 服务层
|
||||
│ │ ├── order/ # 工单服务
|
||||
│ │ ├── fsm/ # 状态机
|
||||
│ │ └── event/ # 事件服务
|
||||
├── viewsh-module-environment-biz/ # 保洁业务线
|
||||
│ ├── service/
|
||||
│ │ ├── cleanorder/ # 保洁工单服务
|
||||
│ │ └── listener/ # 保洁状态监听器
|
||||
└── viewsh-module-ops-server/ # 宿主层
|
||||
└── controller/ # REST API
|
||||
```
|
||||
|
||||
**2. 状态机模式**
|
||||
- 集中管理状态转换规则
|
||||
- 避免状态散落在各个业务方法
|
||||
- 易于维护和扩展新状态
|
||||
- 自动记录状态变更历史
|
||||
|
||||
**3. 监听器模式**
|
||||
- 解耦核心引擎与业务逻辑
|
||||
- 业务线可独立扩展
|
||||
- 支持多监听器协同工作
|
||||
- 符合开闭原则
|
||||
|
||||
**4. 事件溯源**
|
||||
- 所有状态转换记录到 `ops_order_event` 表
|
||||
- 支持完整的审计追溯
|
||||
- 可用于状态重建和数据分析
|
||||
|
||||
### 2.2.5 技术债务
|
||||
|
||||
| 优先级 | 问题 | 改进方案 | 预估工作量 |
|
||||
|-------|------|---------|-----------|
|
||||
| P0 | 分页查询未实现 | 实现 selectPage | 2小时 |
|
||||
| P1 | 异常处理过于简单 | 定义业务异常类体系 | 4小时 |
|
||||
| P1 | 缺少参数校验单元测试 | 增加参数校验测试用例 | 3小时 |
|
||||
| P2 | 日志打印不够详细 | 增加关键操作日志 | 2小时 |
|
||||
| P2 | 缺少性能监控 | 集成 Micrometer 统计 | 4小时 |
|
||||
|
||||
---
|
||||
|
||||
## 2.3 阶段3:智能调度系统(2026.01 - 规划中)
|
||||
|
||||
### 2.3.1 实施计划
|
||||
|
||||
2026年1月 - 2026年2月
|
||||
|
||||
### 2.3.2 阶段目标
|
||||
|
||||
实现自动派单、优先级队列、紧急插队功能。
|
||||
|
||||
### 2.3.3 核心功能
|
||||
|
||||
**1. 保洁员状态管理**
|
||||
|
||||
**状态定义**:
|
||||
- `IDLE` - 空闲(可接新单)
|
||||
- `BUSY` - 忙碌(正在执行工单)
|
||||
- `PAUSED` - 暂停(临时离开)
|
||||
- `OFFLINE` - 离线(工牌离线)
|
||||
|
||||
**核心方法**:
|
||||
- `updateStatus()` - 更新状态
|
||||
- `handleHeartbeat()` - 处理工牌心跳
|
||||
- `listAvailableCleaners()` - 查询可接单的保洁员
|
||||
- `findNearbyCleaners()` - 查询附近的保洁员
|
||||
|
||||
**2. 优先级队列管理**
|
||||
|
||||
**Redis + MySQL 混合队列**:
|
||||
- Redis Sorted Set:实时队列(高性能)
|
||||
- MySQL:持久化存储(数据不丢失)
|
||||
|
||||
**队列状态**:
|
||||
- `WAITING` - 等待中
|
||||
- `PROCESSING` - 处理中
|
||||
- `PAUSED` - 暂停中
|
||||
- `REMOVED` - 已移除
|
||||
|
||||
**核心方法**:
|
||||
- `enqueue()` - 入队
|
||||
- `startExecution()` - 开始执行(WAITING → PROCESSING)
|
||||
- `pauseTask()` - 暂停任务(PROCESSING → PAUSED)
|
||||
- `resumeTask()` - 恢复任务(PAUSED → PROCESSING)
|
||||
|
||||
**3. 自动派单引擎**
|
||||
|
||||
**派单策略**:
|
||||
- 区域优先策略(就近分配)
|
||||
- 负载均衡策略(考虑当前任务数)
|
||||
- 技能匹配策略(维修工单需要相应技能)
|
||||
|
||||
**核心方法**:
|
||||
- `recommendAssignee()` - 推荐执行人员
|
||||
- `autoDispatch()` - 自动派单
|
||||
- `urgentDispatch()` - 紧急派单(可能打断)
|
||||
|
||||
**4. P0 紧急任务打断**
|
||||
|
||||
**打断条件**:
|
||||
- 目标保洁员状态为 BUSY
|
||||
- 新工单优先级为 P0
|
||||
|
||||
**打断流程**:
|
||||
1. 查找附近的保洁员
|
||||
2. 如果保洁员正在执行任务(BUSY)
|
||||
3. 暂停当前任务(工单状态 + 队列状态同步变更)
|
||||
4. 派发 P0 工单
|
||||
5. P0 完成后,自动恢复被中断的任务
|
||||
|
||||
**5. IoT 设备集成**
|
||||
|
||||
**工牌设备集成**:
|
||||
- 订阅设备状态变更事件
|
||||
- 接收设备心跳消息
|
||||
- 发送语音播报指令
|
||||
|
||||
**语音播报优化**:
|
||||
- 自动去重(同一工单不重复播报)
|
||||
- P0 紧急任务:高分贝警报 + 震动
|
||||
- 普通/P1 任务:普通语音 + 震动
|
||||
|
||||
### 2.3.4 实施进度
|
||||
|
||||
**已完成 ✅**:
|
||||
- 队列状态管理(WAITING、PROCESSING、PAUSED、REMOVED)
|
||||
- 派单引擎实现(DispatchEngine)
|
||||
- 保洁员状态管理
|
||||
- P0 紧急任务打断机制
|
||||
- 语音播报去重服务
|
||||
|
||||
**进行中 ⏳**:
|
||||
- 工牌通知服务集成
|
||||
- 消息队列集成(MQ)
|
||||
- 定时任务(自动生成工单、绩效计算)
|
||||
|
||||
**待实施 📋**:
|
||||
- 绩效评价系统
|
||||
- 单元测试(派单逻辑、队列管理、紧急插队)
|
||||
|
||||
---
|
||||
|
||||
## 2.4 架构重构历程(2026.01.06)
|
||||
|
||||
### 2.4.1 背景
|
||||
|
||||
在阶段2和阶段3的实施过程中,发现了一些架构问题,需要进行重构。
|
||||
|
||||
### 2.4.2 问题识别
|
||||
|
||||
**问题1:DispatchEngine 职责不清晰**
|
||||
|
||||
- 接口定义了 13 个方法,但很多返回 null
|
||||
- 派单引擎与队列服务的职责边界模糊
|
||||
- `autoDispatch()` 实际只是调用 `orderQueueService.startExecution()`
|
||||
|
||||
**问题2:通用方法抽离不彻底**
|
||||
|
||||
- `OpsOrderService` 只有基础的 CRUD
|
||||
- 暂停/恢复逻辑直接调用状态机,没有与队列联动
|
||||
- 打断逻辑只在保洁业务层实现,其他业务线无法复用
|
||||
|
||||
**问题3:Listener 设计不明确**
|
||||
|
||||
- 监听器注册是全局的,无法区分业务类型
|
||||
- 监听器之间没有优先级定义
|
||||
- `CleanOrderStateChangeListener` 直接依赖注入 `CleanOrderService`,产生循环依赖风险
|
||||
|
||||
**问题4:状态与队列状态同步问题**
|
||||
|
||||
- 工单状态 (`WorkOrderStatusEnum`) 和队列状态 (`OrderQueueStatusEnum`) 是两个独立的状态机
|
||||
- 两者之间的同步需要业务层手动保证
|
||||
|
||||
### 2.4.3 重构方案设计
|
||||
|
||||
**核心原则**:
|
||||
1. 单一职责:每个组件只负责一个明确的职责
|
||||
2. 开闭原则:通过接口和策略模式支持扩展
|
||||
3. 依赖倒置:业务层依赖通用层的抽象接口
|
||||
4. 清晰分层:通用能力下沉,业务能力上浮
|
||||
|
||||
**重新定义组件职责**:
|
||||
|
||||
**DispatchEngine(派单引擎)- 重新定位**:
|
||||
- 职责:只负责"决策",推荐最合适的执行人员
|
||||
- 不负责:状态管理、设备通知、业务逻辑
|
||||
|
||||
**OrderLifecycleManager(工单生命周期管理)- 新增**:
|
||||
- 职责:统一管理工单状态和队列状态的同步变更
|
||||
- 核心方法:`pauseOrder()`, `resumeOrder()`, `interruptOrder()`, `completeOrder()`
|
||||
|
||||
**OrderStateMachine(状态机)- 优化**:
|
||||
- 职责:验证状态转换规则、记录事件
|
||||
- 不触发:任何业务逻辑(移除监听器机制)
|
||||
|
||||
**OrderEventPublisher(事件发布器)- 新增**:
|
||||
- 职责:发布工单状态变更事件
|
||||
- 实现业务解耦
|
||||
|
||||
### 2.4.4 重构实施
|
||||
|
||||
**Phase 1: 基础重构**
|
||||
|
||||
| 任务 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| 重构 DispatchEngine 接口和实现 | 纯决策层 | ✅ 完成 |
|
||||
| 创建 OrderLifecycleManager | 生命周期管理 | ✅ 完成 |
|
||||
| 重构 OrderStateMachine(移除监听器) | 纯状态管理 | ✅ 完成 |
|
||||
| 创建 OrderEventPublisher | 事件发布器 | ✅ 完成 |
|
||||
| 迁移暂停/恢复逻辑到 OrderLifecycleManager | 统一状态同步 | ✅ 完成 |
|
||||
|
||||
**Phase 2: 业务层改造**
|
||||
|
||||
| 任务 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| 创建事件处理器替代监听器 | CleanOrderEventHandler | ✅ 完成 |
|
||||
| 重构 CleanOrderService | 使用 OrderLifecycleManager | ✅ 完成 |
|
||||
| 更新派单策略实现 | CleanerAreaPriorityStrategy | ✅ 完成 |
|
||||
| 单元测试编写 | 测试用例 | 📋 待进行 |
|
||||
|
||||
**Phase 3: 优化与验证**
|
||||
|
||||
| 任务 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| 性能优化 | - | ⏳ 待进行 |
|
||||
| 集成测试 | - | ⏳ 待进行 |
|
||||
| 文档更新 | - | ⏳ 待进行 |
|
||||
|
||||
### 2.4.5 收获与反思
|
||||
|
||||
**成功经验**:
|
||||
|
||||
1. **职责分离带来清晰度**
|
||||
- DispatchEngine 专注于决策
|
||||
- OrderLifecycleManager 统一管理状态同步
|
||||
- 各组件职责清晰,易于理解和维护
|
||||
|
||||
2. **事件驱动架构提升扩展性**
|
||||
- 业务方通过订阅事件处理自己的逻辑
|
||||
- 新增业务类型只需实现事件处理器
|
||||
- 核心引擎不需要修改
|
||||
|
||||
3. **兼容性保证平滑迁移**
|
||||
- 保留旧实现作为兼容层
|
||||
- 业务流程不中断
|
||||
- 逐步迁移到新架构
|
||||
|
||||
**踩坑记录**:
|
||||
|
||||
1. **循环依赖问题**
|
||||
- 问题:DispatchEngine 依赖 CleanOrderService,CleanOrderService 依赖 DispatchEngine
|
||||
- 解决:使用 `@Lazy` 延迟注入
|
||||
|
||||
2. **状态同步复杂性**
|
||||
- 问题:工单状态和队列状态需要保持一致
|
||||
- 解决:引入 OrderLifecycleManager 统一管理
|
||||
|
||||
3. **监听器优先级**
|
||||
- 问题:监听器之间没有优先级定义
|
||||
- 解决:改为事件驱动,由业务方决定处理顺序
|
||||
|
||||
**改进建议**:
|
||||
|
||||
1. **从设计阶段就明确职责**
|
||||
- 在编码前充分讨论组件职责
|
||||
- 使用 UML 图展示组件关系
|
||||
- 定期 Review 架构设计
|
||||
|
||||
2. **持续重构**
|
||||
- 不要等技术债务积累太多
|
||||
- 定期(每季度)进行架构 Review
|
||||
- 及时重构不合理的设计
|
||||
|
||||
3. **完善测试**
|
||||
- 单元测试覆盖核心逻辑
|
||||
- 集成测试覆盖端到端流程
|
||||
- 性能测试验证优化效果
|
||||
|
||||
---
|
||||
|
||||
## 2.5 版本对比
|
||||
|
||||
### 2.5.1 v1.0(阶段2完成)
|
||||
|
||||
**特点**:
|
||||
- 状态机 + 监听器模式
|
||||
- 基础工单 CRUD
|
||||
- REST API
|
||||
- 单元测试
|
||||
|
||||
**优点**:
|
||||
- 功能完整
|
||||
- 测试覆盖率高
|
||||
- 监听器模式支持扩展
|
||||
|
||||
**缺点**:
|
||||
- DispatchEngine 职责不清晰
|
||||
- 通用方法抽离不彻底
|
||||
- 状态同步需要手动保证
|
||||
|
||||
### 2.5.2 v2.0(架构重构)
|
||||
|
||||
**特点**:
|
||||
- 纯决策层的派单引擎
|
||||
- 统一的声明周期管理
|
||||
- 事件驱动架构
|
||||
- 兼容层保证平滑迁移
|
||||
|
||||
**优点**:
|
||||
- 职责清晰
|
||||
- 易于扩展
|
||||
- 易于测试
|
||||
- 向后兼容
|
||||
|
||||
**缺点**:
|
||||
- 组件数量增加
|
||||
- 学习曲线稍陡
|
||||
|
||||
---
|
||||
|
||||
## 2.6 关键里程碑
|
||||
|
||||
| 日期 | 里程碑 | 说明 |
|
||||
|------|--------|------|
|
||||
| 2025-12 | 阶段1完成 | 基础数据层 |
|
||||
| 2026-01 | 阶段2完成 | 工单引擎、状态机、REST API |
|
||||
| 2026-01-06 | 架构重构方案 | 重新定义组件职责 |
|
||||
| 2026-01 | 阶段3规划 | 智能调度系统(部分完成) |
|
||||
| 2026 Q2 | 安保条线 | 计划实施 |
|
||||
| 2026 Q3 | 工程条线 | 计划实施 |
|
||||
| 2026 Q4 | 客服条线 | 计划实施 |
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 3: 核心架构设计](./part3-核心架构设计.md)
|
||||
785
docs/ops-architecture/part3-核心架构设计.md
Normal file
785
docs/ops-architecture/part3-核心架构设计.md
Normal file
@@ -0,0 +1,785 @@
|
||||
# Part 3: 核心架构设计
|
||||
|
||||
本文档详细说明 Ops 模块的核心架构设计,包括服务职责划分、分层设计、Redis + MySQL 混合队列等关键技术方案。
|
||||
|
||||
---
|
||||
|
||||
## 3.1 服务职责划分
|
||||
|
||||
### 3.1.1 DispatchEngine(派单引擎)- 纯决策层
|
||||
|
||||
**职责定义**:
|
||||
|
||||
DispatchEngine 是一个纯决策层组件,只负责根据策略推荐最<E88D90><E69C80><EFBFBD>适的执行人员,不涉及状态管理、设备通知或业务逻辑。
|
||||
|
||||
**核心接口**:
|
||||
|
||||
```java
|
||||
public interface DispatchEngine {
|
||||
|
||||
/**
|
||||
* 推荐执行人员(核心方法)
|
||||
* @param context 派单上下文(区域、优先级、技能要求等)
|
||||
* @return 推荐的执行人员ID,如果没有合适的返回null
|
||||
*/
|
||||
AssigneeRecommendation recommendAssignee(DispatchContext context);
|
||||
|
||||
/**
|
||||
* 批量推荐(用于选择最优人员)
|
||||
*/
|
||||
List<AssigneeRecommendation> recommendAssignees(DispatchContext context, int limit);
|
||||
|
||||
/**
|
||||
* 判断是否可以打断当前任务
|
||||
*/
|
||||
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
|
||||
|
||||
/**
|
||||
* 注册派单策略
|
||||
*/
|
||||
void registerStrategy(DispatchStrategy strategy);
|
||||
|
||||
/**
|
||||
* 注册业务类型与策略的映射
|
||||
*/
|
||||
void registerBusinessTypeStrategy(String businessType, String strategyName);
|
||||
}
|
||||
```
|
||||
|
||||
**推荐结果(AssigneeRecommendation)**:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@Builder
|
||||
public class AssigneeRecommendation {
|
||||
private Long assigneeId; // 执行人员ID
|
||||
private String assigneeName; // 执行人员姓名
|
||||
private Integer score; // 匹配分数 0-100
|
||||
private String reason; // 推荐理由
|
||||
private Double distance; // 距离(米)
|
||||
private Integer currentLoad; // 当前任务数
|
||||
}
|
||||
```
|
||||
|
||||
**打断决策(InterruptDecision)**:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@Builder
|
||||
public class InterruptDecision {
|
||||
private boolean canInterrupt; // 是否可以打断
|
||||
private String reason; // 决策理由
|
||||
private Integer currentPriority; // 当前任务优先级
|
||||
private Integer urgentPriority; // 紧急任务优先级
|
||||
}
|
||||
```
|
||||
|
||||
**设计原则**:
|
||||
|
||||
1. **单一职责**:只负责决策,不管状态管理和业务逻辑
|
||||
2. **策略模式**:通过不同的策略实现不同的派单算法
|
||||
3. **可扩展性**:注册新策略,无需修改核心代码
|
||||
|
||||
### 3.1.2 OrderLifecycleManager(生命周期管理)- 状态同步器
|
||||
|
||||
**职责定义**:
|
||||
|
||||
OrderLifecycleManager 负责统一管理工单状态和队列状态的同步变更,确保两者的一致性。
|
||||
|
||||
**核心接口**:
|
||||
|
||||
```java
|
||||
public interface OrderLifecycleManager {
|
||||
|
||||
/**
|
||||
* 暂停工单(同步更新队列状态和工单状态)
|
||||
*/
|
||||
void pauseOrder(Long orderId, Long operatorId, String reason);
|
||||
|
||||
/**
|
||||
* 恢复工单(同步更新队列状态和工单状态)
|
||||
*/
|
||||
void resumeOrder(Long orderId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 打断工单(P0紧急任务场景)
|
||||
* 同步更新:工单状态(ARRIVED → PAUSED)+ 队列状态(PROCESSING → PAUSED)
|
||||
*/
|
||||
void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 完成工单(清理队列记录)
|
||||
*/
|
||||
void completeOrder(Long orderId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 取消工单
|
||||
*/
|
||||
void cancelOrder(Long orderId, Long operatorId, String reason);
|
||||
}
|
||||
```
|
||||
|
||||
**核心实现**:
|
||||
|
||||
```java
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
|
||||
|
||||
@Resource
|
||||
private OrderStateMachine orderStateMachine;
|
||||
|
||||
@Resource
|
||||
private OrderQueueService orderQueueService;
|
||||
|
||||
@Resource
|
||||
private OrderEventPublisher eventPublisher;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void pauseOrder(Long orderId, Long operatorId, String reason) {
|
||||
// 1. 查询工单和队列
|
||||
OpsOrderDO order = getOrderByOrderId(orderId);
|
||||
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
|
||||
|
||||
// 2. 验证状态
|
||||
validateCanPause(order, queueDTO);
|
||||
|
||||
// 3. 同步转换状态
|
||||
// 工单状态:ARRIVED → PAUSED
|
||||
orderStateMachine.transition(order, PAUSED,
|
||||
OperatorTypeEnum.CLEANER, operatorId, reason);
|
||||
|
||||
// 队列状态:PROCESSING → PAUSED
|
||||
if (queueDTO != null) {
|
||||
orderQueueService.pauseTask(queueDTO.getId());
|
||||
}
|
||||
|
||||
// 4. 发布事件
|
||||
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
|
||||
.orderId(orderId)
|
||||
.orderType(order.getOrderType())
|
||||
.oldStatus(ARRIVED)
|
||||
.newStatus(PAUSED)
|
||||
.operatorId(operatorId)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId) {
|
||||
// 1. 查询工单和队列
|
||||
OpsOrderDO order = getOrderByOrderId(orderId);
|
||||
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
|
||||
|
||||
// 2. 验证状态(只有 ARRIVED 可以被打断)
|
||||
if (!canInterrupt(order.getStatus())) {
|
||||
throw new IllegalStateException("工单状态不允许被打断: " + order.getStatus());
|
||||
}
|
||||
|
||||
// 3. 同步转换状态
|
||||
orderStateMachine.transition(order, PAUSED,
|
||||
OperatorTypeEnum.SYSTEM, operatorId, "被P0任务打断: " + urgentOrderId);
|
||||
|
||||
if (queueDTO != null) {
|
||||
orderQueueService.pauseTask(queueDTO.getId());
|
||||
orderQueueService.updateEventMessage(queueDTO.getId(),
|
||||
"被P0任务打断,紧急工单ID: " + urgentOrderId);
|
||||
}
|
||||
|
||||
// 4. 发布事件
|
||||
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
|
||||
.orderId(orderId)
|
||||
.orderType(order.getOrderType())
|
||||
.oldStatus(ARRIVED)
|
||||
.newStatus(PAUSED)
|
||||
.operatorId(operatorId)
|
||||
.addPayload("urgentOrderId", urgentOrderId)
|
||||
.addPayload("interruptReason", "P0_TASK_INTERRUPT")
|
||||
.build());
|
||||
}
|
||||
|
||||
// ... 其他方法实现
|
||||
}
|
||||
```
|
||||
|
||||
**关键特性**:
|
||||
|
||||
1. **事务保证**:所有状态变更在事务中完成,保证原子性
|
||||
2. **状态同步**:工单状态和队列状态同步更新
|
||||
3. **事件发布**:状态变更完成后发布领域事件
|
||||
|
||||
### 3.1.3 OrderStateMachine(状态机)- 纯状态管理
|
||||
|
||||
**职责定义**:
|
||||
|
||||
OrderStateMachine 负责验证状态转换规则和记录事件,不触发任何业务逻辑。
|
||||
|
||||
**核心接口**:
|
||||
|
||||
```java
|
||||
public interface OrderStateMachine {
|
||||
|
||||
/**
|
||||
* 执行状态转换(不触发任何业务逻辑)
|
||||
* @return 状态转换结果
|
||||
*/
|
||||
TransitionResult transition(OpsOrderDO order,
|
||||
WorkOrderStatusEnum newStatus,
|
||||
OperatorTypeEnum operatorType,
|
||||
Long operatorId,
|
||||
String remark);
|
||||
|
||||
/**
|
||||
* 检查状态转换是否合法
|
||||
*/
|
||||
boolean canTransition(WorkOrderStatusEnum from, WorkOrderStatusEnum to);
|
||||
|
||||
/**
|
||||
* 获取允许的转换目标
|
||||
*/
|
||||
Set<WorkOrderStatusEnum> getAllowedTransitions(WorkOrderStatusEnum from);
|
||||
}
|
||||
```
|
||||
|
||||
**状态转换规则**:
|
||||
|
||||
```java
|
||||
private static final Map<WorkOrderStatusEnum, Set<WorkOrderStatusEnum>> TRANSITIONS = Map.of(
|
||||
PENDING, Set.of(ASSIGNED, CANCELLED),
|
||||
ASSIGNED, Set.of(ARRIVED, CANCELLED),
|
||||
ARRIVED, Set.of(PAUSED, COMPLETED),
|
||||
PAUSED, Set.of(ARRIVED, CANCELLED),
|
||||
COMPLETED, Collections.emptySet(),
|
||||
CANCELLED, Collections.emptySet()
|
||||
);
|
||||
```
|
||||
|
||||
**设计原则**:
|
||||
|
||||
1. **纯状态管理**:只管状态转换,不管业务逻辑
|
||||
2. **事件溯源**:记录所有状态变更到事件表
|
||||
3. **规则验证**:验证状态转换的合法性
|
||||
|
||||
### 3.1.4 OrderEventPublisher(事件发布器)- 业务解耦器
|
||||
|
||||
**职责定义**:
|
||||
|
||||
OrderEventPublisher 负责发布工单相关的领域事件,实现业务解耦。
|
||||
|
||||
**核心接口**:
|
||||
|
||||
```java
|
||||
public interface OrderEventPublisher {
|
||||
|
||||
/**
|
||||
* 发布状态变更事件
|
||||
*/
|
||||
void publishStateChanged(OrderStateChangedEvent event);
|
||||
|
||||
/**
|
||||
* 发布工单创建事件
|
||||
*/
|
||||
void publishOrderCreated(OrderCreatedEvent event);
|
||||
|
||||
/**
|
||||
* 发布工单完成事件
|
||||
*/
|
||||
void publishOrderCompleted(OrderCompletedEvent event);
|
||||
}
|
||||
```
|
||||
|
||||
**领域事件定义**:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@Builder
|
||||
public class OrderStateChangedEvent {
|
||||
private Long orderId;
|
||||
private String orderType; // 工单类型 CLEAN/REPAIR/SECURITY
|
||||
private WorkOrderStatusEnum oldStatus;
|
||||
private WorkOrderStatusEnum newStatus;
|
||||
private OperatorTypeEnum operatorType;
|
||||
private Long operatorId;
|
||||
private LocalDateTime eventTime;
|
||||
private Map<String, Object> payload; // 扩展信息
|
||||
}
|
||||
```
|
||||
|
||||
**使用示例**:
|
||||
|
||||
```java
|
||||
// 保洁工单事件处理器
|
||||
@Component
|
||||
public class CleanOrderEventHandler {
|
||||
|
||||
@EventListener
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 只处理保洁类型的工单
|
||||
if (!"CLEAN".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getNewStatus()) {
|
||||
case ARRIVED:
|
||||
handleArrived(event);
|
||||
break;
|
||||
case PAUSED:
|
||||
handlePaused(event);
|
||||
break;
|
||||
case COMPLETED:
|
||||
handleCompleted(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.2 分层架构设计
|
||||
|
||||
### 3.2.1 模块结构图
|
||||
|
||||
```
|
||||
viewsh-module-ops/
|
||||
├── viewsh-module-ops-api/ # [契约层]
|
||||
│ ├── enums/ # 枚举类
|
||||
│ │ ├── WorkOrderTypeEnum.java # 工单类型
|
||||
│ │ ├── WorkOrderStatusEnum.java # 工单状态
|
||||
│ │ ├── PriorityEnum.java # 优先级
|
||||
│ │ ├── OrderQueueStatusEnum.java # 队列状态
|
||||
│ │ └── ...
|
||||
│ ├── dto/ # 数据传输对象
|
||||
│ │ ├── order/
|
||||
│ │ ├── queue/
|
||||
│ │ └── ...
|
||||
│ ├── event/ # 领域事件定义
|
||||
│ │ ├── OrderStateChangedEvent.java
|
||||
│ │ ├── OrderCreatedEvent.java
|
||||
│ │ └── OrderCompletedEvent.java
|
||||
│ └── api/ # 服务接口
|
||||
│ ├── dispatch/DispatchEngineService.java # 派单引擎接口
|
||||
│ └<><E29494>─ queue/OrderQueueService.java # 队列服务接口
|
||||
│
|
||||
├── viewsh-module-ops-biz/ # [通用业务层]
|
||||
│ ├── core/ # 核心组件
|
||||
│ │ ├── statemachine/ # 状态机
|
||||
│ │ │ ├── OrderStateMachine.java # 状态机接口
|
||||
│ │ │ └── OrderStateMachineImpl.java # 状态机实现
|
||||
│ │ ├── lifecycle/ # 生命周期管理
|
||||
│ │ │ ├── OrderLifecycleManager.java # 生命周期管理接口
|
||||
│ │ │ └── OrderLifecycleManagerImpl.java # 生命周期管理实现
|
||||
│ │ ├── dispatch/ # 派单引擎
|
||||
│ │ │ ├── DispatchEngine.java # 派单引擎接口
|
||||
│ │ │ ├── DispatchEngineImpl.java # 派单引擎实现
|
||||
│ │ │ ├── DispatchStrategy.java # 派单策略接口
|
||||
│ │ │ └── DispatchContext.java # 派单上下文
|
||||
│ │ ├── event/ # 事件处理
|
||||
│ │ │ ├── OrderEventPublisher.java # 事件发布器接口
|
||||
│ │ │ └── OrderEventPublisherImpl.java # 事件发布器实现
|
||||
│ │ └── queue/ # 队列管理
|
||||
│ │ ├── OrderQueueService.java # 队列服务接口
|
||||
│ │ └── OrderQueueServiceImpl.java # 队列服务实现
|
||||
│ │
|
||||
│ ├── service/ # 业务服务
|
||||
│ │ ├── OrderService.java # 通用工单服务接口
|
||||
│ │ └── OrderServiceImpl.java # 通用工单服务实现
|
||||
│ │
|
||||
│ └── dal/ # 数据访问层
|
||||
│ ├── dataobject/ # DO 实体
|
||||
│ └── mysql/ # Mapper 接口
|
||||
│
|
||||
├── viewsh-module-environment-biz/ # [保洁业务层]
|
||||
│ ├── service/ # 业务服务
|
||||
│ │ ├── cleanorder/
|
||||
│ │ │ ├── CleanOrderService.java # 保洁工单服务接口
|
||||
│ │ │ └── CleanOrderServiceImpl.java # 保洁工单服务实现
|
||||
│ │ ├── cleaner/
|
||||
│ │ │ └── CleanerStatusService.java # 保洁员状态服务
|
||||
│ │ └── dispatch/
|
||||
│ │ └── CleanerAreaAssignStrategy.java # 保洁派单策略
|
||||
│ │
|
||||
│ └── handler/ # 事件处理器
|
||||
│ └── CleanOrderEventHandler.java # 保洁工单事件处理器
|
||||
│
|
||||
├── viewsh-module-security-biz/ # [安保业务层]
|
||||
│ └── ...(待实现)
|
||||
│
|
||||
├── viewsh-module-facilities-biz/ # [工程业务层]
|
||||
│ └── ...(待实现)
|
||||
│
|
||||
├── viewsh-module-service-biz/ # [客服业务层]
|
||||
│ └── ...(待实现)
|
||||
│
|
||||
└── viewsh-module-ops-server/ # [宿主层]
|
||||
└── controller/ # REST API
|
||||
└── admin/
|
||||
└── OpsOrderController.java
|
||||
```
|
||||
|
||||
### 3.2.2 层级依赖关系
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 宿主层 (Server) │
|
||||
│ - REST API 聚合 │
|
||||
│ - 全局配置管理 │
|
||||
└──────────────────────────┬──────────────────────────────┘
|
||||
│
|
||||
┌──────────────────┴──────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ 业务线层 (Biz) │ │ 通用业务层 │
|
||||
│ - 业务逻辑 │ │ (Ops Biz) │
|
||||
│ - 业务规则 │ ─────────▶ │ - 核心引擎 │
|
||||
│ - 扩展字段 │ 依赖 │ - 公共逻辑 │
|
||||
└──────────────────┘ └────────┬─────────┘
|
||||
│
|
||||
│
|
||||
┌──────────────────────────────────┴──────────┐
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────────────────────────────────┴──────┐
|
||||
│ 契约层 (API) │
|
||||
│ - DTO 定义 │
|
||||
│ - 枚举定义 │
|
||||
│ - 服务接口 │
|
||||
│ - 领域事件定义 │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**依赖规则**:
|
||||
|
||||
1. **纵向依赖**:Server → Biz → API
|
||||
2. **横向依赖**:业务线 → 通用业务层 → 契约层
|
||||
3. **禁止依赖**:API 层不依赖任何业务模块
|
||||
|
||||
### 3.2.3 服务边界定义
|
||||
|
||||
**契约层(API)**:
|
||||
- 定义:DTO、枚举、服务接口、领域事件
|
||||
- 不依赖:任何业务模块
|
||||
- 被依赖:所有业务模块
|
||||
|
||||
**通用业务层(Biz)**:
|
||||
- 定义:工单引擎、状态机、队列、派单引擎
|
||||
- 依赖:API 层
|
||||
- 被依赖:业务线层
|
||||
- 职责:提供通用的工单管理能力
|
||||
|
||||
**业务线层**:
|
||||
- 定义:业务特定逻辑、派单策略、事件处理器
|
||||
- 依赖:API 层、通用业务层
|
||||
- 职责:实现特定业务类型的功能
|
||||
|
||||
---
|
||||
|
||||
## 3.3 Redis + MySQL 混合队列设计
|
||||
|
||||
### 3.3.1 为什么选择混合队列
|
||||
|
||||
| 对比项 | 纯 Redis | 纯 MySQL | Redis + MySQL |
|
||||
|--------|----------|----------|---------------|
|
||||
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| 持久化 | ❌ 需额外配置 | ✅ 天然支持 | ✅ MySQL 持久化 |
|
||||
| 优先级支持 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| 数据一致性 | ⚠️ 可能丢失 | ✅ 强一致性 | ✅ 最终一致性 |
|
||||
| 复杂查询 | ⚠️ 有限 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| 成本 | 中 | 低 | 中 |
|
||||
|
||||
**结论**:Redis + MySQL 混合队列兼顾性能、可靠性、功能。
|
||||
|
||||
### 3.3.2 架构设计
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 应用层 │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ 入队操作 │ │ 出队操作 │ │ 查询操作 │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
└─────────┼──────────────────┼──────────────────┼─────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Redis Queue Layer │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ Sorted Set: ops:order:queue:{cleanerId} │ │
|
||||
│ │ Score: 优先级分数 + 时间戳 │ │
|
||||
│ │ Member: JSON字符串 {queueId, orderId, priority...} │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ Hash: ops:order:queue:info:{queueId} │ │
|
||||
│ │ 存储完整的队列信息(用于查询单个记录) │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ Lock: ops:order:queue:lock:{cleanerId} │ │
|
||||
│ │ 分布式锁(防止并发操作) │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ ▲
|
||||
│ 异步同步 │ 查询历史
|
||||
▼ │
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ MySQL Persistent Layer │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ ops_order_queue 表 │ │
|
||||
│ │ - 持久化存储所有队列记录 │ │
|
||||
│ │ - 支持复杂查询、统计分析 │ │
|
||||
│ │ - 数据备份和恢复 │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.3.3 Redis 数据结构
|
||||
|
||||
**Sorted Set:实时队列**
|
||||
|
||||
**Key 格式**:`ops:order:queue:{cleanerId}`
|
||||
|
||||
**Score 计算公式**:
|
||||
```
|
||||
score = priorityScore + timestamp
|
||||
|
||||
priorityScore:
|
||||
P0 = 0
|
||||
P1 = 1000000
|
||||
P2 = 2000000
|
||||
|
||||
timestamp: 当前时间戳(毫秒)
|
||||
|
||||
结果:优先级高的排在前面,同优先级按时间排序
|
||||
```
|
||||
|
||||
**Member 格式**:
|
||||
```json
|
||||
{
|
||||
"queueId": 123,
|
||||
"orderId": 456,
|
||||
"priority": 1,
|
||||
"status": "WAITING",
|
||||
"enqueueTime": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
**操作命令**:
|
||||
```bash
|
||||
# 入队
|
||||
ZADD ops:order:queue:1001 score '{"queueId":123,...}'
|
||||
|
||||
# 出队(获取最高优先级任务)
|
||||
ZPOPMIN ops:order:queue:1001
|
||||
|
||||
# 查询待办数量
|
||||
ZCARD ops:order:queue:1001
|
||||
|
||||
# 查询前N个任务
|
||||
ZRANGE ops:order:queue:1001 0 N WITHSCORES
|
||||
```
|
||||
|
||||
**Hash:队列详细信息**
|
||||
|
||||
**Key 格式**:`ops:order:queue:info:{queueId}`
|
||||
|
||||
**用途**:快速查询单个队列记录的详细信息
|
||||
|
||||
### 3.3.4 数据同步策略
|
||||
|
||||
**写入流程(异步双写)**:
|
||||
|
||||
```java
|
||||
public Long enqueue(OrderQueueDTO dto) {
|
||||
// 1. 先写 MySQL(保证数据不丢失)
|
||||
Long queueId = mysqlMapper.insert(dto);
|
||||
|
||||
// 2. 再写 Redis(异步,失败不影响主流程)
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
redisQueueService.enqueue(dto);
|
||||
} catch (Exception e) {
|
||||
log.error("Redis 队列写入失败,依赖定时同步任务补偿", e);
|
||||
}
|
||||
});
|
||||
|
||||
return queueId;
|
||||
}
|
||||
```
|
||||
|
||||
**读取流程(优先 Redis)**:
|
||||
|
||||
```java
|
||||
public List<OrderQueueDTO> getTasksByUserId(Long userId) {
|
||||
// 1. 先查 Redis(实时数据)
|
||||
List<OrderQueueDTO> redisTasks = redisQueueService.getTasks(userId);
|
||||
|
||||
if (!CollectionUtils.isEmpty(redisTasks)) {
|
||||
return redisTasks;
|
||||
}
|
||||
|
||||
// 2. Redis 没有数据,查 MySQL 并同步到 Redis
|
||||
List<OrderQueueDTO> mysqlTasks = mysqlMapper.selectByUserId(userId);
|
||||
redisQueueService.batchEnqueue(mysqlTasks);
|
||||
|
||||
return mysqlTasks;
|
||||
}
|
||||
```
|
||||
|
||||
**定时同步(数据一致性保障)**:
|
||||
|
||||
```java
|
||||
@Scheduled(cron = "0 */5 * * * ?")
|
||||
public void syncMySQLToRedis() {
|
||||
// 查询最近1小时变更的数据
|
||||
List<OrderQueueDTO> changedTasks = mysqlMapper.selectChangedAfter(DateUtils.addHours(-1));
|
||||
// 同步到 Redis
|
||||
redisQueueService.batchEnqueue(changedTasks);
|
||||
log.info("定时同步完成,同步{}条记录", changedTasks.size());
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.4 设计模式应用
|
||||
|
||||
### 3.4.1 状态机模式
|
||||
|
||||
**定义**:
|
||||
允许对象在内部状态改变时改变其行为,对象看起来就像改变了它的类一样。
|
||||
|
||||
**应用场景**:
|
||||
- 工单状态转换
|
||||
- 状态转换规则验证
|
||||
- 事件记录
|
||||
|
||||
**优点**:
|
||||
- 集中管理状态转换规则
|
||||
- 避免状态散落在各个业务方法
|
||||
- 易于维护和扩展
|
||||
|
||||
### 3.4.2 观察者模式
|
||||
|
||||
**定义**:
|
||||
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
|
||||
|
||||
**应用场景**:
|
||||
- 状态变更事件监听
|
||||
- 业务线扩展(保洁、安保、工程、客服)
|
||||
|
||||
**实现方式**:
|
||||
- 旧版本:监听器(Listener)
|
||||
- 新版本:事件处理器(EventHandler)+ Spring @EventListener
|
||||
|
||||
**优点**:
|
||||
- 解耦核心引擎与业务逻辑
|
||||
- 业务线可独立扩展
|
||||
- 支持多监听器协同工作
|
||||
|
||||
### 3.4.3 策略模式
|
||||
|
||||
**定义**:
|
||||
定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。
|
||||
|
||||
**应用场景**:
|
||||
- 派单策略(区域优先、负载均衡、技能匹配)
|
||||
- 不同业务类型使用不同的派单策略
|
||||
|
||||
**实现方式**:
|
||||
```java
|
||||
public interface DispatchStrategy {
|
||||
AssigneeRecommendation recommend(DispatchContext context);
|
||||
}
|
||||
|
||||
@Component("cleanerAreaStrategy")
|
||||
public class CleanerAreaAssignStrategy implements DispatchStrategy {
|
||||
// 保洁派单策略实现
|
||||
}
|
||||
|
||||
@Component("repairSkillStrategy")
|
||||
public class RepairSkillAssignStrategy implements DispatchStrategy {
|
||||
// 维修派单策略实现
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 算法与客户端分离
|
||||
- 避免多重条件判断
|
||||
- 易于扩展新策略
|
||||
|
||||
### 3.4.4 模板方法模式
|
||||
|
||||
**定义**:
|
||||
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
|
||||
|
||||
**应用场景**:
|
||||
- 工单创建流程(创建 → 分配 → 通知)
|
||||
- 派单流程(推荐 → 入队 → 派发 → 通知)
|
||||
|
||||
**实现方式**:
|
||||
```java
|
||||
public abstract class AbstractDispatchService {
|
||||
|
||||
public final void dispatch(Long orderId) {
|
||||
// 1. 推荐执行人员
|
||||
AssigneeRecommendation recommendation = recommendAssignee(orderId);
|
||||
|
||||
// 2. 入队
|
||||
Long queueId = enqueue(orderId, recommendation.getAssigneeId());
|
||||
|
||||
// 3. 派发(子类实现)
|
||||
doDispatch(queueId, recommendation);
|
||||
|
||||
// 4. 通知(子类实现)
|
||||
notifyAssignee(queueId, recommendation);
|
||||
}
|
||||
|
||||
protected abstract void doDispatch(Long queueId, AssigneeRecommendation recommendation);
|
||||
protected abstract void notifyAssignee(Long queueId, AssigneeRecommendation recommendation);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.5 调用链设计
|
||||
|
||||
### 3.5.1 工单创建流程
|
||||
|
||||
```
|
||||
CleanOrderService.createAutoCleanOrder()
|
||||
│
|
||||
├─> DispatchEngine.recommendAssignee() 【通用】推荐保洁员
|
||||
│ └─> CleanerAreaAssignStrategy 【保洁】执行推荐逻辑
|
||||
│
|
||||
├─> OrderService.createOrder() 【通用】创建工单
|
||||
│ └─> OrderStateMachine.transition() 【通用】状态:PENDING
|
||||
│ └─> OrderEventPublisher.publish() 【通用】发布创建事件
|
||||
│
|
||||
├─> OrderQueueService.enqueue() 【通用】入队
|
||||
│
|
||||
└─> CleanOrderEventHandler.onCreate() 【保洁】处理创建事件
|
||||
└─> 根据保洁员状态决定是否立即派单
|
||||
```
|
||||
|
||||
### 3.5.2 工单打断流程
|
||||
|
||||
```
|
||||
CleanOrderService.upgradePriorityToP0()
|
||||
│
|
||||
├─> DispatchEngine.evaluateInterrupt() 【通用】判断是否可打断
|
||||
│
|
||||
├─> OrderLifecycleManager.interruptOrder() 【通用】打断工单
|
||||
│ ├─> OrderStateMachine.transition() 【通用】状态:ARRIVED → PAUSED
|
||||
│ ├─> OrderQueueService.pauseTask() 【通用】队列:PROCESSING → PAUSED
|
||||
│ └─> OrderEventPublisher.publish() 【通用】发布状态变更事件
|
||||
│
|
||||
├─> CleanOrderEventHandler.onInterrupt() 【保洁】处理打断事件
|
||||
│ ├─> 更新保洁员状态
|
||||
│ ├─> 记录扩展信息
|
||||
│ └─> 派发P0工单
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 4: 关键技术方案](./part4-关键技术方案.md)
|
||||
1136
docs/ops-architecture/part4-关键技术方案.md
Normal file
1136
docs/ops-architecture/part4-关键技术方案.md
Normal file
File diff suppressed because it is too large
Load Diff
977
docs/ops-architecture/part5-架构重构实践.md
Normal file
977
docs/ops-architecture/part5-架构重构实践.md
Normal file
@@ -0,0 +1,977 @@
|
||||
# Part 5: 架构重构实践
|
||||
|
||||
本文档详细记录 Ops 模块架构重构的完整实践过程,为未来的架构演进提供参考。
|
||||
|
||||
---
|
||||
|
||||
## 5.1 问题识别
|
||||
|
||||
在阶段2和阶段3的实施过程中,我们发现了一些架构问题,需要进行重构。
|
||||
|
||||
### 5.1.1 DispatchEngine 职责不清晰
|
||||
|
||||
**问题表现**:
|
||||
|
||||
DispatchEngine 接口定义了 13 个方法,但很多方法返回 null 或空实现:
|
||||
|
||||
```java
|
||||
public interface DispatchEngine {
|
||||
AssigneeRecommendation recommendAssignee(DispatchContext context);
|
||||
|
||||
// 以下方法职责不清晰
|
||||
void autoDispatch(Long orderId); // 实际只是调用 orderQueueService.startExecution()
|
||||
void handleOrderCompleted(Long orderId); // 业务逻辑,不应该在引擎中
|
||||
void notifyAssignee(Long assigneeId); // 设备通知,不应该在引擎中
|
||||
|
||||
// 还有 10 个类似的方法...
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
|
||||
1. **职责边界模糊**:派单引擎混合了决策、业务逻辑、设备通知等多种职责
|
||||
2. **命名误导**:`autoDispatch` 看起来是自动派单,实际只是更新队列状态
|
||||
3. **扩展困难**:新增业务类型时,需要修改 DispatchEngine 核心代码
|
||||
|
||||
**影响**:
|
||||
- 开发人员困惑:不知道哪些功能应该放在 DispatchEngine 中
|
||||
- 测试困难:职责过多导致单元测试复杂
|
||||
- 维护成本高:修改一处可能影响多个功能
|
||||
|
||||
### 5.1.2 通用方法抽离不彻底
|
||||
|
||||
**问题表现**:
|
||||
|
||||
`OpsOrderService` 只有基础的 CRUD 操作,暂停/恢复等通用功能散落在各处:
|
||||
|
||||
```java
|
||||
// OpsOrderService 中只有基础操作
|
||||
public interface OpsOrderService {
|
||||
Long createOrder(OrderCreateReqDTO reqDTO);
|
||||
void updateOrder(OrderUpdateReqDTO reqDTO);
|
||||
void deleteOrder(Long orderId);
|
||||
OpsOrderRespDTO getOrder(Long orderId);
|
||||
}
|
||||
|
||||
// 暂停/恢复逻辑直接在 CleanOrderService 中实现
|
||||
@Service
|
||||
public class CleanOrderServiceImpl {
|
||||
|
||||
public void pauseOrder(Long orderId) {
|
||||
// 1. 更新工单状态
|
||||
orderStateMachine.transition(order, PAUSED, ...);
|
||||
|
||||
// 2. 更新队列状态 - 手动保证同步
|
||||
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
|
||||
orderQueueService.pauseTask(queue.getId());
|
||||
|
||||
// 问题:状态同步逻辑需要业务层手动处理,容易遗漏
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
|
||||
1. **重复代码**:每个业务类型都要实现一遍暂停/恢复逻辑
|
||||
2. **一致性风险**:手动同步工单状态和队列状态,容易出现不一致
|
||||
3. **缺少统一入口**:没有统一的生命周期管理
|
||||
|
||||
**影响**:
|
||||
- 代码重复率高
|
||||
- 容易出现状态不一致的 bug
|
||||
- 新增业务类型时需要重复实现
|
||||
|
||||
### 5.1.3 Listener 设计不明确
|
||||
|
||||
**问题表现**:
|
||||
|
||||
监听器注册是全局的,无法区分业务类型,且存在循环依赖风险:
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class CleanOrderStateChangeListener implements OrderStateChangeListener {
|
||||
|
||||
@Resource
|
||||
private CleanOrderService cleanOrderService; // 依赖业务服务
|
||||
|
||||
@Override
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 问题1:无法区分业务类型,需要手动判断
|
||||
if (!"CLEAN".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 问题2:直接调用业务服务,可能产生循环依赖
|
||||
cleanOrderService.handleStateChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
// CleanOrderService 同时依赖 DispatchEngine
|
||||
@Service
|
||||
public class CleanOrderServiceImpl {
|
||||
@Resource
|
||||
private DispatchEngine dispatchEngine; // 可能产生循环依赖
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
|
||||
1. **监听器注册全局化**:所有监听器都会收到所有事件,需要手动过滤
|
||||
2. **缺少优先级定义**:多个监听器执行顺序不确定
|
||||
3. **循环依赖风险**:Listener → Service → Engine → Listener
|
||||
|
||||
**影响**:
|
||||
- 性能浪费(全局广播)
|
||||
- 循环依赖导致启动失败
|
||||
- 难以控制执行顺序
|
||||
|
||||
### 5.1.4 状态与队列状态同步问题
|
||||
|
||||
**问题表现**:
|
||||
|
||||
工单状态(`WorkOrderStatusEnum`)和队列状态(`OrderQueueStatusEnum`)是两个独立的状态机:
|
||||
|
||||
```java
|
||||
// 工单状态
|
||||
PENDING → ASSIGNED → ARRIVED → PAUSED → COMPLETED
|
||||
|
||||
// 队列状态
|
||||
WAITING → PROCESSING → PAUSED → REMOVED
|
||||
```
|
||||
|
||||
**同步困难**:
|
||||
|
||||
```java
|
||||
// 暂停工单时,需要同时更新两个状态
|
||||
public void pauseOrder(Long orderId) {
|
||||
// 步骤1:更新工单状态
|
||||
orderStateMachine.transition(order, WorkOrderStatusEnum.PAUSED, ...);
|
||||
|
||||
// 步骤2:查询队列
|
||||
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
|
||||
|
||||
// 步骤3:更新队列状态
|
||||
orderQueueService.updateStatus(queue.getId(), OrderQueueStatusEnum.PAUSED);
|
||||
|
||||
// 问题:如果步骤3失败,会导致状态不一致
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
|
||||
1. **双写一致性**:需要手动保证两个状态的同步
|
||||
2. **事务边界不清晰**:状态更新可能跨越多个服务
|
||||
3. **缺少统一协调**:没有统一的地方管理状态同步
|
||||
|
||||
**影响**:
|
||||
- 容易出现状态不一致
|
||||
- 调试困难
|
||||
- 数据修复成本高
|
||||
|
||||
---
|
||||
|
||||
## 5.2 重构方案设计
|
||||
|
||||
### 5.2.1 核心设计原则
|
||||
|
||||
**1. 单一职责原则(SRP)**:
|
||||
- 每个组件只负责一个明确的职责
|
||||
- DispatchEngine 只负责决策
|
||||
- OrderStateMachine 只负责状态管理
|
||||
- OrderLifecycleManager 负责状态同步
|
||||
|
||||
**2. 开闭原则(OCP)**:
|
||||
- 通过接口和策略模式支持扩展
|
||||
- 新增业务类型不需要修改核心代码
|
||||
|
||||
**3. 依赖倒置原则(DIP)**:
|
||||
- 业务层依赖通用层的抽象接口
|
||||
- 通用层不依赖具体业务实现
|
||||
|
||||
**4. 清晰分层**:
|
||||
- 通用能力下沉(状态机、队列、派单引擎)
|
||||
- 业务能力上浮(业务规则、事件处理)
|
||||
|
||||
### 5.2.2 组件职责重新定义
|
||||
|
||||
**DispatchEngine(派单引擎)- 纯决策层**:
|
||||
|
||||
```java
|
||||
public interface DispatchEngine {
|
||||
/**
|
||||
* 推荐执行人员(核心职责)
|
||||
*/
|
||||
AssigneeRecommendation recommendAssignee(DispatchContext context);
|
||||
|
||||
/**
|
||||
* 判断是否可以打断当前任务
|
||||
*/
|
||||
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
|
||||
|
||||
/**
|
||||
* 注册派单策略
|
||||
*/
|
||||
void registerStrategy(String businessType, AssignStrategy strategy);
|
||||
}
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- ✅ 根据上下文推荐最合适的执行人员
|
||||
- ✅ 评估是否可以打断当前任务
|
||||
- ✅ 管理派单策略
|
||||
- ❌ 不负责状态管理
|
||||
- ❌ 不负责设备通知
|
||||
- ❌ 不负责业务逻辑
|
||||
|
||||
**OrderLifecycleManager(生命周期管理)- 新增组件**:
|
||||
|
||||
```java
|
||||
public interface OrderLifecycleManager {
|
||||
/**
|
||||
* 暂停工单(同步更新工单状态和队列状态)
|
||||
*/
|
||||
void pauseOrder(Long orderId, Long operatorId, String reason);
|
||||
|
||||
/**
|
||||
* 恢复工单
|
||||
*/
|
||||
void resumeOrder(Long orderId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 打断工单(P0紧急任务场景)
|
||||
*/
|
||||
void interruptOrder(Long orderId, Long urgentOrderId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 完成工单
|
||||
*/
|
||||
void completeOrder(Long orderId, Long operatorId);
|
||||
}
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- ✅ 统一管理工单状态和队列状态的同步变更
|
||||
- ✅ 保证状态一致性(事务)
|
||||
- ✅ 发布状态变更事件
|
||||
- ❌ 不包含业务特定逻辑
|
||||
|
||||
**OrderStateMachine(状态机)- 优化**:
|
||||
|
||||
```java
|
||||
public interface OrderStateMachine {
|
||||
/**
|
||||
* 执行状态转换(不触发业务逻辑)
|
||||
*/
|
||||
TransitionResult transition(OpsOrderDO order,
|
||||
WorkOrderStatusEnum newStatus,
|
||||
OperatorTypeEnum operatorType,
|
||||
Long operatorId,
|
||||
String remark);
|
||||
|
||||
/**
|
||||
* 检查状态转换是否合法
|
||||
*/
|
||||
boolean canTransition(WorkOrderStatusEnum from, WorkOrderStatusEnum to);
|
||||
}
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- ✅ 验证状态转换规则
|
||||
- ✅ 记录状态变更事件
|
||||
- ❌ 不再包含监听器机制(移除)
|
||||
- ❌ 不触发任何业务逻辑
|
||||
|
||||
**OrderEventPublisher(事件发布器)- 新增组件**:
|
||||
|
||||
```java
|
||||
public interface OrderEventPublisher {
|
||||
/**
|
||||
* 发布状态变更事件
|
||||
*/
|
||||
void publishStateChanged(OrderStateChangedEvent event);
|
||||
|
||||
/**
|
||||
* 发布工单创建事件
|
||||
*/
|
||||
void publishOrderCreated(OrderCreatedEvent event);
|
||||
}
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- ✅ 发布工单相关的领域事件
|
||||
- ✅ 实现业务解耦
|
||||
|
||||
### 5.2.3 新架构设计图
|
||||
|
||||
**重构前**:
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ DispatchEngine │
|
||||
│ (职责混乱,13个方法) │
|
||||
│ - 推荐人员 │
|
||||
│ - 自动派单 │
|
||||
│ - 设备通知 │
|
||||
│ - 业务逻辑 │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ OrderStateMachine │
|
||||
│ (包含监听器,触发业务逻辑) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ CleanOrderService │
|
||||
│ (手动同步状态,容易出错) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**重构后**:
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ DispatchEngine │ │ OrderLifecycle │
|
||||
│ (纯决策层) │ │ Manager │
|
||||
│ - 推荐执行人 │ │ (状态同步器) │
|
||||
│ - 评估打断 │ │ - 暂停/恢复 │
|
||||
└────────┬─────────┘ │ - 打断/完成 │
|
||||
│ └────────┬─────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────────────────────────┐
|
||||
│ OrderStateMachine │
|
||||
│ (纯状态管理,不触发业务逻辑) │
|
||||
└────────┬───────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ OrderEventPublisher │
|
||||
│ (事件发布器) │
|
||||
└────────┬───────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ CleanOrderEventHandler │
|
||||
│ (保洁业务事件处理器) │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2.4 重构前后对比
|
||||
|
||||
| 方面 | 重构前 | 重构后 |
|
||||
|------|--------|--------|
|
||||
| 职责划分 | 模糊,混合多种职责 | 清晰,单一职责 |
|
||||
| 状态同步 | 业务层手动同步 | OrderLifecycleManager 统一管理 |
|
||||
| 业务扩展 | 修改核心代码 | 实现事件处理器 |
|
||||
| 循环依赖 | 存在风险 | 通过事件解耦 |
|
||||
| 测试难度 | 困难,职责过多 | 容易,职责单一 |
|
||||
|
||||
---
|
||||
|
||||
## 5.3 重构实施过程
|
||||
|
||||
### 5.3.1 Phase 1: 基础重构
|
||||
|
||||
**任务 1:重构 DispatchEngine 接口**
|
||||
|
||||
**Before**:
|
||||
```java
|
||||
public interface DispatchEngine {
|
||||
AssigneeRecommendation recommendAssignee(DispatchContext context);
|
||||
void autoDispatch(Long orderId);
|
||||
void handleOrderCompleted(Long orderId);
|
||||
void notifyAssignee(Long assigneeId);
|
||||
// ... 还有 9 个方法
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```java
|
||||
public interface DispatchEngine {
|
||||
/**
|
||||
* 推荐执行人员(核心方法)
|
||||
*/
|
||||
AssigneeRecommendation recommendAssignee(DispatchContext context);
|
||||
|
||||
/**
|
||||
* 评估是否可以打断
|
||||
*/
|
||||
InterruptDecision evaluateInterrupt(Long currentAssigneeId, DispatchContext urgentContext);
|
||||
|
||||
/**
|
||||
* 注册派单策略
|
||||
*/
|
||||
void registerStrategy(String businessType, AssignStrategy strategy);
|
||||
}
|
||||
```
|
||||
|
||||
**任务 2:创建 OrderLifecycleManager**
|
||||
|
||||
```java
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OrderLifecycleManagerImpl implements OrderLifecycleManager {
|
||||
|
||||
@Resource
|
||||
private OrderStateMachine orderStateMachine;
|
||||
|
||||
@Resource
|
||||
private OrderQueueService orderQueueService;
|
||||
|
||||
@Resource
|
||||
private OrderEventPublisher eventPublisher;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void pauseOrder(Long orderId, Long operatorId, String reason) {
|
||||
// 1. 查询工单和队列
|
||||
OpsOrderDO order = getOrderByOrderId(orderId);
|
||||
OrderQueueDTO queueDTO = orderQueueService.getByOpsOrderId(orderId);
|
||||
|
||||
// 2. 验证状态
|
||||
validateCanPause(order, queueDTO);
|
||||
|
||||
// 3. 同步转换状态(事务保证原子性)
|
||||
orderStateMachine.transition(order, PAUSED, operatorType, operatorId, reason);
|
||||
|
||||
if (queueDTO != null) {
|
||||
orderQueueService.pauseTask(queueDTO.getId());
|
||||
}
|
||||
|
||||
// 4. 发布事件
|
||||
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
|
||||
.orderId(orderId)
|
||||
.orderType(order.getOrderType())
|
||||
.oldStatus(ARRIVED)
|
||||
.newStatus(PAUSED)
|
||||
.operatorId(operatorId)
|
||||
.build());
|
||||
}
|
||||
|
||||
// ... 其他方法实现
|
||||
}
|
||||
```
|
||||
|
||||
**任务 3:重构 OrderStateMachine(移除监听器)**
|
||||
|
||||
**Before**:
|
||||
```java
|
||||
@Service
|
||||
public class OrderStateMachine {
|
||||
|
||||
private List<OrderStateChangeListener> listeners = new ArrayList<>();
|
||||
|
||||
public void registerListener(OrderStateChangeListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void transition(OpsOrderDO order, WorkOrderStatusEnum newStatus, ...) {
|
||||
// 更新状态
|
||||
order.setStatus(newStatus.name());
|
||||
orderMapper.updateById(order);
|
||||
|
||||
// 触发监听器
|
||||
for (OrderStateChangeListener listener : listeners) {
|
||||
listener.onStateChanged(new OrderStateChangedEvent(...));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```java
|
||||
@Component
|
||||
public class OrderStateMachine {
|
||||
|
||||
@Resource
|
||||
private OrderEventPublisher eventPublisher; // 改为使用事件发布器
|
||||
|
||||
@Transactional
|
||||
public void transition(OpsOrderDO order, WorkOrderStatusEnum newStatus, ...) {
|
||||
// 1. 验证状态转换
|
||||
validateTransition(currentStatus, newStatus);
|
||||
|
||||
// 2. 更新状态
|
||||
order.setStatus(newStatus.name());
|
||||
orderMapper.updateById(order);
|
||||
|
||||
// 3. 记录事件
|
||||
eventService.recordEvent(orderId, oldStatus, newStatus, ...);
|
||||
|
||||
// 4. 发布事件(替代监听器)
|
||||
eventPublisher.publishStateChanged(OrderStateChangedEvent.builder()
|
||||
.orderId(order.getId())
|
||||
.oldStatus(oldStatus)
|
||||
.newStatus(newStatus)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**任务 4:创建 OrderEventPublisher**
|
||||
|
||||
```java
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OrderEventPublisherImpl implements OrderEventPublisher {
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Override
|
||||
@Async // 异步发布,不阻塞主流程
|
||||
public void publishStateChanged(OrderStateChangedEvent event) {
|
||||
try {
|
||||
applicationEventPublisher.publishEvent(event);
|
||||
log.info("状态变更事件已发布: orderId={}, {} -> {}",
|
||||
event.getOrderId(), event.getOldStatus(), event.getNewStatus());
|
||||
} catch (Exception e) {
|
||||
log.error("状态变更事件发布失败: orderId={}", event.getOrderId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3.2 Phase 2: 业务层改造
|
||||
|
||||
**任务 1:创建事件处理器替代监听器**
|
||||
|
||||
**Before(Listener)**:
|
||||
```java
|
||||
@Component
|
||||
public class CleanOrderStateChangeListener implements OrderStateChangeListener {
|
||||
|
||||
@Resource
|
||||
private CleanOrderService cleanOrderService;
|
||||
|
||||
@Override
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
if (!"CLEAN".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
cleanOrderService.handleStateChanged(event);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**After(EventHandler)**:
|
||||
```java
|
||||
@Component
|
||||
public class CleanOrderEventHandler {
|
||||
|
||||
@Resource
|
||||
private CleanerStatusService cleanerStatusService;
|
||||
|
||||
@Resource
|
||||
private BadgeNotificationService badgeNotificationService;
|
||||
|
||||
@EventListener
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 只处理保洁类型的工单
|
||||
if (!"CLEAN".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getNewStatus()) {
|
||||
case ARRIVED:
|
||||
handleArrived(event);
|
||||
break;
|
||||
case PAUSED:
|
||||
handlePaused(event);
|
||||
break;
|
||||
case COMPLETED:
|
||||
handleCompleted(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleArrived(OrderStateChangedEvent event) {
|
||||
// 更新保洁员状态为 BUSY
|
||||
cleanerStatusService.updateStatus(event.getOperatorId(), CleanerStatusEnum.BUSY);
|
||||
|
||||
// 发送工牌通知
|
||||
badgeNotificationService.notifyWorkStart(event.getOperatorId(), event.getOrderId());
|
||||
}
|
||||
|
||||
private void handleCompleted(OrderStateChangedEvent event) {
|
||||
// 更新保洁员状态为 IDLE
|
||||
cleanerStatusService.updateStatus(event.getOperatorId(), CleanerStatusEnum.IDLE);
|
||||
|
||||
// 自动派发下一个任务
|
||||
dispatchEngine.autoDispatchNext(event.getOrderId(), event.getOperatorId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**任务 2:重构 CleanOrderService**
|
||||
|
||||
**Before**:
|
||||
```java
|
||||
@Service
|
||||
public class CleanOrderServiceImpl {
|
||||
|
||||
public void pauseOrder(Long orderId) {
|
||||
// 手动同步状态
|
||||
orderStateMachine.transition(order, PAUSED, ...);
|
||||
OrderQueueDTO queue = orderQueueService.getByOpsOrderId(orderId);
|
||||
orderQueueService.pauseTask(queue.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```java
|
||||
@Service
|
||||
public class CleanOrderServiceImpl {
|
||||
|
||||
@Resource
|
||||
private OrderLifecycleManager orderLifecycleManager; // 使用生命周期管理器
|
||||
|
||||
public void pauseOrder(Long orderId, String reason) {
|
||||
// 委托给生命周期管理器,保证状态同步
|
||||
orderLifecycleManager.pauseOrder(orderId, SecurityUtils.getLoginUserId(), reason);
|
||||
|
||||
// 业务特定逻辑
|
||||
updateCleanerStatus(orderId, CleanerStatusEnum.PAUSED);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3.3 Phase 3: 优化与验证
|
||||
|
||||
**任务 1:性能优化**
|
||||
|
||||
**优化点 1:事件异步发布**
|
||||
```java
|
||||
@Service
|
||||
public class OrderEventPublisherImpl {
|
||||
|
||||
@Async("eventExecutor") // 使用独立线程池
|
||||
public void publishStateChanged(OrderStateChangedEvent event) {
|
||||
applicationEventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public class AsyncConfig {
|
||||
|
||||
@Bean("eventExecutor")
|
||||
public Executor eventExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5);
|
||||
executor.setMaxPoolSize(10);
|
||||
executor.setQueueCapacity(100);
|
||||
executor.setThreadNamePrefix("event-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优化点 2:批量状态更新**
|
||||
```java
|
||||
public void batchPauseOrders(List<Long> orderIds, String reason) {
|
||||
// 批量更新,减少数据库交互
|
||||
orderLifecycleManager.batchPause(orderIds, operatorId, reason);
|
||||
}
|
||||
```
|
||||
|
||||
**任务 2:集成测试**
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
class OrderLifecycleManagerIntegrationTest {
|
||||
|
||||
@Resource
|
||||
private OrderLifecycleManager orderLifecycleManager;
|
||||
|
||||
@Resource
|
||||
private OpsOrderMapper orderMapper;
|
||||
|
||||
@Resource
|
||||
private OpsOrderQueueMapper queueMapper;
|
||||
|
||||
@Test
|
||||
void testPauseOrder_ShouldSyncBothStatuses() {
|
||||
// Given
|
||||
Long orderId = createTestOrder();
|
||||
|
||||
// When
|
||||
orderLifecycleManager.pauseOrder(orderId, 1L, "测试暂停");
|
||||
|
||||
// Then
|
||||
OpsOrderDO order = orderMapper.selectById(orderId);
|
||||
assertEquals("PAUSED", order.getStatus());
|
||||
|
||||
OpsOrderQueueDO queue = queueMapper.selectByOpsOrderId(orderId);
|
||||
assertEquals("PAUSED", queue.getQueueStatus());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**任务 3:文档更新**
|
||||
|
||||
更新以下文档:
|
||||
- [x] part2-架构演进史.md - 添加重构历程
|
||||
- [x] part3-核心架构设计.md - 更新服务职责
|
||||
- [ ] part5-架构重构实践.md - 本文档
|
||||
- [ ] API 文档 - 更新接口说明
|
||||
|
||||
---
|
||||
|
||||
## 5.4 重构收获与反思
|
||||
|
||||
### 5.4.1 成功经验
|
||||
|
||||
**1. 职责分离带来清晰度**
|
||||
|
||||
重构后,每个组件的职责非常明确:
|
||||
- DispatchEngine:只做决策,不管执行
|
||||
- OrderLifecycleManager:只管状态同步,不管业务逻辑
|
||||
- OrderEventPublisher:只管事件发布,不管事件处理
|
||||
- EventHandler:只处理特定业务的事件
|
||||
|
||||
**收益**:
|
||||
- 新人快速理解代码
|
||||
- 单元测试简单明确
|
||||
- 问题定位更快
|
||||
|
||||
**2. 事件驱动架构提升扩展性**
|
||||
|
||||
通过事件驱动,新增业务类型只需:
|
||||
```java
|
||||
// 1. 实现事件处理器
|
||||
@Component
|
||||
public class RepairOrderEventHandler {
|
||||
@EventListener
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
if (!"REPAIR".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
// 处理维修工单的特定逻辑
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 无需修改核心引擎代码
|
||||
```
|
||||
|
||||
**收益**:
|
||||
- 核心引擎稳定,无需修改
|
||||
- 业务团队独立开发
|
||||
- 新功能上线快
|
||||
|
||||
**3. 兼容性保证平滑迁移**
|
||||
|
||||
保留旧实现作为兼容层:
|
||||
```java
|
||||
@Deprecated
|
||||
public class OldDispatchEngine {
|
||||
// 保留旧接口,内部委托给新实现
|
||||
public void autoDispatch(Long orderId) {
|
||||
orderLifecycleManager.dispatch(orderId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**收益**:
|
||||
- 业务流程不中断
|
||||
- 逐步迁移,风险可控
|
||||
- 回滚方案简单
|
||||
|
||||
### 5.4.2 踩坑记录
|
||||
|
||||
**坑 1:循环依赖问题**
|
||||
|
||||
**问题**:
|
||||
```java
|
||||
@Service
|
||||
public class DispatchEngineImpl {
|
||||
@Resource
|
||||
private CleanOrderService cleanOrderService; // DispatchEngine -> CleanOrderService
|
||||
}
|
||||
|
||||
@Service
|
||||
public class CleanOrderServiceImpl {
|
||||
@Resource
|
||||
private DispatchEngine dispatchEngine; // CleanOrderService -> DispatchEngine
|
||||
}
|
||||
|
||||
// 启动报错:Circular dependency detected
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```java
|
||||
@Service
|
||||
public class DispatchEngineImpl {
|
||||
@Lazy // 使用延迟注入
|
||||
@Resource
|
||||
private CleanOrderService cleanOrderService;
|
||||
}
|
||||
```
|
||||
|
||||
**更好的解决方案**:
|
||||
```java
|
||||
// 通过事件解耦,避免直接依赖
|
||||
@Component
|
||||
public class CleanOrderEventHandler {
|
||||
@EventListener
|
||||
public void onOrderDispatched(OrderDispatchedEvent event) {
|
||||
// 处理派单事件
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**坑 2:状态同步的事务边界**
|
||||
|
||||
**问题**:
|
||||
```java
|
||||
@Transactional
|
||||
public void pauseOrder(Long orderId) {
|
||||
// 更新工单状态(在事务内)
|
||||
orderStateMachine.transition(order, PAUSED, ...);
|
||||
|
||||
// 更新队列状态(调用另一个服务,可能在另一个事务)
|
||||
orderQueueService.pauseTask(queueId); // 如果失败,工单状态已提交
|
||||
}
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```java
|
||||
@Transactional(rollbackFor = Exception.class) // 确保所有异常都回滚
|
||||
public void pauseOrder(Long orderId) {
|
||||
// 所有数据库操作在同一个事务中
|
||||
orderStateMachine.transition(order, PAUSED, ...);
|
||||
orderQueueService.pauseTask(queueId);
|
||||
|
||||
// 事件发布在事务提交后(使用 @TransactionalEventListener)
|
||||
eventPublisher.publishStateChanged(...);
|
||||
}
|
||||
```
|
||||
|
||||
**坑 3:事件处理器的执行顺序**
|
||||
|
||||
**问题**:
|
||||
```java
|
||||
@Component
|
||||
public class CleanOrderEventHandler {
|
||||
@EventListener
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 处理器A:更新保洁员状态
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
public class CleanOrderNotificationHandler {
|
||||
@EventListener
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 处理器B:发送通知
|
||||
// 问题:可能在处理器A之前执行
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```java
|
||||
@Component
|
||||
public class CleanOrderEventHandler {
|
||||
|
||||
@EventListener
|
||||
@Order(1) // 优先级1,先执行
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 更新保洁员状态
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
public class CleanOrderNotificationHandler {
|
||||
|
||||
@EventListener
|
||||
@Order(2) // 优先级2,后执行
|
||||
public void onStateChanged(OrderStateChangedEvent event) {
|
||||
// 发送通知
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4.3 改进建议
|
||||
|
||||
**1. 从设计阶段就明确职责**
|
||||
|
||||
在编码前:
|
||||
- 使用 UML 类图展示组件关系
|
||||
- 定义清晰的接口契约
|
||||
- 明确依赖方向
|
||||
|
||||
**2. 持续重构,不要积累技术债务**
|
||||
|
||||
建立机制:
|
||||
- 每季度进行架构 Review
|
||||
- 及时重构不合理的设计
|
||||
- 不要等到问题严重才重构
|
||||
|
||||
**3. 完善测试,保证重构质量**
|
||||
|
||||
测试策略:
|
||||
- 单元测试覆盖核心逻辑
|
||||
- 集成测试覆盖端到端流程
|
||||
- 重构前后对比测试
|
||||
|
||||
---
|
||||
|
||||
## 5.5 兼容性处理
|
||||
|
||||
### 5.5.1 保证平滑迁移
|
||||
|
||||
**兼容层设计**:
|
||||
|
||||
```java
|
||||
@Deprecated
|
||||
@Service("oldDispatchEngine")
|
||||
public class DispatchEngineAdapter implements OldDispatchEngine {
|
||||
|
||||
@Resource
|
||||
private DispatchEngine newDispatchEngine;
|
||||
|
||||
@Resource
|
||||
private OrderLifecycleManager orderLifecycleManager;
|
||||
|
||||
@Override
|
||||
public void autoDispatch(Long orderId) {
|
||||
// 委托给新实现
|
||||
orderLifecycleManager.dispatch(orderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAssignee(Long assigneeId) {
|
||||
// 废弃方法,记录警告
|
||||
log.warn("notifyAssignee() is deprecated, please use EventHandler instead");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5.2 逐步切换策略
|
||||
|
||||
**阶段 1:保留旧接口**
|
||||
- 新旧实现并存
|
||||
- 旧接口内部调用新实现
|
||||
- 添加 @Deprecated 注解
|
||||
|
||||
**阶段 2:迁移业务代码**
|
||||
- 逐个模块迁移到新接口
|
||||
- 测试验证功能正常
|
||||
|
||||
**阶段 3:清理旧代码**
|
||||
- 确认所有业务已迁移
|
||||
- 删除旧接口和兼容层
|
||||
|
||||
### 5.5.3 版本兼容性
|
||||
|
||||
**版本规划**:
|
||||
- v2.0: 新架构发布,旧接口标记为 Deprecated
|
||||
- v2.1: 旧接口保留,持续迁移
|
||||
- v3.0: 删除旧接口
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 6: 性能与可靠性](./part6-性能与可靠性.md)
|
||||
301
docs/ops-architecture/part6-性能与可靠性.md
Normal file
301
docs/ops-architecture/part6-性能与可靠性.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# Part 6: 性能与可靠性
|
||||
|
||||
本文档详细说明 Ops 模块的性能优化策略和可靠性保障措施。
|
||||
|
||||
---
|
||||
|
||||
## 6.1 性能优化
|
||||
|
||||
### 6.1.1 Redis 缓存策略
|
||||
|
||||
**热点数据缓存**:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class CleanerStatusService {
|
||||
|
||||
@Cacheable(value = "cleaner:status", key = "#userId", unless = "#result == null")
|
||||
public CleanerStatusDO getStatus(Long userId) {
|
||||
return cleanerStatusMapper.selectByUserId(userId);
|
||||
}
|
||||
|
||||
@CacheEvict(value = "cleaner:status", key = "#userId")
|
||||
public void updateStatus(Long userId, CleanerStatusEnum status) {
|
||||
cleanerStatusMapper.updateStatus(userId, status);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**缓存更新策略**:
|
||||
- **Cache Aside**: 先更新数据库,再删除缓存
|
||||
- **过期时间**: 5-30 分钟,避免数据过期
|
||||
- **空值缓存**: 防止缓存穿透
|
||||
|
||||
### 6.1.2 数据库优化
|
||||
|
||||
**索引设计**:
|
||||
|
||||
```sql
|
||||
-- 工单表索引
|
||||
CREATE INDEX idx_status ON ops_order(status);
|
||||
CREATE INDEX idx_assignee_id ON ops_order(assignee_id);
|
||||
CREATE INDEX idx_priority_status ON ops_order(priority, status);
|
||||
CREATE INDEX idx_create_time ON ops_order(create_time);
|
||||
|
||||
-- 队列表索引
|
||||
CREATE UNIQUE INDEX uk_user_order ON ops_order_queue(user_id, ops_order_id);
|
||||
CREATE INDEX idx_status ON ops_order_queue(status);
|
||||
CREATE INDEX idx_priority ON ops_order_queue(priority);
|
||||
```
|
||||
|
||||
**查询优化**:
|
||||
|
||||
```java
|
||||
// ❌ N+1 查询
|
||||
List<OpsOrderDO> orders = orderMapper.selectList(wrapper);
|
||||
for (OpsOrderDO order : orders) {
|
||||
UserDO user = userMapper.selectById(order.getAssigneeId()); // N次查询
|
||||
}
|
||||
|
||||
// ✅ 批量查询
|
||||
List<OpsOrderDO> orders = orderMapper.selectList(wrapper);
|
||||
Set<Long> userIds = orders.stream().map(OpsOrderDO::getAssigneeId).collect(Collectors.toSet());
|
||||
List<UserDO> users = userMapper.selectBatchIds(userIds); // 1次查询
|
||||
Map<Long, UserDO> userMap = users.stream().collect(Collectors.toMap(UserDO::getId, u -> u));
|
||||
```
|
||||
|
||||
### 6.1.3 异步处理
|
||||
|
||||
**事件异步发布**:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class OrderEventPublisherImpl {
|
||||
|
||||
@Async("eventExecutor")
|
||||
public void publishStateChanged(OrderStateChangedEvent event) {
|
||||
applicationEventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public class AsyncConfig {
|
||||
|
||||
@Bean("eventExecutor")
|
||||
public Executor eventExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5);
|
||||
executor.setMaxPoolSize(10);
|
||||
executor.setQueueCapacity(100);
|
||||
executor.setThreadNamePrefix("event-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.1.4 批量操作优化
|
||||
|
||||
```java
|
||||
// 批量入队
|
||||
public void batchEnqueue(List<OrderQueueDTO> queueList) {
|
||||
// MySQL 批量插入
|
||||
orderQueueMapper.insertBatch(queueList);
|
||||
|
||||
// Redis 批量写入
|
||||
CompletableFuture.runAsync(() -> {
|
||||
redisQueueService.batchEnqueue(queueList);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.2 并发控制
|
||||
|
||||
### 6.2.1 分布式锁
|
||||
|
||||
**Redis 分布式锁**:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class OrderQueueService {
|
||||
|
||||
public void enqueue(Long orderId, Long userId) {
|
||||
String lockKey = "queue:lock:" + userId;
|
||||
|
||||
// 获取分布式锁
|
||||
Boolean lock = redisTemplate.opsForValue()
|
||||
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
|
||||
|
||||
if (Boolean.TRUE.equals(lock)) {
|
||||
try {
|
||||
// 执行入队操作
|
||||
doEnqueue(orderId, userId);
|
||||
} finally {
|
||||
// 释放锁
|
||||
redisTemplate.delete(lockKey);
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException("系统繁忙,请稍后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2.2 乐观锁
|
||||
|
||||
```java
|
||||
@TableName("ops_order")
|
||||
public class OpsOrderDO {
|
||||
private Long id;
|
||||
private String status;
|
||||
|
||||
@Version // 乐观锁字段
|
||||
private Integer version;
|
||||
}
|
||||
|
||||
// 更新时自动检查版本号
|
||||
orderMapper.updateById(order); // 如果 version 不匹配,更新失败
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.3 可靠性保障
|
||||
|
||||
### 6.3.1 Redis + MySQL 双写策略
|
||||
|
||||
**写入流程**:
|
||||
|
||||
```java
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long enqueue(OrderQueueDTO dto) {
|
||||
// 1. 先写 MySQL(保证数据不丢失)
|
||||
Long queueId = orderQueueMapper.insert(dto);
|
||||
|
||||
// 2. 异步写 Redis(失败不影响主流程)
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
redisQueueService.enqueue(dto);
|
||||
} catch (Exception e) {
|
||||
log.error("Redis 队列写入失败,依赖定时同步任务补偿", e);
|
||||
}
|
||||
});
|
||||
|
||||
return queueId;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3.2 定时同步任务
|
||||
|
||||
```java
|
||||
@Scheduled(cron = "0 */5 * * * ?") // 每5分钟同步一次
|
||||
public void syncMySQLToRedis() {
|
||||
// 查询最近变更的数据
|
||||
List<OrderQueueDTO> changedTasks =
|
||||
orderQueueMapper.selectChangedAfter(DateUtils.addHours(-1));
|
||||
|
||||
// 同步到 Redis
|
||||
redisQueueService.batchEnqueue(changedTasks);
|
||||
|
||||
log.info("定时同步完成,同步{}条记录", changedTasks.size());
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3.3 故障恢复机制
|
||||
|
||||
**Redis 宕机降级**:
|
||||
|
||||
```java
|
||||
public List<OrderQueueDTO> getTasksByUserId(Long userId) {
|
||||
try {
|
||||
// 1. 优先从 Redis 获取
|
||||
List<OrderQueueDTO> redisTasks = redisQueueService.getTasksByUserId(userId);
|
||||
if (redisTasks != null && !redisTasks.isEmpty()) {
|
||||
return redisTasks;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Redis 查询失败,降级到 MySQL", e);
|
||||
}
|
||||
|
||||
// 2. Redis 失败,从 MySQL 获取
|
||||
return orderQueueMapper.selectListByUserId(userId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.4 监控与告警
|
||||
|
||||
### 6.4.1 关键指标监控
|
||||
|
||||
**系统指标**:
|
||||
- QPS(每秒请求数)
|
||||
- 响应时间(P50、P95、P99)
|
||||
- 错误率
|
||||
- 线程池使用率
|
||||
|
||||
**业务指标**:
|
||||
- 工单创建速率
|
||||
- 派单成功率
|
||||
- 平均响应时长
|
||||
- 队列积压数量
|
||||
|
||||
### 6.4.2 告警规则
|
||||
|
||||
| 告警项 | 触发条件 | 级别 | 处理方式 |
|
||||
|--------|---------|------|----------|
|
||||
| P0 工单超时 | 超时未接单 > 3 分钟 | P0 | 钉钉 + 短信 |
|
||||
| 队列积压 | 某区域积压 > 10 个 | P1 | 钉钉通知 |
|
||||
| 派单失败率 | 失败率 > 5% | P1 | 钉钉通知 |
|
||||
| API 错误率 | 错误率 > 1% | P2 | 邮件通知 |
|
||||
|
||||
---
|
||||
|
||||
## 6.5 容灾设计
|
||||
|
||||
### 6.5.1 服务降级
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class DispatchEngineService {
|
||||
|
||||
@Resource
|
||||
private DispatchEngine dispatchEngine;
|
||||
|
||||
public DispatchResult dispatch(DispatchContext context) {
|
||||
try {
|
||||
// 正常派单流程
|
||||
return dispatchEngine.dispatch(context);
|
||||
} catch (Exception e) {
|
||||
log.error("自动派单失败,降级为手动派单", e);
|
||||
|
||||
// 降级方案:创建待分配工单
|
||||
return createPendingOrder(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5.2 数据备份
|
||||
|
||||
**MySQL 备份**:
|
||||
```bash
|
||||
# 每日备份
|
||||
mysqldump -h localhost -u root -p aiot_ops > backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
**Redis 持久化**:
|
||||
```bash
|
||||
# redis.conf
|
||||
save 900 1 # 900秒内至少1个key变化,执行BGSAVE
|
||||
save 300 10 # 300秒内至少10个key变化
|
||||
save 60 10000 # 60秒内至少10000个key变化
|
||||
|
||||
appendonly yes # 开启 AOF
|
||||
appendfsync everysec # 每秒同步
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**下一章**:[Part 7: 扩展性设计](./part7-扩展性设计.md)
|
||||
51
docs/ops-architecture/part7-扩展性设计.md
Normal file
51
docs/ops-architecture/part7-扩展性设计.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Part 7: 扩展性设计
|
||||
|
||||
本章节指导开发者如何基于 Ops 核心引擎扩展新的业务条线(如安保、工程、客服)或自定义派单逻辑。
|
||||
|
||||
## 7.1 新增业务条线流程
|
||||
|
||||
新增一个业务条线(如安保 `SECURITY`)需遵循以下步骤:
|
||||
|
||||
### 7.1.1 定义业务标识
|
||||
在 `ops-api` 模块的 `WorkOrderTypeEnum` 中新增业务枚举值:
|
||||
```java
|
||||
SECURITY("SECURITY", "安保")
|
||||
```
|
||||
|
||||
### 7.1.2 创建业务扩展表
|
||||
根据业务特有字段创建扩展表(参考 `ops_order_clean_ext`),并通过 `ops_order_id` 与主表关联。
|
||||
- **示例**:`ops_order_security_ext` 存储巡更路线 ID、巡更点位数量等。
|
||||
|
||||
### 7.1.3 实现业务逻辑编排
|
||||
在新的业务模块(如 `security-biz`)中创建 `SecurityOrderService`,调用通用的 `OpsOrderService` 完成基础 CRUD,并在业务层处理特有逻辑。
|
||||
|
||||
## 7.2 自定义派单策略
|
||||
|
||||
调度引擎采用策略模式,支持为不同业务配置不同的推荐算法。
|
||||
|
||||
### 7.2.1 实现分配策略 (AssignStrategy)
|
||||
实现 `AssignStrategy` 接口,定义具体的执行人推荐逻辑:
|
||||
- **工程维修**:优先匹配具备相关技能证书(SkillId)的员工。
|
||||
- **安保巡更**:优先匹配当前排班在该路线附近的保安。
|
||||
|
||||
### 7.2.2 注册策略
|
||||
在实现类上标注 `@Component`,引擎会自动通过 `registerAssignStrategy` 完成注册。
|
||||
|
||||
## 7.3 基于事件的行为扩展
|
||||
|
||||
推荐使用领域事件(OrderStateChangedEvent)实现核心引擎与业务副作用的解耦。
|
||||
|
||||
### 7.3.1 编写 EventHandler
|
||||
创建业务专属的处理器(如 `SecurityOrderEventHandler`):
|
||||
- **场景**:当安保工单状态变为 `COMPLETED` 时,联动触发周边监控摄像头的预置位回归。
|
||||
- **实现**:
|
||||
```java
|
||||
@EventListener
|
||||
public void onSecurityOrderCompleted(OrderStateChangedEvent event) {
|
||||
if (!"SECURITY".equals(event.getOrderType())) return;
|
||||
// 执行安保特有的副作用逻辑
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
**下一章**:[Part 8: 测试指南](./part8-测试指南.md)
|
||||
29
docs/ops-architecture/part8-测试指南.md
Normal file
29
docs/ops-architecture/part8-测试指南.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Part 8: 测试指南
|
||||
|
||||
为保证 Ops 模块在高速演进过程中的稳定性,所有核心逻辑必须通过自动化测试覆盖。
|
||||
|
||||
## 8.1 单元测试规范
|
||||
|
||||
### 8.1.1 依赖模拟 (Mocking)
|
||||
使用 `JUnit 5` 和 `Mockito`。推荐继承项目通用的测试基类或使用 `MockitoExtension`。
|
||||
- **核心原则**:Mock 掉 Mapper 层和外部 RPC 调用,专注验证 Service 层的状态转换逻辑。
|
||||
|
||||
### 8.1.2 状态机测试
|
||||
针对 `OrderStateMachine` 的每一次修改,必须确保:
|
||||
1. **合法路径测试**:验证允许的状态转换(如 `ARRIVED` -> `COMPLETED`)能成功。
|
||||
2. **非法路径测试**:验证不允许的转换(如 `PENDING` -> `COMPLETED`)必须抛出异常。
|
||||
|
||||
## 8.2 集成测试规范
|
||||
|
||||
### 8.2.1 混合队列验证
|
||||
由于采用 Redis + MySQL 双写架构,集成测试需重点验证:
|
||||
- 事务提交后,Redis 队列中的分数(Score)与优先级(Priority)是否匹配。
|
||||
- `QueueSyncHandler` 是否正确处理了缓存失效后的补偿逻辑。
|
||||
|
||||
## 8.3 常用测试工具
|
||||
|
||||
- **Podam**:用于快速生成填充了随机数据的 DO/DTO 对象。
|
||||
- **BaseDbAndRedisUnitTest**:支持 H2 内存数据库和内置 Redis 的集成测试基类。
|
||||
|
||||
---
|
||||
**下一章**:[Part 9: 技术决策记录 (ADR)](./part9-技术决策记录.md)
|
||||
21
docs/ops-architecture/part9-技术决策记录.md
Normal file
21
docs/ops-architecture/part9-技术决策记录.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Part 9: 技术决策记录 (ADR)
|
||||
|
||||
记录 Ops 模块重大架构决策的背景、方案及权衡。
|
||||
|
||||
## ADR-001: 采用 Redis + MySQL 混合队列架构
|
||||
- **背景**:工单派单对实时性要求极高,纯数据库查询在数据量大时存在性能瓶颈;而纯缓存方案在宕机时存在丢失关键业务数据的风险。
|
||||
- **决策**:使用 Redis Sorted Set 承载实时排序与出队,使用 MySQL 存储持久化流水。
|
||||
- **后果**:提升了派单响应速度(毫秒级),但增加了双写一致性维护的复杂度。
|
||||
|
||||
## ADR-002: 引入 OrderLifecycleManager 生命周期层
|
||||
- **背景**:初期版本中,工单状态机与 Redis 队列的同步散落在各业务 Service 中,导致代码重复且易出现状态不一致。
|
||||
- **决策**:抽象统一的生命周期管理器,采用责任链模式编排“状态转换 -> 队列同步 -> 事件发布”流程。
|
||||
- **后果**:业务层代码量减少 40%,核心链路逻辑更加清晰。
|
||||
|
||||
## ADR-003: 异步事件驱动架构 (EDA)
|
||||
- **背景**:核心派单逻辑中掺杂了大量的语音播报、短信通知等副作用逻辑,严重影响主流程吞吐量。
|
||||
- **决策**:引入领域事件,状态机变更后同步发布 Spring Event,副作用逻辑通过 `@Async` 监听器异步处理。
|
||||
- **后果**:实现了核心引擎与业务插件的完全解耦。
|
||||
|
||||
---
|
||||
**下一章**:[Part 10: 附录](./part10-附录.md)
|
||||
Reference in New Issue
Block a user