Commit d6a79552 by weiss

Merge remote-tracking branch 'origin/master'

2 parents 4775603b f871b337
package com.pipihelper.project.controller;
import com.alibaba.fastjson.JSONObject;
import com.pipihelper.project.core.Result;
import com.pipihelper.project.core.ResultGenerator;
import com.pipihelper.project.rostering.model.DateRuleModel;
import com.pipihelper.project.rostering.model.RosteringModel;
import com.pipihelper.project.rostering.model.ShiftRuleModel;
import com.pipihelper.project.rostering.model.StaffRuleModel;
import com.pipihelper.project.rostering.service.RosteringService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @description:
* @author: zsw
* @create: 2022-10-15 20:01
**/
@RestController
@RequestMapping("/api/v1/rostering")
public class RosteringController {
@Autowired
private RosteringService rosteringService;
@PostMapping(value = "gen")
public Result genRostering(@RequestParam("month") int month,
@RequestParam("shiftRuleModel") String shiftRuleModelStr,
@RequestParam("staffRuleModel") String staffRuleModelStr,
@RequestParam("dateRuleModel") String dateRuleModelStr) {
List<ShiftRuleModel> shiftRuleModels = JSONObject.parseArray(shiftRuleModelStr, ShiftRuleModel.class);
List<StaffRuleModel> staffRuleModels = JSONObject.parseArray(staffRuleModelStr, StaffRuleModel.class);
List<DateRuleModel> dateRuleModels = JSONObject.parseArray(dateRuleModelStr, DateRuleModel.class);
return ResultGenerator.genSuccessResult(rosteringService.gen(month, shiftRuleModels, staffRuleModels, dateRuleModels));
}
}
package com.pipihelper.project.interceptor;
import com.pipihelper.project.core.Result;
import com.pipihelper.project.core.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 白名单拦截器,用于控制允许访问的请求
*
* @author zlikun
* @date 2021/10/21 14:39
*/
@Slf4j
@Component
@Aspect
public class ResponseAop {
//这里改成自己项目的控制层路径
@Pointcut("execution(public * com.pipihelper.project.controller..*(..))")
public void httpResponse() {
}
//环切
@Around("httpResponse()")
public Result handlerController(ProceedingJoinPoint proceedingJoinPoint) {
Result result = new Result();
try {
//获取方法的执行结果
Object proceed = proceedingJoinPoint.proceed();
//如果方法的执行结果是Result,则将该对象直接返回
if (proceed instanceof Result) {
result = (Result) proceed;
} else {
//否则,就要封装到Result的data中
result.setData(proceed);
}
} catch (Throwable throwable) {
//如果出现了异常,调用异常处理方法将错误信息封装到Result中并返回
result = handlerException(throwable);
}
return result;
}
//异常处理
private Result handlerException(Throwable throwable) {
Result result = new Result();
//这里需要注意,返回枚举类中的枚举在写的时候应该和异常的名称相对应,以便动态的获取异常代码和异常信息,我这边是统一返回500,msg存报的异常
//获取异常名称的方法
String errorName = throwable.toString();
errorName = errorName.substring(errorName.lastIndexOf(".") + 1);
result.setMessage(errorName);
result.setCode(ResultCode.INTERNAL_SERVER_ERROR);
return result;
}
}
...@@ -19,8 +19,14 @@ public class DateRuleModel { ...@@ -19,8 +19,14 @@ public class DateRuleModel {
*/ */
private Date date; private Date date;
/**
* 班次名称
*/
private String shift; private String shift;
/**
* 次数
*/
private Integer times; private Integer times;
} }
...@@ -3,6 +3,7 @@ package com.pipihelper.project.rostering.model; ...@@ -3,6 +3,7 @@ package com.pipihelper.project.rostering.model;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* @description: * @description:
...@@ -20,7 +21,7 @@ public class RosteringModel { ...@@ -20,7 +21,7 @@ public class RosteringModel {
/** /**
* 班次 * 班次
*/ */
private String shift; private List<String> shift;
/** /**
* 时间 * 时间
......
package com.pipihelper.project.rostering.model; package com.pipihelper.project.rostering.model;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.Data; import lombok.Data;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
...@@ -27,16 +24,6 @@ public class ShiftRuleModel { ...@@ -27,16 +24,6 @@ public class ShiftRuleModel {
private Integer everyDay; private Integer everyDay;
/** /**
* 班次前允许
*/
private List<String> frontAllow;
/**
* 班次前不允许
*/
private List<String> frontDenied;
/**
* 班次后允许 * 班次后允许
*/ */
private List<String> backAllow; private List<String> backAllow;
...@@ -55,23 +42,13 @@ public class ShiftRuleModel { ...@@ -55,23 +42,13 @@ public class ShiftRuleModel {
/** /**
* 班次最大次数后指定班次 * 班次最大次数后指定班次
*/ */
private String maxContinuityFollow; private List<String> maxContinuityFollow;
//{"backAllow":["早班"],"backDenied":["晚班"],"everyDay":5,"frontAllow":["早班"],"frontDenied":["晚班"],"maxContinuity":5,"maxContinuityFollow":"休息","name":"早班"} /**
public static void main(String[] args) { * 是否休息
ShiftRuleModel shiftRuleModel = new ShiftRuleModel(); */
shiftRuleModel.setName("早班"); private boolean isRest;
shiftRuleModel.setEveryDay(5);
shiftRuleModel.setFrontAllow(Lists.newArrayList("早班"));
shiftRuleModel.setFrontDenied(Lists.newArrayList("晚班"));
shiftRuleModel.setBackAllow(Lists.newArrayList("早班"));
shiftRuleModel.setBackDenied(Lists.newArrayList("晚班"));
shiftRuleModel.setMaxContinuity(5);
shiftRuleModel.setMaxContinuityFollow("休息");
String s = JSONObject.toJSONString(shiftRuleModel); //{"backAllow":["早班"],"backDenied":["晚班"],"everyDay":5,"frontAllow":["早班"],"frontDenied":["晚班"],"maxContinuity":5,"maxContinuityFollow":"休息","name":"早班"}
System.out.println(s);
}
} }
...@@ -3,8 +3,10 @@ package com.pipihelper.project.rostering.model; ...@@ -3,8 +3,10 @@ package com.pipihelper.project.rostering.model;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
/** /**
...@@ -54,31 +56,5 @@ public class StaffRuleModel { ...@@ -54,31 +56,5 @@ public class StaffRuleModel {
private Date date; private Date date;
} }
//{"fixedShift":{"date":"2022-10-14 00:00:00","shift":"AAAA"},"maxTimes":{"shift":"AAA","times":5},"minTimes":{"shift":"AAA","times":5},"name":"xxxx"} //{"fixedShift":{"date":"2022-10-14 00:00:00","shift":"AAAA"},"maxTimes":{"shift":"AAA","times":5},"minTimes":{"shift":"AAA","times":5},"name":"xxxx"}
public static void main(String[] args) {
StaffRuleModel staffRuleModel = new StaffRuleModel();
staffRuleModel.setName("xxxx");
Threshold maxTimes = new Threshold();
maxTimes.setShift("AAA");
maxTimes.setTimes(5);
Threshold minTimes = new Threshold();
minTimes.setShift("AAA");
minTimes.setTimes(5);
Threshold fixedShift = new Threshold();
fixedShift.setDate(DateUtil.parse("2022-10-14 00:00:00"));
fixedShift.setShift("AAAA");
staffRuleModel.setMaxTimes(maxTimes);
staffRuleModel.setMinTimes(minTimes);
staffRuleModel.setFixedShift(fixedShift);
String s = JSONObject.toJSONString(staffRuleModel, SerializerFeature.WriteDateUseDateFormat);
System.out.println(s);
}
} }
...@@ -16,15 +16,14 @@ public interface RosteringService { ...@@ -16,15 +16,14 @@ public interface RosteringService {
/** /**
* 生成班次 * 生成班次
* @param staffs 人员 *
* @param shifts 班次 * @param month
* @param shiftRuleModels 班次规则 * @param shiftRuleModels 班次规则
* @param staffRuleModels 原因规则 * @param staffRuleModels 原因规则
* @param dateRuleModels 时间规则 * @param dateRuleModels 时间规则
* @return * @return
*/ */
List<RosteringModel> gen(List<String> staffs, List<RosteringModel> gen(int month,
List<String> shifts,
List<ShiftRuleModel> shiftRuleModels, List<ShiftRuleModel> shiftRuleModels,
List<StaffRuleModel> staffRuleModels, List<StaffRuleModel> staffRuleModels,
List<DateRuleModel> dateRuleModels); List<DateRuleModel> dateRuleModels);
......
package com.pipihelper.project.rostering.service.impl; package com.pipihelper.project.rostering.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.Lists;
import com.pipihelper.project.rostering.model.DateRuleModel; import com.pipihelper.project.rostering.model.DateRuleModel;
import com.pipihelper.project.rostering.model.RosteringModel; import com.pipihelper.project.rostering.model.RosteringModel;
import com.pipihelper.project.rostering.model.ShiftRuleModel; import com.pipihelper.project.rostering.model.ShiftRuleModel;
import com.pipihelper.project.rostering.model.StaffRuleModel; import com.pipihelper.project.rostering.model.StaffRuleModel;
import com.pipihelper.project.rostering.service.RosteringService; import com.pipihelper.project.rostering.service.RosteringService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* @description: * @description:
...@@ -18,38 +33,178 @@ import java.util.List; ...@@ -18,38 +33,178 @@ import java.util.List;
public class RosteringServiceImpl implements RosteringService { public class RosteringServiceImpl implements RosteringService {
@Override @Override
public List<RosteringModel> gen(List<String> staffs, public List<RosteringModel> gen(int month,
List<String> shifts,
List<ShiftRuleModel> shiftRuleModels, List<ShiftRuleModel> shiftRuleModels,
List<StaffRuleModel> staffRuleModels, List<StaffRuleModel> staffRuleModels,
List<DateRuleModel> dateRuleModels) { List<DateRuleModel> dateRuleModels) {
//校验
verify(staffs, shifts, shiftRuleModels, staffRuleModels);
int daysOfMonth = getDaysOfNextMonth(month);
Map<String, StaffRuleModel> staffRuleModelMap = staffRuleModels.stream().collect(Collectors.toMap(StaffRuleModel::getName, Function.identity()));
//记录用户当次排版已经排了多少次
Map<String, List<ShiftRuleModel>> staffMap = new HashMap<>();
for (int i = 0; i < daysOfMonth; i++) {
int currentDay = i + 1;
return null; staffRuleModels.forEach(e -> {
List<ShiftRuleModel> currentShifts = Optional.ofNullable(staffMap.get(e.getName())).orElse(Lists.newArrayList());
//指定日期班次
StaffRuleModel.Threshold fixedShift = e.getFixedShift();
boolean isFixed = Optional.ofNullable(fixedShift).map(StaffRuleModel.Threshold::getDate).filter(fixedDate -> DateUtil.dayOfMonth(fixedDate) == currentDay).isPresent();
ShiftRuleModel shiftRuleModel = null;
if (isFixed) {
shiftRuleModel = shiftRuleModels.stream().filter(shift -> ObjectUtil.equal(shift.getName(), fixedShift.getShift())).findFirst().orElse(null);
} }
if (shiftRuleModel == null) {
shiftRuleModel = getByShiftRule(shiftRuleModels, currentShifts, e);
}
private void verify(List<String> staffs, //判断是否已经达到了最大次数
List<String> shifts, StaffRuleModel.Threshold maxTimes = e.getMaxTimes();
List<ShiftRuleModel> shiftRuleModels, if (maxTimes != null && maxTimes.getTimes() != null && StringUtils.isNotBlank(maxTimes.getShift())) {
List<StaffRuleModel> staffRuleModels) { String shift = maxTimes.getShift();
int count = (int) currentShifts.stream().filter(result -> ObjectUtil.equal(result.getName(), shift)).count();
count = ObjectUtil.equal(shiftRuleModel.getName(), shift) ? count + 1 : count;
if (count > maxTimes.getTimes()) {
shiftRuleModel = getRandom(shiftRuleModels.stream().filter(ignore -> ObjectUtil.notEqual(ignore.getName(), shift)).collect(Collectors.toList()));
}
}
//添加到map中
currentShifts.add(shiftRuleModel);
staffMap.putIfAbsent(e.getName(), currentShifts);
});
}
if (shiftRuleModels.stream().noneMatch(e -> shifts.contains(e.getName()))) { List<RosteringModel> result = new ArrayList<>();
throw new RuntimeException("班次配置不合法"); staffRuleModels.forEach(e -> {
List<String> shifts = Optional.ofNullable(staffMap.get(e.getName())).orElse(Lists.newArrayList())
.stream().map(ShiftRuleModel::getName).collect(Collectors.toList());
StaffRuleModel.Threshold minTimes = e.getMinTimes();
if (minTimes != null && minTimes.getTimes() != null && StringUtils.isNotBlank(minTimes.getShift())) {
int count = (int) shifts.stream().filter(shift -> ObjectUtil.equal(shift, minTimes.getShift())).count();
int diff = minTimes.getTimes() - count;
if (diff > 0) {
int index = 0;
for (int i = 0; i < diff; i++) {
index = getRandomInt(shifts.size() - 1, index);
shifts.set(index, minTimes.getShift());
}
}
} }
if (staffRuleModels.stream().noneMatch(e -> staffs.contains(e.getName()))) { RosteringModel rosteringModel = new RosteringModel();
throw new RuntimeException("员工配置不合法"); rosteringModel.setName(e.getName());
rosteringModel.setShift(shifts);
result.add(rosteringModel);
});
return result;
} }
/**
* 获取某个月的天数
*
* @return
*/
private int getDaysOfNextMonth(int month) {
Calendar calendar = Calendar.getInstance();
//获得当前日期往后推1个月 amount 为设置的月份值 +为往后推 +号可以省略 -为往前推
calendar.add(Calendar.MONTH, +1);
//获得下一个月是多少年
int year = calendar.get(Calendar.YEAR);
calendar.set(year, month, 0);
//获得下一个月有多少天
return calendar.get(Calendar.DAY_OF_MONTH);
}
/**
* 根据班次规则获取
*
* @param shiftRuleModels
* @param currentShifts
* @return
*/
private ShiftRuleModel getByShiftRule(List<ShiftRuleModel> shiftRuleModels, List<ShiftRuleModel> currentShifts, StaffRuleModel staffRuleModel) {
//随机获取,当前排班为null 则代表是第一次排班
if (CollectionUtil.isEmpty(currentShifts)) {
return getRandom(shiftRuleModels.stream().filter(e -> BooleanUtil.isFalse(e.isRest())).collect(Collectors.toList()));
}
//获取最后一次分配的班次
int size = currentShifts.size();
ShiftRuleModel lastShift = currentShifts.get(size - 1);
//获取最后一次分配的班次的连续次数
int currentShiftTimes = 0;
for (int i = size - 1; i >= 0; i--) {
ShiftRuleModel currentShift = currentShifts.get(i);
if (ObjectUtil.equal(lastShift.getName(), currentShift.getName())) {
currentShiftTimes++;
} else {
break;
}
}
//根据次数获取
ShiftRuleModel result = getByTimes(shiftRuleModels, lastShift, currentShiftTimes);
if (result == null) {
//证明没有设置连续班,首先判断允许班次是否为空,不为空则从允许班次中随机获取一个班次
List<String> backAllow = lastShift.getBackAllow();
if (CollectionUtil.isNotEmpty(backAllow)) {
result = getRandom(shiftRuleModels.stream().filter(e -> backAllow.contains(e.getName())).collect(Collectors.toList()));
}
//证明没有设置连续班,首先判断不允许允许班次是否为空,不为空则排除不允许班次随机获取班次
List<String> backDenied = lastShift.getBackDenied();
if (CollectionUtil.isNotEmpty(backDenied)) {
result = getRandom(shiftRuleModels.stream().filter(e -> !backDenied.contains(e.getName())).collect(Collectors.toList()));
}
}
if (result == null) {
result = getRandom(shiftRuleModels);
}
//是否有休息并且
if (BooleanUtil.isFalse(result.isRest()) && BooleanUtil.isFalse(lastShift.isRest())) {
result = shiftRuleModels.stream()
.filter(e -> ObjectUtil.equal(e.getName(), lastShift.getName()))
.findFirst().orElse(null);
}
return result;
}
private ShiftRuleModel getByTimes(List<ShiftRuleModel> shiftRuleModels, ShiftRuleModel lastShift, Integer currentShiftTimes) {
//当前班次的次数小于最大连续次数,则返回同样班次
Integer maxContinuity = lastShift.getMaxContinuity();
if (maxContinuity == null) {
return null;
}
if (currentShiftTimes < maxContinuity) {
return lastShift;
}
List<String> maxContinuityFollows = lastShift.getMaxContinuityFollow();
return shiftRuleModels.stream().filter(e -> maxContinuityFollows.contains(e.getName())).findFirst().orElse(null);
}
private ShiftRuleModel getRandom(List<ShiftRuleModel> shiftRuleModels) {
return shiftRuleModels.get(RandomUtil.randomInt(0, shiftRuleModels.size()));
}
private int getRandomInt(int size, int index) {
int i = RandomUtil.randomInt(0, size);
if (i == index) {
return getRandomInt(size, index);
}
return i;
} }
} }
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!