通用资源调度预约系统 —— 架构设计与方案分享
本文不涉及任何源代码实现,仅从架构设计和方案层面进行探讨。
一、系统定位
在很多行业中都存在这样的场景:有若干可调度的资源(设备、场地、人力),有若干需要使用资源的任务,系统需要将任务智能地分配到合适的资源和时间窗口上。
例如:
- 医疗场景:将患者的检查申请分配到合适的设备和时段
- 教育场景:将课程安排到教室和时间窗口
- 物流场景:将运输任务分配到车辆和时段
- 实验室场景:将实验任务分配到仪器和时段
本文以实际项目经验为基础,抽象出一套通用资源调度预约系统的架构设计方案。
二、核心领域模型
2.1 三大核心实体
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 资源池(Queue) │────▶│ 时段(Slot) │◀────│ 任务(Order) │
│ │ │ │ │ │
│ - 名称 │ │ - 日期 │ │ - 任务名称 │
│ - 所属组织 │ │ - 开始/结束时间 │ │ - 任务项ID │
│ - 排班模式 │ │ - 各来源容量上限 │ │ - 来源类型 │
│ - 优先级 │ │ - 已用数量 │ │ - 预约状态 │
│ - 强制归集标识 │ │ - 工作模式 │ │ - 合并标识 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
资源池(Resource Pool):代表一组可调度的资源,例如某台设备、某个场地。核心配置包括:
- 所属组织/部门
- 排班模式(全天/分时段)
- 最大预约天数
- 强制归集标识(将关联任务强制调度到同一资源池)
时段(Time Slot):资源池在特定时间窗口内的可用容量。核心设计是按来源类型隔离容量,例如:
- A类来源容量上限 50,已用 30
- B类来源容量上限 20,已用 10
- C类来源容量上限 -1(不限制)
- 总容量上限 60
任务(Task/Order):需要被调度的最小工作单元。包含任务项关联、来源类型、预约状态、合并标识等。
2.2 任务项配置(Item Config)
任务项是系统中"可调度事项"的定义,类似于商品SKU。每个任务项配置包括:
- 名称、编码
- 预计耗时
- 前置准备时间和准备类型
- 匹配优先级
- 允许的预约方式
2.3 资源池-任务项绑定(Pool-Item Binding)
多对多关系,描述"某个资源池可以处理哪些任务项",同时配置:
- 该任务项在该资源池的预计用时
- 工作日工作时间
- 节假日工作时间
- 优先匹配时间窗口
三、核心调度算法
3.1 整体流程
输入:一组待调度的任务
│
▼
Step 1: 获取任务项配置(名称、耗时、准备类型等)
│
▼
Step 2: 查询约束规则
├── 合并规则(哪些任务可以合并到同一时段)
├── 排斥规则(哪些任务不能在同一时段)
└── 时间限制规则(哪些任务有建议的调度时间范围)
│
▼
Step 3: 匹配资源池和时段
├── 根据任务项找到可处理的资源池
├── 按来源类型验证容量
└── 按工作时间过滤有效时段
│
▼
Step 4: 智能分组
├── 强制归集分组:相同归集标识的任务归入同一资源池
├── 合并分组:可合并的任务分配到同一时段
├── 排斥分组:互相排斥的任务分配到不同时段
├── 时间限制分组:按建议时间范围匹配
└── 普通分组:无特殊约束的任务
│
▼
Step 5: 执行匹配
├── 强制归集任务 → 直接锁定目标资源池
├── 剩余任务 → 按策略匹配最佳时段
│ ├── 策略A:最快完成(优先最近的可用时段)
│ └── 策略B:最少移动(优先同一资源池的连续时段)
└── 输出:每个任务的推荐资源池 + 时段
3.2 合并与排斥机制
合并机制:某些任务项配置了相同的"合并类型",系统会尝试将它们安排到同一时段,减少用户的等待和移动。例如:
- 任务A(准备类型=P)和任务B(准备类型=P)→ 合并到同一时段
- 合并后,增强型任务优先排在前面
排斥机制:某些任务项之间存在互斥关系(如使用相同资源但不能同时进行),系统会将它们安排到不同时段。排斥还支持时间窗口约束,例如"任务A完成后48小时内不能做任务B"。
关键设计:合并和排斥是互斥的——存在排斥关系的任务项不会被合并。
3.3 强制归集机制
当某个资源池配置了"强制归集"标识后,只要待调度的任务项中存在匹配该归集标识的项,所有具有相同归集标识的任务都会被强制调度到该资源池。
归集组X = {任务A(归集标识=X), 任务B(归集标识=X), 任务C(归集标识=X)}
如果资源池R配置了归集组X → 任务A、B、C全部调度到资源池R
任务D(归集标识=Y) → 走正常匹配流程
这解决了一个常见场景:多个关联任务必须在同一资源上完成。
3.4 容量管理
容量管理的核心设计是按来源类型隔离:
时段T1:
├── 总容量上限: 60
├── 来源A容量: 50 (已用 30)
├── 来源B容量: 20 (已用 10)
└── 来源C容量: -1 (不限)
调度任务时:
1. 检查任务来源类型对应的容量是否已满
2. 检查总容量是否已满
3. 任一条件不满足 → 跳过该时段
这种设计保证了不同类型来源的资源隔离,避免某一类型占用全部容量。
四、系统架构设计
4.1 整体架构
┌──────────────────────────────────────┐
│ 多终端接入层 │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │管理端 │ │操作端 │ │用户端 │ │
│ └──┬───┘ └──┬───┘ └──┬───┘ │
└─────┼────────┼────────┼──────────────┘
│ │ │
┌─────▼────────▼────────▼──────────────┐
│ API 网关层 │
│ 认证鉴权 / 路由分发 / 限流熔断 │
└─────────────┬────────────────────────┘
│
┌─────────────▼────────────────────────┐
│ 业务服务层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 调度引擎服务 │ │ 资源管理服务 │ │
│ │ │ │ │ │
│ │ - 智能推荐 │ │ - 资源池管理 │ │
│ │ - 合并/排斥 │ │ - 时段管理 │ │
│ │ - 容量校验 │ │ - 任务项管理 │ │
│ │ - 状态流转 │ │ - 排班模板 │ │
│ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 任务管理服务 │ │ 通知服务 │ │
│ │ │ │ │ │
│ │ - 任务CRUD │ │ - 消息推送 │ │
│ │ - 状态跟踪 │ │ - 短信/微信 │ │
│ │ - 历史记录 │ │ - 第三方集成 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────┬────────────────────────┘
│
┌─────────────▼────────────────────────┐
│ 基础设施层 │
│ ┌────────┐ ┌───────┐ ┌───────────┐ │
│ │ MySQL │ │ Redis │ │ MQ队列 │ │
│ └────────┘ └───────┘ └───────────┘ │
│ ┌────────┐ ┌───────┐ ┌───────────┐ │
│ │ XXL-Job│ │ OSS │ │ 监控中心 │ │
│ └────────┘ └───────┘ └───────────┘ │
└──────────────────────────────────────┘
4.2 技术选型
| 层次 | 技术 | 选型理由 |
|---|---|---|
| Web容器 | Undertow | 比Tomcat更高的吞吐量,更低的内存占用 |
| ORM | MyBatis-Plus | 灵活的SQL控制 + 便捷的CRUD操作 |
| 认证 | Sa-Token + JWT | 轻量级、高性能的认证方案 |
| 缓存 | Redis + Redisson | 分布式锁、缓存、分布式会话 |
| 消息队列 | RabbitMQ | 异步通知、解耦、削峰 |
| 定时任务 | XXL-Job | 分布式任务调度、可视化管理 |
| 多数据源 | Dynamic Datasource | 支持多库场景,动态切换 |
| 连接池 | HikariCP | Spring Boot默认,高性能连接池 |
4.3 多终端架构
系统采用多模块单体架构,通过不同的入口模块服务不同终端:
┌─────────────────────────────────────────┐
│ 公共模块 (common) │
│ 枚举定义 / 工具类 / 通用组件 │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ 系统模块 (system) │
│ 领域模型 / Service / Mapper / XML │
└────────────────┬────────────────────────┘
│
┌────────┬───────┴────────┬───────────────┐
│ 管理端 │ 操作端 │ 用户端 │
│ (admin) │ (nurse) │ (app) │
│ 系统配置 │ 现场操作 │ 自助预约 │
│ 排班管理 │ 号源调整 │ 查询取消 │
│ 数据统计 │ 患者叫号 │ 通知接收 │
└────────┴───────────────┴───────────────┘
三个终端模块共享同一套业务逻辑(system模块),但各自独立部署、独立配置,可根据负载情况独立扩展。
五、关键设计决策
5.1 分布式锁策略
调度场景下的并发控制至关重要。系统采用Redis分布式锁来保证:
- 同一时段的容量扣减是原子操作
- 防止超卖(容量溢出)
- 锁的粒度控制在"资源池+时段"级别
锁的Key设计:lock:{poolId}:{slotId}
超时时间:30秒
获取超时:3秒
5.2 异步通知机制
调度结果需要通知下游系统(如执行系统、PACS系统等),采用MQ异步通知:
- 调度成功后立即返回,通知异步发送
- 通知失败时记录到重试队列
- 支持多种通知渠道(MQ、HTTP回调、WebSocket)
5.3 排班模板与自动排班
支持模板化排班,管理员可以:
- 配置排班模板(工作日模式、节假日模式、时段分组)
- 一键生成指定日期范围的排班
- 节假日自动同步调整
- 批量开关排班、批量设置工作模式
5.4 第三方系统集成
通过数据同步层与上游系统(如HIS、LIS等)对接:
- RabbitMQ监听数据变更事件
- 支持多种数据库类型(MySQL、Oracle、SQL Server、PostgreSQL)
- 动态数据源 + 动态驱动加载
- 字段映射可配置化
六、扩展性设计
6.1 资源池无限扩展
新增资源(如新设备、新场地)只需:
- 创建新的资源池记录
- 绑定任务项
- 配置排班
- 系统自动纳入调度
6.2 任务项类型扩展
通过任务项的树形结构和类型字段,支持无限扩展任务类型,无需修改代码。
6.3 调度策略扩展
调度算法通过策略模式设计,新增调度策略只需实现接口:
- 最快完成策略
- 最少移动策略
- 负载均衡策略
- 优先级策略
- ...(可自定义扩展)
6.4 多租户支持
通过数据源隔离 + 组织机构树,天然支持多租户场景。
七、性能优化要点
- 批量操作优化:MySQL
rewriteBatchedStatements=true,大幅提升批量写入性能 - 连接池调优:HikariCP 参数精细化配置,根据实际负载调整
- 缓存策略:热点数据(如任务项配置、资源池配置)使用Redis缓存
- 线程池管理:异步通知使用独立线程池,避免影响主流程
- SQL优化:MyBatis-Plus条件构造器避免N+1查询,合理使用索引
- Undertow容器:相比Tomcat有更高的并发处理能力
八、总结
这套通用资源调度预约系统的核心设计理念是:
- 领域驱动:资源池、时段、任务三大核心实体清晰分离
- 规则引擎化:合并、排斥、时间限制等规则可配置化,不硬编码
- 容量隔离:按来源类型隔离容量,保证公平性
- 强制归集:支持关联任务的强制聚合调度
- 多终端统一:一套业务逻辑服务多个终端,独立部署独立扩展
- 高可扩展:资源、任务类型、调度策略均可配置化扩展
这套架构不仅适用于医疗场景,任何涉及"资源-时段-任务"三方调度的场景都可以复用这套设计模式。
本文来自博客园,作者:Rolay,转载请注明原文链接:https://www.cnblogs.com/rolayblog/p/21281420

浙公网安备 33010602011771号