SpringBoot+Vue3 MES 假期排班:N 班 M 倒、休息落库与节假日覆盖怎么实现
🌐文档地址:https://ruoyioffice.com
👇 文章底部获取源码和演示地址 👇
💬 :17156169080(获取产品咨询)
工厂排班最容易被低估:班次配置看起来只有早、中、晚,真正落地时却要处理四班三倒、跨夜时段、轮休、法定假期、补班、计划确认和员工查询。只在前端日历里循环几个颜色,数据一到月底就解释不清。
▲ 排班计划先生成每日班组记录,再叠加节假日规则;休息也是一种必须可追踪的排班状态
引言:假期排班不是“周六周日不上班”
制造现场与办公室考勤有一个根本差异:生产线可能全年连续运行。
对行政部门来说,周末通常意味着休息;对连续生产车间来说,星期几并不决定是否开机。真正决定谁上班的是:
- 当前采用单白班、两班倒、三班倒还是四班;
- 参与轮换的班组有几个;
- 每隔几天、几周、几个月或几个季度轮换;
- 哪些日期被明确设置为停产假期或补班工作日;
- 排班计划是否已经确认并固化为日历;
- 员工属于哪个班组。
因此,MES 不能把“周末”“假期”“轮休”混成一个布尔字段。
这套实现把它们拆成三层:
- 计划层决定周期、班型和倒班节奏;
- 事实层保存某天某班组分到哪个班次,休息也落库;
- 展示层在查询日历时叠加节假日过滤,再按分类、班组或个人聚合。
▲ 排班计划是一个需要确认的业务对象,不是前端临时生成的一张日历
一、先把三个容易混淆的概念拆开
1. 班次:一天中的工作时间段
班次描述“几点到几点”。系统内置了四种班型的默认值:
- 单白班:08:00—18:00;
- 两班倒:08:00—20:00、20:00—次日 08:00;
- 三班倒:08:00—16:00、16:00—24:00、00:00—08:00;
- 四班:00:00—06:00、06:00—12:00、12:00—18:00、18:00—24:00。
创建排班计划后,后端按班型自动生成默认班次,实施人员仍可在草稿阶段调整名称和时间。
这里的“班型”只决定实际班次数 M,不等于参与轮换的班组数 N。
2. 班组:参与轮换的人员集合
班组管理负责组织成员。员工查询个人排班时,后端先找该员工所在班组,再读取班组的每日排班记录。
当前实现按“一名员工属于一个 MES 班组”查询。如果企业存在同一员工同时支援多个产线、临时借调或一个人跨多个班组,就需要在成员关系和生效日期上继续扩展,不能只给查询接口拼一个数组。
3. 节假日:对日期可用性的额外覆盖
节假日设置只有两种类型:
WORKDAY = 1:工作日;HOLIDAY = 2:节假日。
周末不会因为星期六、星期日自动变成停产日。前端只把周末日期标红,是否显示为“休”仍由mes_cal_holiday中的显式记录决定。
这点非常重要:制造日历必须由企业自己的生产制度决定,不能把行政日历硬编码成全厂规则。
▲ 未显式设置为假期的日期仍显示“班”;点击日期可维护工作日、节假日与备注
二、排班计划为什么要经历“草稿 → 确认”
排班计划包含:
- 计划编码和名称;
- 班组分类;
- 开始、结束日期;
- 班型;
- 倒班方式;
- 倒班间隔;
- 草稿或已确认状态。
创建计划时状态固定为草稿。草稿阶段可以调整班次、班组和周期;确认后才批量生成每日排班事实。
▲ 确认前维护班次和参与班组;确认后生成日历并锁定计划
确认时至少有两道校验:
- 计划仍处于草稿状态;
- 班组数量 N 不少于实际班次数 M。
@Transactional(rollbackFor=Exception.class)publicvoidconfirmPlan(Longid){MesCalPlanDOplan=validatePlanPrepare(id);validateTeamCountForConfirm(id);MesCalPlanDOupdateObj=newMesCalPlanDO();updateObj.setId(id);updateObj.setStatus(MesCalPlanStatusEnum.CONFIRMED.getStatus());planMapper.updateById(updateObj);teamShiftService.generateTeamShiftRecords(id);}状态更新和日历生成放在同一个事务里。任何一步失败,计划不会停留在“已确认但没有日历”的半成品状态。
撤销确认则做相反动作:
- 删除该计划已生成的班组排班记录;
- 把计划恢复为草稿;
- 允许调整后再次确认、重新生成。
这比直接编辑已生效日历更容易审计。但如果排班已经被报工、考勤或计件工资引用,生产系统还需要增加“已消费不可撤销”或版本化策略,当前实现尚未建立这层引用约束。
三、N 班 M 倒到底怎么算
最典型的是四班三倒:
- N = 4 个班组;
- M = 3 个实际班次;
- 每天 3 个班组上班,1 个班组休息;
- 下一轮整体移动一个档位。
▲ 班组数量可以多于班次数,多出的档位不是丢弃,而是明确的休息位置
核心算法先算当天轮换索引shiftIndex,再给每个班组算槽位:
intslot=Math.floorMod(teamIndex+shiftIndex,teamCount);if(slot<shiftCount){record.setShiftId(shifts.get(slot).getId());}else{record.setShiftId(null);// 休息}班组的轮换顺序来自计划班组关系的查询顺序,当前实现按关系记录 ID 升序读取。因此,“先把哪个班组加入计划”会影响首日落在哪个班次,实施时不能把班组添加顺序当成无关信息。
为什么模数使用teamCount,不是shiftCount?
因为环形轨道上移动的是全部 N 个班组。只有前 M 个槽位对应真实班次,剩余N - M个槽位对应休息。
四班三倒第一天可以是:
- 甲组:白班;
- 乙组:中班;
- 丙组:夜班;
- 丁组:休息。
轮换一格后:
- 丁组:白班;
- 甲组:中班;
- 乙组:夜班;
- 丙组:休息。
如果只保存上班的三条数据,系统就无法知道当天轮休的是哪个班组,也无法稳定推导下一天的环形位置。
为什么休息记录使用shiftId = null
mes_cal_team_shift每天为每个参与轮换的班组生成一条记录:
MesCalTeamShiftDO.builder().planId(planId).day(currentDay).teamId(teamId).shiftId(null).sort(slot+1).build();日历响应把空班次解释为“休息”。
这种设计有三个直接收益:
- 个人日历能明确显示休息,而不是误以为数据缺失;
- 可统计各班组上班与轮休天数;
- 撤销计划时可以按
planId完整清理。
四、按天、周、月、季度轮换如何共用一个算法
系统支持四种倒班方式:
- 按天;
- 按周;
- 按月;
- 按季度。
差异只在“什么时候让shiftIndex + 1”。
按天轮换时,根据计划开始日期的偏移量判断:
if(dayOffset%plan.getShiftCount()==0){returnshiftIndex+1;}例如shiftCount = 2,意味着每两个自然日移动一格。
按周轮换则在新一周的周一移动;按月在每月 1 日移动;按季度在新季度第一天移动。计划开始日不是周期边界时,第一段会从开始日持续到下一个边界。
这里还要区分两个字段:
shiftType:实际有几个班次;shiftMethod:多久轮换一次。
“三班倒”不代表“每三天轮一次”。前者描述一天内的班次数,后者才描述轮换节奏。
五、节假日怎样覆盖排班,又不破坏原始记录
计划确认时,系统会按日期范围生成全部排班记录。它不会因为某天是假期就少插一批数据。
查询日历时才做三件事:
- 读取日期范围内的班组排班;
- 读取同一范围内显式标记为
HOLIDAY的日期; - 按日期分组,遇到假期日期就不返回该日排班。
for(Map.Entry<String,List<MesCalTeamShiftDO>>entry:dayGroupMap.entrySet()){Stringday=entry.getKey();if(holidaySet.contains(day)){continue;}// 构建班次、班组与日历响应}▲ 同一批排班事实可以按班组类型、具体班组或员工查看,节假日在查询阶段统一覆盖
这种“事实不改、查询覆盖”的好处是:
- 取消假期后,原排班无需重新生成即可恢复显示;
- 不会因为先确认计划、后录入假期而造成数据永久缺口;
- 假期规则和轮班算法保持解耦。
但它也有明确边界:当前是隐藏整天排班,不是为假期生成特殊班、值守班或加班班次。连续生产企业如果节假日仍保留少量值守,需要把“整日过滤”升级成按日历类型、班组或计划的例外规则。
六、三个查询视角如何复用同一本日历
前端排班日历提供三种视角。
按分类
先按calendarType找出对应类型的全部班组,再批量查询这些班组在日期范围内的记录。适合查看某类生产团队的整体覆盖。
按班组
直接按teamId查询。适合车间负责人查看本组轮换。
按个人
先通过班组成员关系找到员工所属班组,再读取班组日历。适合员工自助查看。
三种视角没有各自生成一份日历,而是复用mes_cal_team_shift。这能避免分类页、班组页和个人页出现三套互相打架的数据。
七、数据模型与多租户边界
这条链路至少涉及五类数据:
mes_cal_plan:排班计划头;mes_cal_plan_shift:计划内班次;mes_cal_plan_team:计划参与班组;mes_cal_team_shift:确认后生成的每日事实;mes_cal_holiday:日期覆盖规则。
实际表结构都包含tenant_id,平台 MyBatis 租户插件按表字段注入隔离条件。虽然部分 Java DO 继承的是BaseDO而不是TenantBaseDO,不能据此误判为没有租户隔离,最终仍要同时核对表结构和租户插件配置。
节假日保存使用按日期查询后更新、否则插入的 Upsert 逻辑。生产环境建议再增加“租户 + 日期”的唯一约束,避免并发点击或重复请求插入同一天的两条记录。
八、当前实现还要诚实说明哪些边界
1. 排班日历尚未自动驱动生产工单
当前代码完成的是班组日历,没有把排班结果用于工单派工、工序任务、设备产能或交期重排。
文章不能把“存在 MES 排班模块”写成“工单已经按班次自动排产”。如果要继续联动,至少需要定义:
- 工单计划时间怎样映射到班次;
- 假期导致产能减少后如何顺延;
- 一个工序能否跨两个班次;
- 班组能力、设备能力和物料齐套谁优先。
2. 跨夜班次目前只是时间字符串
两班倒中的夜班是20:00—08:00。现有排班事实保存的是“归属日期 + 班次编号”,没有单独落开始、结束时间戳。
考勤、报工或计件消费排班时,必须约定夜班归属哪一天,并把结束时间小于开始时间解释为次日,不能直接在同一天做时间比较。
3. 计划重叠尚未形成硬约束
当前确认逻辑校验状态和班组数量,但没有看到同一班组在同一日期只能命中一个已确认计划的冲突校验。
实施时应避免给同一班组创建重叠计划;产品继续完善时,建议在确认阶段做日期区间冲突检查。
4. 计划分类与班组分类尚未强校验
计划包含calendarType,班组也有自己的calendarType,但把班组加入计划时没有看到两者必须一致的校验。“按分类”查询实际依据的是班组分类,而不是计划字段。实施配置不一致时,计划能确认,分类日历却可能出现在意外分组。
5. 假期是租户级整日覆盖
当前假期表没有车间、班组或计划维度。设置某天为假期后,该租户的排班日历查询会统一过滤这一天。
多工厂、多地区法定假期不同的场景,应给日历规则增加组织、工厂或适用范围,不能复制多套查询特例。
6. 单白班只给第一个班组生成记录
单白班分支直接取第一个班组。如果一个计划关联多个独立白班班组,希望它们都在同一天上班,需要调整生成规则,不能沿用轮班分支的默认语义。
九、建议怎样验证一套 MES 假期排班
不要只验证页面能打开,至少准备以下场景。
场景一:三班三组,按天轮换
连续生成三天,检查三个班组是否依次在白、中、夜之间轮换,总记录数应为3 天 × 3 组。
场景二:四班三倒
检查每天是否有三条上班记录和一条shiftId = null的休息记录,第二天休息班组是否向前轮换。
场景三:五班三倒
每天应有三条上班、两条休息。不能因为班组多于班次就拒绝确认。
场景四:计划中途跨周、跨月
分别验证周一、月初、季度首日是否只推进一次,计划开始日不在边界时是否保持第一段稳定。
场景五:先确认计划,再新增假期
日历应立即隐藏该日;改回工作日后应恢复显示,数据库中的原排班事实不应被删除。
场景六:撤销确认再重新确认
旧记录应按计划清空,新记录只生成一份,不能重复累计。
十、从排班日历继续走向高级排产
排班是高级排产的基础,但不是高级排产本身。
比较稳妥的演进顺序是:
- 先让班次、班组、轮休和假期成为可信日历;
- 再给工序、工作站和设备定义产能;
- 把工单需求量换算成标准工时;
- 按可用班次计算最早完工时间;
- 遇到假期、停机、插单时重新计算;
- 最后再引入甘特图、约束优化或 APS 算法。
如果第一步日历都不可信,后面的排产越智能,结果只会越快偏离现场。
常见问题(FAQ)
周末会自动休息吗?
不会。周末只在界面上标红;是否过滤排班由节假日记录决定。
四班三倒是不是必须四个班次?
不是。四班三倒表示 4 个班组轮换 3 个实际班次,每天有 1 个班组休息。
为什么休息也要保存数据库?
因为休息是环形轮换中的一个明确槽位。没有休息记录,就无法解释某班组当天为什么没有班,也不利于后续统计和轮换。
修改节假日后要重新确认计划吗?
当前不需要。假期在日历查询阶段过滤,原排班记录仍然保留。
已确认计划还能直接改班次吗?
不能。需要先撤销确认、清空已生成日历,回到草稿后修改并重新确认。
MES 排班和 HR 考勤排班是一套数据吗?
不是。MES 面向生产班组和生产日历,HR 考勤面向员工出勤规则。两者可以对接,但不应该因为名称相同就共用一张业务表。
结语
MES 假期排班的核心不是画一个月历,而是把计划、班次、班组、轮换索引、休息槽位和日期覆盖拆成可解释的数据层。
N ≥ M让四班三倒、五班三倒自然成立;休息落库让轮换轨迹可追踪;确认与撤销保证日历可重算;节假日在查询阶段覆盖,避免破坏原始排班事实。
把这些基础边界做稳,排班日历才有资格继续服务报工、考勤、计件工资和生产排产。
如果这篇对你有用,点个「在看」或收藏。
🌐演示地址:https://ruoyioffice.com/web
📦GitHub 源码:https://github.com/yuqing2026/ruoyi-office
📦Gitee 源码:https://gitee.com/yqzy1688/ruoyi-office
💬微信:17156169080(获取产品咨询)
打开演示地址直接查看系统。