需求
工作人员分为五个班组:甲、乙、丙、丁、戊,现在需要给这五个班组安排每日值班计划,要求就是需要实现:三班倒、做三休二
。
其中每日值班分为三个班次:白班、中班、夜班
。
三班倒的意思就是:某个班组连续三天需要在不同的班次工作。比如甲班,第一天是在白班,那么第二天就是在中班,第三天就是在夜班
。
做三休二顾名思义就是工作三天,休息两天。比如甲班,第一、二、三天都工作了,那么第四、五天就休息,第六天再开始工作,如此循环
。
需要实现的效果如下图:
安排值班计划时,需要先选择从哪一天开始到哪一天结束,比如我选择要安排的值班日期是:2024-05-01 到 2024-05-31,就是一整个月的值班计划
。除了选择要安排的日期,也可以选择从哪个班次(白班、中班、夜班三选一)开始排班,比如班次选择的是中班,那就是从2024-05-01的中班开始排班一直轮询到结束时间,那2024-05-01这一天的白班就没有排就是空的
。
然后不管是从哪个班次开始排班,都是要甲组作为第一个排班的班组。
实现
先设置一个班组列表:
List<String> teamList = Arrays.asList("甲组","乙组","丙组","丁组","戊组"); // 轮班的班组列表
再设置一个班次列表:
//List<String> shiftList = Arrays.asList("白班","中班","夜班"); // 轮班的班次列表
// 因为是做三休二,所以除了白、中、夜班,在班次列表中,我们加上两个休班,这样方便我们进行轮班
List<String> shiftList = Arrays.asList("白班","中班","夜班","休班","休班"); // 轮班的班次列表
根据开始时间、结束时间,获取这个区间的所有日期:
/**
* 根据开始、结束时间获取之间的所有日期
*/
public static List<String> rangeDay(DateTime start,DateTime end,String format){
List<String> dates = new LinkedList<>();
DateTime currentDate = start;
while (!currentDate.isAfter(end)) {
dates.add(DateUtil.format(currentDate,format));
currentDate = currentDate.offset(DateField.DAY_OF_MONTH,1);
}
return dates;
}
将排班信息保存到 TeamRotation 实体对象的list中:
@Getter
@Setter
public class TeamRotation {
/**
* 排班日期
*/
private String pbrq;
/**
* 白班班组
*/
private String bbbz;
/**
* 中班班组
*/
private String zbbz;
/**
* 夜班班组
*/
private String ybbz;
/**
* 休班班组1
*/
private String xbbz1;
/**
* 休班班组2
*/
private String xbbz2;
public TeamRotation() {}
public TeamRotation(String pbrq) {
this.pbrq = pbrq;
}
}
开始排班
下面是实现排班的方法:
/**
* 轮询班次
* @param dateList 日期列表
* @param jzbc 甲组班次(默认从白班开始)
*/
public static List<TeamRotation> pollingShifts(List<String> dateList,String jzbc){
List<TeamRotation> result = new LinkedList<>(); // 存储最后的结果
List<String> teamList = Arrays.asList("甲组","乙组","丙组","丁组","戊组"); // 轮班的班组列表
List<String> shiftList = Arrays.asList("白班","中班","夜班","休班","休班"); // 轮班的班次列表
int shiftSize = shiftList.size();
int count = 0; // 标记第一天
boolean flag = false; // 标记从哪个班次开始轮询
boolean isBye = false; // 标记是否有轮空的班次(没有被安排班组)
for (String date : dateList) {
TeamRotation rotation = new TeamRotation(date);
for (int i = 0; i < shiftSize; i++) {
if (jzbc.equals(shiftList.get(i))) flag = true;
// 还没到开始轮询的班次,则跳过,并且标记有轮空的班次
if (!flag){
isBye = true;
continue;
}
// 获取当前班次值班的班组(第一天默认是第一个班组)
String bz = count == 0 ? teamList.get(0) : teamList.get(i);
switch (i){
case 0: rotation.setBbbz(bz);break;
case 1: rotation.setZbbz(bz);break;
case 2: rotation.setYbbz(bz);break;
case 3: rotation.setXbbz1(bz);break;
case 4: rotation.setXbbz2(bz);break;
}
if (count == 0) Collections.rotate(teamList, 1); // 将班组列表向右移动1位(只有第一天排班才需要移动)
}
if (count == 0){
if (isBye){
// 在开始轮询的班次之前有班次没有安排班组(比如开始轮询的班次是“夜班”,那么第一天的白班和中班就没有安排班组)
// 选择开始安排的班次是白、中、夜三个班次选一,所以夜班、休班、休班是一定有安排到班组的,白、中两个班次就不一定会有班组。
// 创建一个temp list,它的元素分别是第一天五个班次对应的班组,但因为白、中两个班次不一定会有班组,所以第一、第二个元素先设置成空字符串
List<String> temp = new LinkedList<>(Arrays.asList("", "", rotation.getYbbz(),rotation.getXbbz1(),rotation.getXbbz2()));
// 然后在 teamList 中,要找到除夜、休、休这三个班次的班组以外的其它两个班组
List<String> reduce = teamList.stream().filter(item -> !temp.contains(item)).collect(Collectors.toList());
// 再将这两个班组设置到temp中第一、第二个位置
temp.set(0,reduce.get(0));
temp.set(1,reduce.get(1));
// 最后再将temp赋值给teamList,这样后续排班时才能实现三班倒和轮休
teamList = temp;
}else {
// 第一天排班时假如是从白班开始,每遍历一个班次,teamList就会移动一次,当遍历完五次时,teamList会重新变成 甲乙丙丁戊 的排序
// 这个时候我们就需要将 teamList 的顺序变成第一天五个班次对应的班组的顺序,这样后续排班时才能实现三班倒和轮休
teamList = Arrays.asList(rotation.getBbbz(),rotation.getZbbz(),rotation.getYbbz(),rotation.getXbbz1(),rotation.getXbbz2());
}
}
Collections.rotate(teamList, 1); // 每安排好一天的排班,teamList需要向右移动1位
result.add(rotation);
count++;
}
return result;
}
public static void main(String[] args) {
DateTime start = DateUtil.parseDate("2024-05-01");
DateTime end = DateUtil.parseDate("2024-05-10");
List<String> dateList = rangeDay(start, end, "yyyy-MM-dd");
List<TeamRotation> result = pollingShifts(dateList,"白班");
for (TeamRotation one : result) {
System.out.println(one.getPbrq() + "\t白班:" + one.getBbbz() + "\t中班:" + one.getZbbz() + "\t夜班:" + one.getYbbz() + "\t休班:" + one.getXbbz1() + "\t休班:" + one.getXbbz2());
}
}
实现效果
看这张图很明显就是五天过后,第六天值班的班组和第一天的一样,第七天和第二天的一样,以此类推。就是五天为一个周期,不同周期之间,每天各个班次值班的班组都是一样的。
第二张图就是假设是从中班开始安排,那么第一天的白班就是null,中班才是甲组,然后丙组在休班2,所以第二天的白班是丙组,甲组则安排在夜班,而中班却是乙组。去对照六号、七号这两天的排班,我们不难发现,其实第一天的白班null应该是对应乙组。
从夜班开始安排也是一样的道理:
其实不管是从哪个班次开始安排,只要我们确定好第一天的排班班组顺序,那么后续的安排就很好安排了,就是轮着去嘛。然后确定第一天的班组顺序,可以好好去理解、调试下 pollingShifts()
方法里,最下面那个 if (count == 0){}
这个判断里面的代码。
最后
以上就是本篇文章的全部内容了,看完记得点赞~ 也可以关注我~