Commit bb3c8ace by 刘鑫

Merge branch 'develop' of https://gitlab.dituhui.com/bsh/project/project into develop

2 parents 096b2f0b 75f99c75
...@@ -76,6 +76,9 @@ public class DispatchConstraintProvider implements ConstraintProvider { ...@@ -76,6 +76,9 @@ public class DispatchConstraintProvider implements ConstraintProvider {
if (!in) { if (!in) {
// 到达时间在日历窗口外,惩罚得分 // 到达时间在日历窗口外,惩罚得分
ret = true; ret = true;
customer.setInTechnicianTimeWindows(false);
} else {
customer.setInTechnicianTimeWindows(true);
} }
} }
} }
...@@ -93,18 +96,18 @@ public class DispatchConstraintProvider implements ConstraintProvider { ...@@ -93,18 +96,18 @@ public class DispatchConstraintProvider implements ConstraintProvider {
.asConstraint(ConstraintNameEnum.technicianCapacityMatch.name()); .asConstraint(ConstraintNameEnum.technicianCapacityMatch.name());
} }
protected Constraint dispatchedMatch(ConstraintFactory factory) { protected Constraint dispatchedMatch(ConstraintFactory factory) {
return factory.forEach(Customer.class).filter(customer -> return factory.forEach(Customer.class).filter(customer ->
// 已分配但是分给了别人 // 已分配但是分给了别人
(customer.getDispatchedTechnicianCode() != null && customer.getTechnician() != null (customer.getDispatchedTechnicianCode() != null && customer.getTechnician() != null
&& !StringUtils.equals(customer.getDispatchedTechnicianCode(), customer.getTechnician().getCode())) || && !StringUtils.equals(customer.getDispatchedTechnicianCode(), customer.getTechnician().getCode())) ||
// 已排除但是分给了这个人 // 已排除但是分给了这个人
(customer.getExclusiveTechnicianCode() != null && customer.getTechnician() != null (customer.getExclusiveTechnicianCode() != null && customer.getTechnician() != null
&& StringUtils.equals(customer.getExclusiveTechnicianCode(), && StringUtils.equals(customer.getExclusiveTechnicianCode(),
customer.getTechnician().getCode()))) customer.getTechnician().getCode())))
.penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 50) .penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 50)
.asConstraint(ConstraintNameEnum.dispatchedMatch.name()); .asConstraint(ConstraintNameEnum.dispatchedMatch.name());
} }
// protected Constraint customerTimeWindowsMatch1(ConstraintFactory factory) { // protected Constraint customerTimeWindowsMatch1(ConstraintFactory factory) {
// return factory.forEach(Customer.class).filter( // return factory.forEach(Customer.class).filter(
......
...@@ -21,12 +21,12 @@ public class Customer { ...@@ -21,12 +21,12 @@ public class Customer {
private long id; private long id;
private String code; private String code;
// 已分配 // 已分配
private String dispatchedTechnicianCode; private String dispatchedTechnicianCode;
// 已排除 // 已排除
private String exclusiveTechnicianCode; private String exclusiveTechnicianCode;
// orderid(code)+dt 确定唯一一条工单 // orderid(code)+dt 确定唯一一条工单
private String dt; private String dt;
@JsonIgnore @JsonIgnore
...@@ -50,6 +50,8 @@ public class Customer { ...@@ -50,6 +50,8 @@ public class Customer {
// 离开时间 // 离开时间
// private Integer departureTime; // private Integer departureTime;
private boolean isInTechnicianTimeWindows = true;
public Customer() { public Customer() {
} }
...@@ -135,9 +137,9 @@ public class Customer { ...@@ -135,9 +137,9 @@ public class Customer {
// throw new IllegalStateException("This method must not be called when the shadow variables are not initialized yet."); // throw new IllegalStateException("This method must not be called when the shadow variables are not initialized yet.");
} }
if (previousCustomer == null) { if (previousCustomer == null) {
return technician.getDepot().getLocation().getDistanceTo(technician.getVehicleType(),location); return technician.getDepot().getLocation().getDistanceTo(technician.getVehicleType(), location);
} }
return previousCustomer.getLocation().getDistanceTo(technician.getVehicleType(),location); return previousCustomer.getLocation().getDistanceTo(technician.getVehicleType(), location);
} }
/** /**
...@@ -151,9 +153,9 @@ public class Customer { ...@@ -151,9 +153,9 @@ public class Customer {
// throw new IllegalStateException("This method must not be called when the shadow variables are not initialized yet."); // throw new IllegalStateException("This method must not be called when the shadow variables are not initialized yet.");
} }
if (previousCustomer == null) { if (previousCustomer == null) {
return technician.getDepot().getLocation().getPathTimeTo(technician.getVehicleType(),location); return technician.getDepot().getLocation().getPathTimeTo(technician.getVehicleType(), location);
} }
return previousCustomer.getLocation().getPathTimeTo(technician.getVehicleType(),location); return previousCustomer.getLocation().getPathTimeTo(technician.getVehicleType(), location);
} }
@Override @Override
......
package com.dituhui.pea.dispatch.quartz.dispatch; package com.dituhui.pea.dispatch.quartz.dispatch;
import com.dituhui.pea.dispatch.common.RedissonUtil;
import com.dituhui.pea.dispatch.service.SchedulerService; import com.dituhui.pea.dispatch.service.SchedulerService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution; import org.quartz.DisallowConcurrentExecution;
...@@ -23,7 +24,7 @@ import javax.annotation.Resource; ...@@ -23,7 +24,7 @@ import javax.annotation.Resource;
@DisallowConcurrentExecution @DisallowConcurrentExecution
public class AutoDispatchJob extends QuartzJobBean { public class AutoDispatchJob extends QuartzJobBean {
public static final String TEAM_JOB_PREFIX="BOXI_TEAM_"; public static final String TEAM_JOB_PREFIX = "BOXI_TEAM_";
@Resource @Resource
private SchedulerService schedulerService; private SchedulerService schedulerService;
...@@ -36,6 +37,9 @@ public class AutoDispatchJob extends QuartzJobBean { ...@@ -36,6 +37,9 @@ public class AutoDispatchJob extends QuartzJobBean {
String teamId = name.substring(TEAM_JOB_PREFIX.length()); String teamId = name.substring(TEAM_JOB_PREFIX.length());
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
log.info(">>> 自动派工(teamId:{}) 自动任务开始", teamId); log.info(">>> 自动派工(teamId:{}) 自动任务开始", teamId);
/*RedissonUtil.lockOperation(AutoDispatchJob.TEAM_JOB_PREFIX + teamId, 60, () -> {
schedulerService.dispatchRun2(teamId);
});*/
schedulerService.dispatchRun2(teamId); schedulerService.dispatchRun2(teamId);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
log.info(">>> 自动派工(teamId:{}) 自动任务结束,耗时:{}", teamId, end - start); log.info(">>> 自动派工(teamId:{}) 自动任务结束,耗时:{}", teamId, end - start);
......
...@@ -113,52 +113,45 @@ public class SchedulerServiceImpl implements SchedulerService { ...@@ -113,52 +113,45 @@ public class SchedulerServiceImpl implements SchedulerService {
for (int i = 1; i <= nextDaysLimit; i++) { for (int i = 1; i <= nextDaysLimit; i++) {
String currDay = LocalDate.now().plusDays(i).format(DateTimeFormatter.ISO_LOCAL_DATE); String currDay = LocalDate.now().plusDays(i).format(DateTimeFormatter.ISO_LOCAL_DATE);
boolean finalCutOff = cutOff; Optional<DispatchBatch> optional = dispatchBatchRepository.findByTeamIdAndBatchDate(teamId, currDay);
RedissonUtil.lockOperation(AutoDispatchJob.TEAM_JOB_PREFIX + teamId, 60, () -> { if (optional.isPresent()
dispatchRun2OneDay(teamId, currDay, today, finalCutOff); && Objects.nonNull(optional.get().getCutoffedTime())
}); && DateUtil.format(optional.get().getCutoffedTime(), "yyyy-MM-dd").equals(today)) {
} //自动任务截止
} log.error(">>> teamId:{}, day:{} 自动任务已截止", teamId, currDay);
private void dispatchRun2OneDay(String teamId, String currDay, String today, boolean cutOff) {
Optional<DispatchBatch> optional = dispatchBatchRepository.findByTeamIdAndBatchDate(teamId, currDay);
if (optional.isPresent()
&& Objects.nonNull(optional.get().getCutoffedTime())
&& DateUtil.format(optional.get().getCutoffedTime(), "yyyy-MM-dd").equals(today)) {
//自动任务截止
log.error(">>> teamId:{}, day:{} 自动任务已截止", teamId, currDay);
return;
}
try {
log.info("dispatchRun begin----- teamId:{}, day:{}", teamId, currDay);
String batchNo = batchService.buildBatchData2(teamId, currDay, cutOff);
UUID problemId = solveService.generateProblemId(teamId, batchNo);
log.info("dispatchRun teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
DispatchSolution problem = solveService.prepareSolution2(teamId, batchNo, currDay);
if (problem.getCustomerList().size() <= 0) {
log.info("dispatchRun 当前批次没有待指派工单 , teamId:{}, day:{}, batch:{}, problemId:{}, order-size:{}", teamId, currDay, batchNo, problemId, problem.getCustomerList().size());
return; return;
} }
log.info("dispatchRun prepare done, teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
Solver<DispatchSolution> solver = solverFactory.buildSolver(); try {
DispatchSolution solution = solver.solve(problem); log.info("dispatchRun begin----- teamId:{}, day:{}", teamId, currDay);
DispatchSolutionUtils.removeHardConstraintCustomer(solution, solverFactory);
log.info("dispatchRun solve done, teamId:{}, day:{}, batch:{}, problemId:{}, score:{}", teamId, currDay, batchNo, problemId, solution.getScore().toShortString()); String batchNo = batchService.buildBatchData2(teamId, currDay, cutOff);
this.solveService.saveSolutionWrp2(solution); UUID problemId = solveService.generateProblemId(teamId, batchNo);
this.extractService.extractDispatchToOrder2(teamId, batchNo, cutOff); log.info("dispatchRun teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
log.info("dispatchRun done ------ teamId:{}, day:{}", teamId, currDay);
DispatchSolution problem = solveService.prepareSolution2(teamId, batchNo, currDay);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
exporter.write(solution, new File(String.format("dispatchSolution_%s_%s.json", teamId, currDay))); if (problem.getCustomerList().size() <= 0) {
log.info("dispatchRun 当前批次没有待指派工单 , teamId:{}, day:{}, batch:{}, problemId:{}, order-size:{}", teamId, currDay, batchNo, problemId, problem.getCustomerList().size());
//log.info("dispatchRun group:{}, team:{} done", groupId, teamId); return;
} catch (Exception e) { }
log.error(">>> (teamId:{}, day:{})自动排班失败:{}", teamId, currDay, e.getMessage(), e); log.info("dispatchRun prepare done, teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
//throw e; Solver<DispatchSolution> solver = solverFactory.buildSolver();
DispatchSolution solution = solver.solve(problem);
DispatchSolutionUtils.removeHardConstraintCustomer(solution, solverFactory);
log.info("dispatchRun solve done, teamId:{}, day:{}, batch:{}, problemId:{}, score:{}", teamId, currDay, batchNo, problemId, solution.getScore().toShortString());
this.solveService.saveSolutionWrp2(solution);
this.extractService.extractDispatchToOrder2(teamId, batchNo, cutOff);
log.info("dispatchRun done ------ teamId:{}, day:{}", teamId, currDay);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
exporter.write(solution, new File(String.format("dispatchSolution_%s_%s.json", teamId, currDay)));
//log.info("dispatchRun group:{}, team:{} done", groupId, teamId);
} catch (Exception e) {
log.error(">>> (teamId:{}, day:{})自动排班失败:{}", teamId, currDay, e.getMessage(), e);
//throw e;
}
} }
} }
} }
\ No newline at end of file
...@@ -5,13 +5,12 @@ import static java.util.Comparator.comparing; ...@@ -5,13 +5,12 @@ import static java.util.Comparator.comparing;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Arrays; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.optaplanner.constraint.streams.drools.DroolsConstraintStreamScoreDirector; import org.optaplanner.constraint.streams.drools.DroolsConstraintStreamScoreDirector;
...@@ -38,423 +37,442 @@ import com.dituhui.pea.dispatch.pojo.Technician; ...@@ -38,423 +37,442 @@ import com.dituhui.pea.dispatch.pojo.Technician;
public class DispatchSolutionUtils { public class DispatchSolutionUtils {
/** /**
* 打印方案 * 打印方案
* *
* @param solution * @param solution
*/ */
public static void printSolution(DispatchSolution solution) { public static void printSolution(DispatchSolution solution) {
System.out.println("Score: " + solution.getScore().toShortString()); System.out.println("Score: " + solution.getScore().toShortString());
System.out.println("技能约束:"); System.out.println("技能约束:");
solution.getTechnicianList().forEach(technician -> { solution.getTechnicianList().forEach(technician -> {
System.out.printf("技术员%s(%s) %s%n", technician.getId(), technician.getCode(), technician.getSkills()); System.out.printf("技术员%s(%s) %s%n", technician.getId(), technician.getCode(), technician.getSkills());
for (Customer customer : technician.getCustomerList()) { for (Customer customer : technician.getCustomerList()) {
if (!technician.getSkills().contains(customer.getRequiredSkill())) { if (!technician.getSkills().contains(customer.getRequiredSkill())) {
// no match // no match
System.err.printf(" 预约单%s(%s) %s%n", customer.getId(), customer.getCode(), System.err.printf(" 预约单%s(%s) %s%n", customer.getId(), customer.getCode(),
customer.getRequiredSkill()); customer.getRequiredSkill());
} else { } else {
System.out.printf(" 预约单%s(%s) %s%n", customer.getId(), customer.getCode(), System.out.printf(" 预约单%s(%s) %s%n", customer.getId(), customer.getCode(),
customer.getRequiredSkill()); customer.getRequiredSkill());
} }
} }
}); });
AtomicInteger totalNum = new AtomicInteger(0); AtomicInteger totalNum = new AtomicInteger(0);
solution.getTechnicianList().forEach(technician -> { solution.getTechnicianList().forEach(technician -> {
System.out.printf("技术员%s(%s) [%s,%s]%n", technician.getId(), technician.getCode(), System.out.printf("技术员%s(%s) [%s,%s]%n", technician.getId(), technician.getCode(),
printTime(technician.getTimeWindows()[0][0]), printTime(technician.getTimeWindows()[0][1])); printTime(technician.getTimeWindows()[0][0]), printTime(technician.getTimeWindows()[0][1]));
totalNum.addAndGet(technician.getCustomerList().size()); totalNum.addAndGet(technician.getCustomerList().size());
for (Customer customer : technician.getCustomerList()) { for (Customer customer : technician.getCustomerList()) {
Customer previousCustomer = customer.getPreviousCustomer(); Customer previousCustomer = customer.getPreviousCustomer();
int startPath, endPath;// 路上时间 int startPath, endPath;// 路上时间
if (null == previousCustomer) { if (null == previousCustomer) {
startPath = technician.getDepot().getStartTime(); startPath = technician.getDepot().getStartTime();
// endPath = startPath + // endPath = startPath +
// customer.getLocation().getPathTimeTo(technician.getDepot().getLocation()); // customer.getLocation().getPathTimeTo(technician.getDepot().getLocation());
endPath = startPath + technician.getDepot().getLocation().getPathTimeTo(technician.getVehicleType(),customer.getLocation()); endPath = startPath + technician.getDepot().getLocation().getPathTimeTo(technician.getVehicleType(), customer.getLocation());
} else { } else {
startPath = previousCustomer.getDepartureTime(); startPath = previousCustomer.getDepartureTime();
// endPath = startPath + // endPath = startPath +
// customer.getLocation().getPathTimeTo(previousCustomer.getLocation()); // customer.getLocation().getPathTimeTo(previousCustomer.getLocation());
endPath = startPath + previousCustomer.getLocation().getPathTimeTo(technician.getVehicleType(),customer.getLocation()); endPath = startPath + previousCustomer.getLocation().getPathTimeTo(technician.getVehicleType(), customer.getLocation());
} }
if (customer.getArrivalTime() > customer.getEndTime()) { if (customer.getArrivalTime() > customer.getEndTime()) {
// 迟到 // 迟到
System.err.printf( System.err.printf(
" 预约单%s(%s) 预约时间窗[%s=>%s] 路上时间[%s=>%s] 早到等待时间[%s=>%s] 派工时间[%s=>%s] 迟到时间[%s=>%s]%n", " 预约单%s(%s) 预约时间窗[%s=>%s] 路上时间[%s=>%s] 早到等待时间[%s=>%s] 派工时间[%s=>%s] 迟到时间[%s=>%s]%n",
customer.getId(), customer.getCode(), customer.getId(), customer.getCode(),
// 预约时间窗 // 预约时间窗
printTime(customer.getStartTime()), printTime(customer.getEndTime()), printTime(customer.getStartTime()), printTime(customer.getEndTime()),
// 路上时间 // 路上时间
printTime(startPath), printTime(endPath), printTime(startPath), printTime(endPath),
// 早到等待时间 // 早到等待时间
customer.getArrivalTime() < customer.getStartTime() ? printTime(endPath) : "", customer.getArrivalTime() < customer.getStartTime() ? printTime(endPath) : "",
customer.getArrivalTime() < customer.getStartTime() ? printTime(customer.getStartTime()) customer.getArrivalTime() < customer.getStartTime() ? printTime(customer.getStartTime())
: "", : "",
// 派工时间 // 派工时间
printTime(customer.getArrivalTime()), printTime(customer.getDepartureTime()), printTime(customer.getArrivalTime()), printTime(customer.getDepartureTime()),
// 迟到时间 // 迟到时间
printTime(customer.getEndTime()), printTime(customer.getArrivalTime())); printTime(customer.getEndTime()), printTime(customer.getArrivalTime()));
} else { } else {
System.out.printf( System.out.printf(
" 预约单%s(%s) 预约时间窗[%s=>%s] 路上时间[%s=>%s] 早到等待时间[%s=>%s] 派工时间[%s=>%s] 迟到时间[%s=>%s]%n", " 预约单%s(%s) 预约时间窗[%s=>%s] 路上时间[%s=>%s] 早到等待时间[%s=>%s] 派工时间[%s=>%s] 迟到时间[%s=>%s]%n",
customer.getId(), customer.getCode(), customer.getId(), customer.getCode(),
// 预约时间窗 // 预约时间窗
printTime(customer.getStartTime()), printTime(customer.getEndTime()), printTime(customer.getStartTime()), printTime(customer.getEndTime()),
// 路上时间 // 路上时间
printTime(startPath), printTime(endPath), printTime(startPath), printTime(endPath),
// 早到等待时间 // 早到等待时间
customer.getArrivalTime() < customer.getStartTime() ? printTime(endPath) : "", customer.getArrivalTime() < customer.getStartTime() ? printTime(endPath) : "",
customer.getArrivalTime() < customer.getStartTime() ? printTime(customer.getStartTime()) customer.getArrivalTime() < customer.getStartTime() ? printTime(customer.getStartTime())
: "", : "",
// 派工时间 // 派工时间
printTime(customer.getArrivalTime()), printTime(customer.getDepartureTime()), printTime(customer.getArrivalTime()), printTime(customer.getDepartureTime()),
// 迟到时间 // 迟到时间
"", ""); "", "");
} }
} }
}); });
} }
private static String printTime(int startTime) { private static String printTime(int startTime) {
int hour = startTime / 60; int hour = startTime / 60;
int minite = startTime % 60; int minite = startTime % 60;
return StringUtils.leftPad("" + hour, 2, '0') + ":" + StringUtils.leftPad("" + minite, 2, '0'); return StringUtils.leftPad("" + hour, 2, '0') + ":" + StringUtils.leftPad("" + minite, 2, '0');
} }
/** /**
* 生成方案地图页 * 生成方案地图页
* *
* @param solution * @param solution
* @param filename * @param filename
*/ */
public static void exportMapHtml(DispatchSolution solution, String filename) { public static void exportMapHtml(DispatchSolution solution, String filename) {
try { try {
// 仓库起点 // 仓库起点
String depot = "\"" + solution.getDepot().getLocation().getX() + "," String depot = "\"" + solution.getDepot().getLocation().getX() + ","
+ solution.getDepot().getLocation().getY() + "\""; + solution.getDepot().getLocation().getY() + "\"";
// 技术员路线 // 技术员路线
String lines_ = "["; String lines_ = "[";
for (Technician technician : solution.getTechnicianList()) { for (Technician technician : solution.getTechnicianList()) {
if (technician.getCustomerList().size() > 0) { if (technician.getCustomerList().size() > 0) {
lines_ += "\"" + technician.getCustomerList().stream() lines_ += "\"" + technician.getCustomerList().stream()
.map(c -> c.getLocation().getX() + "," + c.getLocation().getY()) .map(c -> c.getLocation().getX() + "," + c.getLocation().getY())
.reduce((a, b) -> a + ";" + b).get() + "\","; .reduce((a, b) -> a + ";" + b).get() + "\",";
} }
} }
lines_ += "]"; lines_ += "]";
final String lines = lines_; final String lines = lines_;
// 技术员偏好中心点 // 技术员偏好中心点
String preferredlocation = "\"" + solution.getTechnicianList().stream() String preferredlocation = "\"" + solution.getTechnicianList().stream()
.map(c -> (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getX()) + "," .map(c -> (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getX()) + ","
+ (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getY())) + (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getY()))
.reduce((a, b) -> a + ";" + b).get() + "\""; .reduce((a, b) -> a + ";" + b).get() + "\"";
// 技术员名称 // 技术员名称
String names = "\"" + solution.getTechnicianList().stream() String names = "\"" + solution.getTechnicianList().stream()
.map(c -> (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getX()) + "," .map(c -> (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getX()) + ","
+ (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getY()) + "," + (c.getPreferredlocation() == null ? "" : c.getPreferredlocation().getY()) + ","
+ c.getCode()) + c.getCode())
.reduce((a, b) -> a + ";" + b).get() + "\""; .reduce((a, b) -> a + ";" + b).get() + "\"";
List<String> dispatchMapLines = IOUtils.readLines(new FileInputStream("data/dispatchMap.html"), "GBK"); List<String> dispatchMapLines = IOUtils.readLines(new FileInputStream("data/dispatchMap.html"), "GBK");
dispatchMapLines = dispatchMapLines.stream().map(line -> { dispatchMapLines = dispatchMapLines.stream().map(line -> {
if (StringUtils.startsWith(line, " var depot = ")) { if (StringUtils.startsWith(line, " var depot = ")) {
return " var depot = " + depot; return " var depot = " + depot;
} else if (StringUtils.startsWith(line, " var preferredlocation = ")) { } else if (StringUtils.startsWith(line, " var preferredlocation = ")) {
return " var preferredlocation = " + preferredlocation; return " var preferredlocation = " + preferredlocation;
} else if (StringUtils.startsWith(line, " var lines = ")) { } else if (StringUtils.startsWith(line, " var lines = ")) {
return " var lines = " + lines; return " var lines = " + lines;
} else if (StringUtils.startsWith(line, " var names = ")) { } else if (StringUtils.startsWith(line, " var names = ")) {
return " var names = " + names; return " var names = " + names;
} else{ } else {
return line; return line;
} }
}).collect(Collectors.toList()); }).collect(Collectors.toList());
IOUtils.writeLines(dispatchMapLines, "\r\n", new FileOutputStream(filename + ".html"), "GBK"); IOUtils.writeLines(dispatchMapLines, "\r\n", new FileOutputStream(filename + ".html"), "GBK");
System.out.println("output map : " + filename); System.out.println("output map : " + filename);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
* 计算方案分数 * 计算方案分数
* *
* @param solution * @param solution
* @return * @return
*/ */
public static HardSoftLongScore getScore(DispatchSolution solution) { public static HardSoftLongScore getScore(DispatchSolution solution) {
DefaultSolverFactory<DispatchSolution> solverFactory = getSolverFactory(); DefaultSolverFactory<DispatchSolution> solverFactory = getSolverFactory();
ScoreDirectorFactory<DispatchSolution> scoreDirectorFactory = solverFactory.getScoreDirectorFactory(); ScoreDirectorFactory<DispatchSolution> scoreDirectorFactory = solverFactory.getScoreDirectorFactory();
DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore> scoreDirector = (DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore>) scoreDirectorFactory DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore> scoreDirector = (DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore>) scoreDirectorFactory
.buildScoreDirector(); .buildScoreDirector();
// Set the working solution of the ScoreDirector object. // Set the working solution of the ScoreDirector object.
scoreDirector.setWorkingSolution(solution); scoreDirector.setWorkingSolution(solution);
// Iterate over the constraints in your problem and call the // Iterate over the constraints in your problem and call the
// `addConstraintMatch()` method // `addConstraintMatch()` method
// for each constraint that is violated by the solution. // for each constraint that is violated by the solution.
// for (Constraint constraint : myConstraints) { // for (Constraint constraint : myConstraints) {
// if (constraint.isViolated(mySolution)) { // if (constraint.isViolated(mySolution)) {
// scoreDirector.addConstraintMatch(constraint, constraint.getScore()); // scoreDirector.addConstraintMatch(constraint, constraint.getScore());
// } // }
// } // }
// Calculate the score of the solution. // Calculate the score of the solution.
HardSoftLongScore score = (HardSoftLongScore) scoreDirector.calculateScore(); HardSoftLongScore score = (HardSoftLongScore) scoreDirector.calculateScore();
return score; return score;
} }
/** /**
* 移动某个技术员的订单,返回新方案<br> * 移动某个技术员的订单,返回新方案<br>
* FIXME 没有成功 * FIXME 没有成功
* *
* @param solution * @param solution
* @param source 技术员 * @param source 技术员
* @param sourceIndex 0开始 * @param sourceIndex 0开始
* @param destinationIndex 0开始 * @param destinationIndex 0开始
* @return * @return
*/ */
public static DispatchSolution moveCustomer(DefaultSolverFactory<DispatchSolution> solverFactory, public static DispatchSolution moveCustomer(DefaultSolverFactory<DispatchSolution> solverFactory,
DispatchSolution solution, Technician source, int sourceIndex, int destinationIndex) { DispatchSolution solution, Technician source, int sourceIndex, int destinationIndex) {
if (null == solverFactory) { if (null == solverFactory) {
solverFactory = getSolverFactory(); solverFactory = getSolverFactory();
} }
ScoreDirectorFactory<DispatchSolution> scoreDirectorFactory = solverFactory.getScoreDirectorFactory(); ScoreDirectorFactory<DispatchSolution> scoreDirectorFactory = solverFactory.getScoreDirectorFactory();
DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore> scoreDirector = (DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore>) scoreDirectorFactory DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore> scoreDirector = (DroolsConstraintStreamScoreDirector<DispatchSolution, HardSoftLongScore>) scoreDirectorFactory
.buildScoreDirector(); .buildScoreDirector();
// Make a change to the solution. // Make a change to the solution.
// doMove(), doSwap(), or doChangeVariable() // doMove(), doSwap(), or doChangeVariable()
// Create a variable descriptor for the "customerList" list property // Create a variable descriptor for the "customerList" list property
ListVariableDescriptor<DispatchSolution> customerDescriptor = (ListVariableDescriptor<DispatchSolution>) scoreDirector ListVariableDescriptor<DispatchSolution> customerDescriptor = (ListVariableDescriptor<DispatchSolution>) scoreDirector
.getSolutionDescriptor().getEntityDescriptorStrict(Technician.class) .getSolutionDescriptor().getEntityDescriptorStrict(Technician.class)
.getVariableDescriptor("customerList"); .getVariableDescriptor("customerList");
// Create the ListChangeMove // Create the ListChangeMove
ListChangeMove<DispatchSolution> move = new ListChangeMove<DispatchSolution>(customerDescriptor, source, ListChangeMove<DispatchSolution> move = new ListChangeMove<DispatchSolution>(customerDescriptor, source,
sourceIndex, source, destinationIndex); sourceIndex, source, destinationIndex);
// apply the move and re-evaluate the score // apply the move and re-evaluate the score
scoreDirector.doAndProcessMove(move, true); scoreDirector.doAndProcessMove(move, true);
return scoreDirector.getWorkingSolution(); return scoreDirector.getWorkingSolution();
} }
public static DefaultSolverFactory<DispatchSolution> getSolverFactory() { public static DefaultSolverFactory<DispatchSolution> getSolverFactory() {
return getSolverFactory(0, 0); return getSolverFactory(0, 0);
} }
public static DefaultSolverFactory<DispatchSolution> getSolverFactory(long unimprovedSecondsSpentLimit, public static DefaultSolverFactory<DispatchSolution> getSolverFactory(long unimprovedSecondsSpentLimit,
long secondsSpentLimit) { long secondsSpentLimit) {
// 创建求解器配置 // 创建求解器配置
// 创建 SolverConfig 对象,并设置求解器配置 // 创建 SolverConfig 对象,并设置求解器配置
SolverConfig solverConfig = new SolverConfig(); SolverConfig solverConfig = new SolverConfig();
solverConfig.setSolutionClass(DispatchSolution.class); solverConfig.setSolutionClass(DispatchSolution.class);
solverConfig.withEntityClassList(Arrays.asList(Technician.class, Customer.class));// 这里不能漏掉,否则约束不生效 solverConfig.withEntityClassList(Arrays.asList(Technician.class, Customer.class));// 这里不能漏掉,否则约束不生效
TerminationConfig terminationConfig = new TerminationConfig(); TerminationConfig terminationConfig = new TerminationConfig();
terminationConfig terminationConfig
.setUnimprovedSecondsSpentLimit(unimprovedSecondsSpentLimit <= 0 ? 5 : unimprovedSecondsSpentLimit);// XX秒没有找到更好方案 .setUnimprovedSecondsSpentLimit(unimprovedSecondsSpentLimit <= 0 ? 5 : unimprovedSecondsSpentLimit);// XX秒没有找到更好方案
terminationConfig.setSecondsSpentLimit(secondsSpentLimit <= 0 ? 60 : secondsSpentLimit);// 总时间不能超过XXs terminationConfig.setSecondsSpentLimit(secondsSpentLimit <= 0 ? 60 : secondsSpentLimit);// 总时间不能超过XXs
solverConfig.withTerminationConfig(terminationConfig); solverConfig.withTerminationConfig(terminationConfig);
// 约束条件 // 约束条件
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class); solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.setScoreDirectorFactoryConfig( solverConfig.setScoreDirectorFactoryConfig(
new ScoreDirectorFactoryConfig().withConstraintProviderClass(DispatchConstraintProvider.class)); new ScoreDirectorFactoryConfig().withConstraintProviderClass(DispatchConstraintProvider.class));
DefaultSolverFactory<DispatchSolution> solverFactory = (DefaultSolverFactory<DispatchSolution>) SolverFactory DefaultSolverFactory<DispatchSolution> solverFactory = (DefaultSolverFactory<DispatchSolution>) SolverFactory
.<DispatchSolution>create(solverConfig); .<DispatchSolution>create(solverConfig);
return solverFactory; return solverFactory;
} }
/** /**
* 打印详细约束得分 * 打印详细约束得分
* *
* @param solution * @param solution
* @param solverFactory * @param solverFactory
*/ */
public static void explainSolutionConstraintDetail(DispatchSolution solution, DefaultSolverFactory<DispatchSolution> solverFactory) { public static void explainSolutionConstraintDetail(DispatchSolution solution, DefaultSolverFactory<DispatchSolution> solverFactory) {
// Obtain a ScoreExplanation object for the best solution // Obtain a ScoreExplanation object for the best solution
// Using score calculation outside the Solver // Using score calculation outside the Solver
// https://www.optaplanner.org/docs/optaplanner/latest/score-calculation/score-calculation.html // https://www.optaplanner.org/docs/optaplanner/latest/score-calculation/score-calculation.html
SolutionManager<DispatchSolution, HardSoftLongScore> scoreManager = SolutionManager.create(solverFactory); SolutionManager<DispatchSolution, HardSoftLongScore> scoreManager = SolutionManager.create(solverFactory);
ScoreExplanation<DispatchSolution, HardSoftLongScore> scoreExplanation = scoreManager.explain(solution); ScoreExplanation<DispatchSolution, HardSoftLongScore> scoreExplanation = scoreManager.explain(solution);
// System.out.println(scoreExplanation.getSummary()); // System.out.println(scoreExplanation.getSummary());
Map<String, ConstraintMatchTotal<HardSoftLongScore>> constraintMatchTotalMap = scoreExplanation Map<String, ConstraintMatchTotal<HardSoftLongScore>> constraintMatchTotalMap = scoreExplanation
.getConstraintMatchTotalMap(); .getConstraintMatchTotalMap();
constraintMatchTotalMap.forEach((key, value) -> { constraintMatchTotalMap.forEach((key, value) -> {
if (!value.getScore().isFeasible()) { if (!value.getScore().isFeasible()) {
// 违反硬约束 // 违反硬约束
System.out.printf("%s 匹配%s次 hard得分:%s%n", value.getConstraintName(), value.getConstraintMatchCount(), System.out.printf("%s 匹配%s次 hard得分:%s%n", value.getConstraintName(), value.getConstraintMatchCount(),
value.getScore().hardScore()); value.getScore().hardScore());
} else { } else {
// 软约束 // 软约束
System.out.printf("%s 匹配%s次 soft得分:%s%n", value.getConstraintName(), value.getConstraintMatchCount(), System.out.printf("%s 匹配%s次 soft得分:%s%n", value.getConstraintName(), value.getConstraintMatchCount(),
value.getScore().softScore()); value.getScore().softScore());
} }
value.getConstraintMatchSet().stream().sorted(comparing(ConstraintMatch::getScore)) value.getConstraintMatchSet().stream().sorted(comparing(ConstraintMatch::getScore))
.forEach(constraintMatch -> { .forEach(constraintMatch -> {
String text = ""; String text = "";
switch (ConstraintNameEnum.valueOf(value.getConstraintName())) { switch (ConstraintNameEnum.valueOf(value.getConstraintName())) {
case skillMatch: case skillMatch:
case customerTimeWindowsMatch: case customerTimeWindowsMatch:
case dispatchedMatch: case dispatchedMatch:
for (Object indictedObject : constraintMatch.getIndictedObjectList()) { for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反硬约束对象,根据具体约束返回不同类型对象 // 违反硬约束对象,根据具体约束返回不同类型对象
if (indictedObject instanceof Customer) { if (indictedObject instanceof Customer) {
Customer customer = (Customer) indictedObject; Customer customer = (Customer) indictedObject;
text += customer.getCode() + ","; text += customer.getCode() + ",";
} }
} }
System.out.printf(" 预约单(%s)违反约束,扣分%s%n", text, constraintMatch.getScore().toShortString()); System.out.printf(" 预约单(%s)违反约束,扣分%s%n", text, constraintMatch.getScore().toShortString());
break; break;
case totalDistance: case totalDistance:
case preferredTotalDistance: case preferredTotalDistance:
for (Object indictedObject : constraintMatch.getIndictedObjectList()) { for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反软约束对象,根据具体约束返回不同类型对象 // 违反软约束对象,根据具体约束返回不同类型对象
if (indictedObject instanceof Technician) { if (indictedObject instanceof Technician) {
Technician technician = (Technician) indictedObject; Technician technician = (Technician) indictedObject;
text += technician.getCode() + ","; text += technician.getCode() + ",";
} }
} }
System.out.printf(" 技术员(%s)总路程得分%s%n", text, constraintMatch.getScore().toShortString()); System.out.printf(" 技术员(%s)总路程得分%s%n", text, constraintMatch.getScore().toShortString());
break; break;
case technicianBalanceSoft: case technicianBalanceSoft:
if (constraintMatch.getScore().softScore() < 0) { if (constraintMatch.getScore().softScore() < 0) {
for (Object indictedObject : constraintMatch.getIndictedObjectList()) { for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反软约束对象,根据具体约束返回不同类型对象 // 违反软约束对象,根据具体约束返回不同类型对象
if (indictedObject instanceof Technician) { if (indictedObject instanceof Technician) {
Technician technician = (Technician) indictedObject; Technician technician = (Technician) indictedObject;
text += technician.getCode() + ","; text += technician.getCode() + ",";
} }
} }
System.out.printf(" 技术员(%s)订单量差距得分%s%n", text, System.out.printf(" 技术员(%s)订单量差距得分%s%n", text,
constraintMatch.getScore().toShortString()); constraintMatch.getScore().toShortString());
} }
break; break;
default: default:
break; break;
} }
}); });
}); });
} }
/** /**
* 导出json结构 * 导出json结构
* *
* @param solution * @param solution
* @param fileName * @param fileName
*/ */
public static void exportSolutionJson(DispatchSolution solution, String fileName) { public static void exportSolutionJson(DispatchSolution solution, String fileName) {
// Create a JacksonSolutionFileIO instance. // Create a JacksonSolutionFileIO instance.
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>( JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(
DispatchSolution.class); DispatchSolution.class);
// Set the output file. // Set the output file.
exporter.write(solution, new File(fileName)); exporter.write(solution, new File(fileName));
} }
/** /**
* 移除hard约束元素订单 * 移除hard约束元素订单
* *
* @param solution * @param solution
* @param solverFactory * @param solverFactory
*/ */
public static void removeHardConstraintCustomer(DispatchSolution solution, public static void removeHardConstraintCustomer(DispatchSolution solution,
SolverFactory<DispatchSolution> solverFactory) { SolverFactory<DispatchSolution> solverFactory) {
SolutionManager<DispatchSolution, HardSoftLongScore> scoreManager = SolutionManager.create(solverFactory); if ("1719308075152764928".equals(solution.getTeamId())) {
ScoreExplanation<DispatchSolution, HardSoftLongScore> scoreExplanation = scoreManager.explain(solution); System.out.println(solution.getTeamId());
Map<String, ConstraintMatchTotal<HardSoftLongScore>> constraintMatchTotalMap = scoreExplanation }
.getConstraintMatchTotalMap(); SolutionManager<DispatchSolution, HardSoftLongScore> scoreManager = SolutionManager.create(solverFactory);
constraintMatchTotalMap.forEach((key, value) -> { ScoreExplanation<DispatchSolution, HardSoftLongScore> scoreExplanation = scoreManager.explain(solution);
if (!value.getScore().isFeasible()) { Map<String, ConstraintMatchTotal<HardSoftLongScore>> constraintMatchTotalMap = scoreExplanation
// 违反硬约束 .getConstraintMatchTotalMap();
value.getConstraintMatchSet().stream().sorted(comparing(ConstraintMatch::getScore)) constraintMatchTotalMap.forEach((key, value) -> {
.forEach(constraintMatch -> { if (!value.getScore().isFeasible()) {
switch (ConstraintNameEnum.valueOf(value.getConstraintName())) { // 违反硬约束
case skillMatch: value.getConstraintMatchSet().stream().sorted(comparing(ConstraintMatch::getScore))
case customerTimeWindowsMatch: .forEach(constraintMatch -> {
for (Object indictedObject : constraintMatch.getIndictedObjectList()) { switch (ConstraintNameEnum.valueOf(value.getConstraintName())) {
// 违反硬约束对象,根据具体约束返回不同类型对象 case skillMatch:
if (indictedObject instanceof Customer) { case customerTimeWindowsMatch:
Customer customer = (Customer) indictedObject; for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反硬约束对象,根据具体约束返回不同类型对象
// 更新shadow变量 if (indictedObject instanceof Customer) {
updateShadowVariable(customer); Customer customer = (Customer) indictedObject;
// 更新shadow变量
// 移除技术员 updateShadowVariable(customer);
customer.getTechnician().getCustomerList().remove(customer); // 移除技术员
customer.getTechnician().getCustomerList().remove(customer);
solution.getUnDispatchedCustomers().add(customer); solution.getUnDispatchedCustomers().add(customer);
} }
} }
break; break;
default: case technicianTimeWindowsMatch:
break; for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
} // 违反硬约束对象,根据具体约束返回不同类型对象
}); if (indictedObject instanceof Technician
} && ObjectUtil.isNotEmpty(((Technician) indictedObject).getCustomerList())) {
}); List<Customer> customerList = new ArrayList<>();
} customerList.addAll(((Technician) indictedObject).getCustomerList());
for (Customer customer : customerList) {
if (!customer.isInTechnicianTimeWindows()) {
private static void updateShadowVariable(Customer sourceCustomer) { // 更新shadow变量
if (sourceCustomer.getTechnician() == null) { updateShadowVariable(customer);
if (sourceCustomer.getArrivalTime() != null) { // 移除技术员
sourceCustomer.setArrivalTime(null); customer.getTechnician().getCustomerList().remove(customer);
} solution.getUnDispatchedCustomers().add(customer);
return; }
} }
}
// 移除当前订单引用 }
Customer previousCustomer = sourceCustomer.getPreviousCustomer(); break;
Customer nextCustomer = sourceCustomer.getNextCustomer(); default:
if (previousCustomer == null) { break;
// 当前订单是第一个订单 }
if(null != nextCustomer) { });
nextCustomer.setPreviousCustomer(null); }
} });
} }
if (nextCustomer == null) {
// 当前订单是最后一个订单
if(null != previousCustomer) { private static void updateShadowVariable(Customer sourceCustomer) {
previousCustomer.setNextCustomer(null); if (sourceCustomer.getTechnician() == null) {
} if (sourceCustomer.getArrivalTime() != null) {
} sourceCustomer.setArrivalTime(null);
if (previousCustomer != null && null != nextCustomer) { }
previousCustomer.setNextCustomer(nextCustomer); return;
nextCustomer.setPreviousCustomer(previousCustomer); }
}
// 移除当前订单引用
// 前一个的离开时间 Customer previousCustomer = sourceCustomer.getPreviousCustomer();
Integer departureTime; Customer nextCustomer = sourceCustomer.getNextCustomer();
if (previousCustomer == null) { if (previousCustomer == null) {
departureTime = (sourceCustomer.getTechnician().getDepot()).getStartTime(); // 当前订单是第一个订单
} else { if (null != nextCustomer) {
departureTime = previousCustomer.getDepartureTime(); nextCustomer.setPreviousCustomer(null);
} }
if (nextCustomer != null) { }
// 更新后续订单 if (nextCustomer == null) {
Customer shadowCustomer = nextCustomer; // 当前订单是最后一个订单
Integer arrivalTime = calculateArrivalTime(shadowCustomer, departureTime); if (null != previousCustomer) {
while (shadowCustomer != null && !Objects.equals(shadowCustomer.getArrivalTime(), arrivalTime)) { previousCustomer.setNextCustomer(null);
shadowCustomer.setArrivalTime(arrivalTime); }
departureTime = shadowCustomer.getDepartureTime(); }
shadowCustomer = shadowCustomer.getNextCustomer(); if (previousCustomer != null && null != nextCustomer) {
arrivalTime = calculateArrivalTime(shadowCustomer, departureTime); previousCustomer.setNextCustomer(nextCustomer);
} nextCustomer.setPreviousCustomer(previousCustomer);
} }
}
// 前一个的离开时间
private static Integer calculateArrivalTime(Customer customer, Integer previousDepartureTime) { Integer departureTime;
if (customer == null || previousDepartureTime == null) { if (previousCustomer == null) {
return null; departureTime = (sourceCustomer.getTechnician().getDepot()).getStartTime();
} } else {
return previousDepartureTime + customer.getPathTimeFromPreviousStandstill(); departureTime = previousCustomer.getDepartureTime();
} }
if (nextCustomer != null) {
// 更新后续订单
Customer shadowCustomer = nextCustomer;
Integer arrivalTime = calculateArrivalTime(shadowCustomer, departureTime);
while (shadowCustomer != null && !Objects.equals(shadowCustomer.getArrivalTime(), arrivalTime)) {
shadowCustomer.setArrivalTime(arrivalTime);
departureTime = shadowCustomer.getDepartureTime();
shadowCustomer = shadowCustomer.getNextCustomer();
arrivalTime = calculateArrivalTime(shadowCustomer, departureTime);
}
}
}
private static Integer calculateArrivalTime(Customer customer, Integer previousDepartureTime) {
if (customer == null || previousDepartureTime == null) {
return null;
}
return previousDepartureTime + customer.getPathTimeFromPreviousStandstill();
}
} }
...@@ -3,8 +3,8 @@ server: ...@@ -3,8 +3,8 @@ server:
dispatch: dispatch:
cron: cron:
expr: 0 */3 8-23 * * ? expr: 0 10 8-23 * * ?
next-day-limit: 20 next-day-limit: 2
scheduler: scheduler:
init-engineer-capacity: init-engineer-capacity:
......
...@@ -147,6 +147,8 @@ public enum StatusCodeEnum { ...@@ -147,6 +147,8 @@ public enum StatusCodeEnum {
ORDER_FINISHED("030", "订单已结束,请勿操作", false), ORDER_FINISHED("030", "订单已结束,请勿操作", false),
FENDAN_IS_TRANSCEND_AND_SPECIAL("031", "分单超派和特殊时间", false), FENDAN_IS_TRANSCEND_AND_SPECIAL("031", "分单超派和特殊时间", false),
ENGINEER_IS_LEAVE_TIME("032", "工单(%s)指派失败!%s的工作日历在该时间段已有日程安排", false),
; ;
/** /**
......
...@@ -357,7 +357,7 @@ public class DispatchServiceImpl implements DispatchService { ...@@ -357,7 +357,7 @@ public class DispatchServiceImpl implements DispatchService {
Result<OrderInfoEntity> entityResult = orderInfoService.insterEngineerOrders(engineerOrders, entity, skill, byTeamId, engineer); Result<OrderInfoEntity> entityResult = orderInfoService.insterEngineerOrders(engineerOrders, entity, skill, byTeamId, engineer);
if (!entityResult.getCode().equals(ResultEnum.SUCCESS.getCode())) { if (!entityResult.getCode().equals(ResultEnum.SUCCESS.getCode())) {
// return Result.failed("当前工程师无法预约合适时间"); // return Result.failed("当前工程师无法预约合适时间");
errorList.add(entity.getOrderId()); errorList.add("单号:" + entity.getOrderId());
continue; continue;
} }
entity = entityResult.getResult(); entity = entityResult.getResult();
...@@ -378,11 +378,9 @@ public class DispatchServiceImpl implements DispatchService { ...@@ -378,11 +378,9 @@ public class DispatchServiceImpl implements DispatchService {
successList.add(entity.getOrderId()); successList.add(entity.getOrderId());
} }
List<String> resultList = new ArrayList<>(); List<String> resultList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(successList)) {
resultList.add(String.format("%s订单指派(改派)成功", String.join(",", successList)));
}
if (CollectionUtils.isNotEmpty(errorList)) { if (CollectionUtils.isNotEmpty(errorList)) {
resultList.add(String.format("%s订单指派(改派)失败,当前工程师无法预约合适时间", String.join(",", errorList))); String msg = errorList.size() > 3 ? String.join(",", errorList.subList(0, 3)) + "..." : String.join(",", errorList);
resultList.add(String.format(StatusCodeEnum.ENGINEER_IS_LEAVE_TIME.getDesc(), msg, engineer.getName()));
} }
return Result.success(CollectionUtils.isNotEmpty(resultList) ? String.join(",", resultList) : null); return Result.success(CollectionUtils.isNotEmpty(resultList) ? String.join(",", resultList) : null);
} }
......
...@@ -290,7 +290,7 @@ public class FendanServiceImpl implements FendanService { ...@@ -290,7 +290,7 @@ public class FendanServiceImpl implements FendanService {
} }
//最后处理全技能数据 //最后处理全技能数据
if (i == mapBlockInfoList.size() - 1 && null != allLayer) { if (i == mapBlockInfoList.size() - 1 && null != allLayer) {
OrgTeamInfo orgTeamInfoAll = BeanUtil.copyProperties(orgTeamEntity, OrgTeamInfo.class); OrgTeamInfo orgTeamInfoAll = BeanUtil.copyProperties(teamMap.get(allLayer.getTeamId()), OrgTeamInfo.class);
orgGroup.setTeamInfos(Arrays.asList(orgTeamInfoAll)); orgGroup.setTeamInfos(Arrays.asList(orgTeamInfoAll));
allLayer = null; allLayer = null;
} }
......
...@@ -35,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -35,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -450,7 +451,7 @@ public class OrderInfoServiceImpl implements OrderInfoService { ...@@ -450,7 +451,7 @@ public class OrderInfoServiceImpl implements OrderInfoService {
/** /**
* 根据工程师当日订单插入订单 * 根据工程师当日订单插入订单(根据工单预计开始时间插入,其他顺延)
* *
* @param skillInfo 待指派订单技能 * @param skillInfo 待指派订单技能
* @param engineerOrders 待指派工程师当日工单 * @param engineerOrders 待指派工程师当日工单
...@@ -462,20 +463,16 @@ public class OrderInfoServiceImpl implements OrderInfoService { ...@@ -462,20 +463,16 @@ public class OrderInfoServiceImpl implements OrderInfoService {
@Transactional @Transactional
@Override @Override
public Result<OrderInfoEntity> insterEngineerOrders(List<OrderInfoEntity> engineerOrders, OrderInfoEntity insertOrder, SkillInfoEntity skillInfo, OrgTeamEntity orgTeam, EngineerInfoEntity engineer) { public Result<OrderInfoEntity> insterEngineerOrders(List<OrderInfoEntity> engineerOrders, OrderInfoEntity insertOrder, SkillInfoEntity skillInfo, OrgTeamEntity orgTeam, EngineerInfoEntity engineer) {
// 获取客户期望时间段
LocalDateTime workStartTime = insertOrder.getExpectTimeBegin();
LocalDateTime workEndTime = insertOrder.getExpectTimeEnd();
//查询工程师当天出勤时间
// List<OccupyInfoDetail> engineerWorkTimeSlice = engineerCalendarService.timeWindowsSlice(engineer.getEngineerCode(), orgTeam.getTeamId(), insertOrder.getDt());
//查询工程师当天请假时间 //查询工程师当天请假时间
List<OccupyInfoDetail> engineerLeaveTimeSlice = engineerCalendarService.getEngineerWorkDayCalendar(engineer.getEngineerCode(), insertOrder.getDt()); List<OccupyInfoDetail> engineerLeaveTimeSlice = engineerCalendarService.getEngineerWorkDayCalendar(engineer.getEngineerCode(), insertOrder.getDt());
log.info("【engineerWorkTimeSlice】结果------------->{}", JsonUtil.toJson(engineerLeaveTimeSlice)); log.info("【engineerWorkTimeSlice】结果------------->{}", JsonUtil.toJson(engineerLeaveTimeSlice));
// 没有单直接返回 // 没有单直接返回
if (CollectionUtils.isEmpty(engineerOrders)) { if (CollectionUtils.isEmpty(engineerOrders)) {
LocalDateTime planStartTime = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime()); Result handleLeaveTimeResult = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime(), true, insertOrder);
if (ObjectUtil.isNull(planStartTime)) { if (!handleLeaveTimeResult.getCode().equals(StatusCodeEnum.SUCCESS)) {
return Result.failed(insertOrder); return Result.failed(insertOrder);
} }
LocalDateTime planStartTime = (LocalDateTime) handleLeaveTimeResult.getResult();
insertOrder.setTakeTime(skillInfo.getTakeTime()); insertOrder.setTakeTime(skillInfo.getTakeTime());
insertOrder.setPlanStartTime(planStartTime); insertOrder.setPlanStartTime(planStartTime);
insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime())); insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime()));
...@@ -501,96 +498,114 @@ public class OrderInfoServiceImpl implements OrderInfoService { ...@@ -501,96 +498,114 @@ public class OrderInfoServiceImpl implements OrderInfoService {
} }
// 没有单直接返回 // 没有单直接返回
if (CollectionUtils.isEmpty(orderSegments)) { if (CollectionUtils.isEmpty(orderSegments)) {
LocalDateTime planStartTime = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime()); Result handleLeaveTimeResult = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime(), true, insertOrder);
if (ObjectUtil.isNull(planStartTime)) { if (!handleLeaveTimeResult.getCode().equals(StatusCodeEnum.SUCCESS)) {
return Result.failed(insertOrder); return Result.failed(insertOrder);
} }
LocalDateTime planStartTime = (LocalDateTime) handleLeaveTimeResult.getResult();
insertOrder.setTakeTime(skillInfo.getTakeTime()); insertOrder.setTakeTime(skillInfo.getTakeTime());
insertOrder.setPlanStartTime(planStartTime); insertOrder.setPlanStartTime(planStartTime);
insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime())); insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime()));
return Result.success(insertOrder); return Result.success(insertOrder);
} }
OrderSegment seg = new OrderSegment();
seg.setX(Double.parseDouble(insertOrder.getX()));
seg.setY(Double.parseDouble(insertOrder.getY()));
seg.setOrderId(insertOrder.getOrderId());
seg.setElapsed(insertOrder.getArriveElapsed());
seg.setDistance(insertOrder.getArriveDistance());
seg.setStart(insertOrder.getExpectTimeBegin());
seg.setEnd(insertOrder.getExpectTimeEnd());
orderSegments.add(seg);
orderSegments = orderSegments.stream().sorted(Comparator.comparing(OrderSegment::getStart)).collect(Collectors.toList()); orderSegments = orderSegments.stream().sorted(Comparator.comparing(OrderSegment::getStart)).collect(Collectors.toList());
// 工单排序
boolean isMove = false;
int moveTime = 0;
for (int i = 0; i < orderSegments.size(); i++) { for (int i = 0; i < orderSegments.size(); i++) {
OrderSegment orderSegment = orderSegments.get(i); OrderSegment orderSegment = orderSegments.get(i);
Result<DistanceDTO> roadResult = pathService.getRoadDistance(insertOrder.getOrderId(), orderSegment.getOrderId(), engineer.getVehicle()); if (isMove && !orderSegment.getOrderId().equals(insertOrder.getOrderId()) && moveTime > 0) {
log.info("【getRoadDistance】参数------------->{},{},{}", insertOrder.getOrderId(), orderSegment.getOrderId(), engineer.getVehicle()); OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(orderSegment.getOrderId());
log.info("【getRoadDistance】结果------------->{}", JsonUtil.toJson(roadResult)); nestOrder.setPlanStartTime(nestOrder.getPlanStartTime().plusMinutes(moveTime));
if (!roadResult.getCode().equals(ResultEnum.SUCCESS.getCode())) { nestOrder.setPlanEndTime(nestOrder.getPlanEndTime().plusMinutes(moveTime));
orderInfoDao.save(nestOrder);
continue; continue;
} }
OrderSegment nestOrderSegment = orderSegments.get(i + 1);
//最近两单都不是需要插入的单直接跳过当前单
if (!orderSegment.getOrderId().equals(insertOrder.getOrderId()) && !nestOrderSegment.getOrderId().equals(insertOrder.getOrderId())) {
continue;
}
Result<DistanceDTO> roadResult = pathService.getRoadDistance(orderSegment.getOrderId(), nestOrderSegment.getOrderId(), engineer.getVehicle());
log.info("【getRoadDistance】参数------------->{},{},{}", orderSegment.getOrderId(), nestOrderSegment.getOrderId(), engineer.getVehicle());
log.info("【getRoadDistance】结果------------->{}", JsonUtil.toJson(roadResult));
DistanceDTO distanceDTO = roadResult.getResult(); DistanceDTO distanceDTO = roadResult.getResult();
// int roadTime = distanceDTO.getTime() / 60;
int roadTime = BigDecimal.valueOf(distanceDTO.getTime()).divide(BigDecimal.valueOf(60), 0, RoundingMode.HALF_UP).intValue(); int roadTime = BigDecimal.valueOf(distanceDTO.getTime()).divide(BigDecimal.valueOf(60), 0, RoundingMode.HALF_UP).intValue();
// 先判断第一单开始时间 if (orderSegment.getOrderId().equals(insertOrder.getOrderId())) {
if (i == 0 && orderSegment.getStart().compareTo(workStartTime.plusMinutes(takeTime + roadTime)) >= 0) { Result handleLeaveTimeResult = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime(), true, insertOrder);
LocalDateTime planStartTime = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime()); if (!handleLeaveTimeResult.getCode().equals(ResultEnum.SUCCESS.getCode())) {
if (ObjectUtil.isNull(planStartTime)) {
return Result.failed(insertOrder); return Result.failed(insertOrder);
} }
LocalDateTime planStartTime = (LocalDateTime) handleLeaveTimeResult.getResult();
insertOrder.setTakeTime(skillInfo.getTakeTime()); insertOrder.setTakeTime(skillInfo.getTakeTime());
insertOrder.setPlanStartTime(planStartTime); insertOrder.setPlanStartTime(planStartTime);
insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime())); insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime()));
OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(orderSegment.getOrderId()); OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(nestOrderSegment.getOrderId());
nestOrder.setArriveDistance(roadTime); nestOrder.setArriveElapsed(roadTime);
insertOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue()); nestOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue());
if (orderSegment.getStart().plusMinutes(takeTime + roadTime).compareTo(nestOrder.getPlanStartTime()) > 0) {
long minutes = Duration.between(nestOrder.getPlanStartTime(), planStartTime.plusMinutes(skillInfo.getTakeTime() + roadTime)).toMinutes();
nestOrder.setPlanStartTime(nestOrder.getPlanStartTime().plusMinutes(minutes));
nestOrder.setPlanEndTime(nestOrder.getPlanEndTime().plusMinutes(minutes));
orderInfoDao.save(nestOrder);
moveTime = (int) minutes;
isMove = true;
continue;
}
orderInfoDao.save(nestOrder); orderInfoDao.save(nestOrder);
return Result.success(insertOrder); return Result.success(insertOrder);
} }
// 判断当前是不是最后一单 if (nestOrderSegment.getOrderId().equals(insertOrder.getOrderId())) {
if (i == orderSegments.size() - 1) { Result handleLeaveTimeResult = handleLeaveTime(engineerLeaveTimeSlice, insertOrder.getExpectTimeBegin(), insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime(), true, insertOrder);
// 最后一单如果没有符合条件的则代表没有合适时间 if (!handleLeaveTimeResult.getCode().equals(ResultEnum.SUCCESS.getCode())) {
if (workEndTime.compareTo(orderSegment.getEnd().plusMinutes(takeTime + roadTime)) < 0) { return handleLeaveTimeResult;
return Result.failed(insertOrder);
} }
// 判断工单开始时间 LocalDateTime planStartTime = (LocalDateTime) handleLeaveTimeResult.getResult();
LocalDateTime planStartTime = insertOrder.getExpectTimeBegin().compareTo(orderSegment.getEnd().plusMinutes(roadTime)) >= 0 ? if (orderSegment.getEnd().plusMinutes(roadTime).compareTo(planStartTime) <= 0) {
insertOrder.getExpectTimeBegin() : orderSegment.getEnd().plusMinutes(roadTime); OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(nestOrderSegment.getOrderId());
planStartTime = handleLeaveTime(engineerLeaveTimeSlice, planStartTime, insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime()); nestOrder.setArriveElapsed(roadTime);
if (ObjectUtil.isNull(planStartTime)) { nestOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue());
return Result.failed(insertOrder); orderInfoDao.save(nestOrder);
continue;
} }
insertOrder.setArriveElapsed(roadTime);
insertOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue());
insertOrder.setTakeTime(skillInfo.getTakeTime()); insertOrder.setTakeTime(skillInfo.getTakeTime());
insertOrder.setPlanStartTime(planStartTime); insertOrder.setPlanStartTime(planStartTime);
insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime())); insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime()));
return Result.success(insertOrder); if (i > 0) {
} OrderSegment orderSegmentPrev = orderSegments.get(i - 1);
// 当前单在中间 Result<DistanceDTO> roadResultPrev = pathService.getRoadDistance(orderSegmentPrev.getOrderId(), insertOrder.getOrderId(), engineer.getVehicle());
OrderSegment nestOrderSegment = orderSegments.get(i + 1); log.info("【getRoadDistancePrev】参数------------->{},{},{}", orderSegmentPrev.getOrderId(), insertOrder.getOrderId(), engineer.getVehicle());
Result<DistanceDTO> roadResult2 = pathService.getRoadDistance(nestOrderSegment.getOrderId(), insertOrder.getOrderId(), engineer.getVehicle()); log.info("【getRoadDistancePrev】结果------------->{}", JsonUtil.toJson(roadResultPrev));
log.info("【getRoadDistance2】参数------------->{},{},{}", nestOrderSegment.getOrderId(), insertOrder.getOrderId(), engineer.getVehicle()); int roadTimePrev = BigDecimal.valueOf(roadResultPrev.getResult().getTime()).divide(BigDecimal.valueOf(60), 0, RoundingMode.HALF_UP).intValue();
log.info("【getRoadDistance2】结果------------->{}", JsonUtil.toJson(roadResult2)); insertOrder.setArriveElapsed(roadTimePrev);
if (!roadResult.getCode().equals(ResultEnum.SUCCESS.getCode())) { insertOrder.setArriveDistance(BigDecimal.valueOf(roadResultPrev.getResult().getDis() * 1000).intValue());
continue;
}
int roadTime2 = BigDecimal.valueOf(roadResult2.getResult().getTime()).divide(BigDecimal.valueOf(60), 0, RoundingMode.HALF_UP).intValue();
// 判断工单开始时间 todo 计算不准确 if (nestOrderSegment.getStart().minusMinutes(roadTime2).compareTo(planStartTime.plusMinutes(takeTime + roadTime)) >= 0) {
LocalDateTime planStartTime = insertOrder.getExpectTimeBegin().compareTo(orderSegment.getEnd().plusMinutes(roadTime)) >= 0 ?
insertOrder.getExpectTimeBegin() : orderSegment.getEnd().plusMinutes(roadTime);
if (nestOrderSegment.getStart().minusMinutes(roadTime2).compareTo(planStartTime.plusMinutes(takeTime + roadTime)) >= 0) {
planStartTime = handleLeaveTime(engineerLeaveTimeSlice, planStartTime, insertOrder.getExpectTimeEnd(), skillInfo.getTakeTime());
if (ObjectUtil.isNull(planStartTime)) {
return Result.failed(insertOrder);
} }
insertOrder.setArriveElapsed(roadTime);
insertOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue());
insertOrder.setTakeTime(skillInfo.getTakeTime());
insertOrder.setPlanStartTime(planStartTime);
insertOrder.setPlanEndTime(planStartTime.plusMinutes(skillInfo.getTakeTime()));
OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(nestOrderSegment.getOrderId()); OrderInfoEntity nestOrder = orderInfoDao.getByOrderId(orderSegment.getOrderId());
nestOrder.setArriveElapsed(roadTime2); nestOrder.setArriveElapsed(roadTime);
nestOrder.setArriveDistance(BigDecimal.valueOf(roadResult2.getResult().getDis() * 1000).intValue()); nestOrder.setArriveDistance(BigDecimal.valueOf(distanceDTO.getDis() * 1000).intValue());
long minutes = Duration.between(nestOrder.getPlanStartTime(), planStartTime.plusMinutes(skillInfo.getTakeTime() + roadTime)).toMinutes();
nestOrder.setPlanStartTime(nestOrder.getPlanStartTime().plusMinutes(minutes));
nestOrder.setPlanEndTime(nestOrder.getPlanEndTime().plusMinutes(minutes));
orderInfoDao.save(nestOrder); orderInfoDao.save(nestOrder);
return Result.success(insertOrder); moveTime = (int) minutes;
isMove = true;
} }
} }
return Result.failed(insertOrder); return Result.success(insertOrder);
} }
/** /**
* 处理工程师请假时间和工单计划开始时间 * 处理工程师请假时间和工单计划开始时间
* *
...@@ -599,22 +614,64 @@ public class OrderInfoServiceImpl implements OrderInfoService { ...@@ -599,22 +614,64 @@ public class OrderInfoServiceImpl implements OrderInfoService {
* @param expectTimeEnd * @param expectTimeEnd
* @return 工单计划开始时间 * @return 工单计划开始时间
*/ */
private LocalDateTime handleLeaveTime(List<OccupyInfoDetail> engineerLeaveTimeSlice, LocalDateTime expectTimeBegin, LocalDateTime expectTimeEnd, Integer takeTime) { private LocalDateTime handleLeaveTime(List<OccupyInfoDetail> engineerLeaveTimeSlice, LocalDateTime expectTimeBegin, LocalDateTime expectTimeEnd, Integer takeTime, Integer rodeTime, boolean isLeave) {
if (CollectionUtils.isEmpty(engineerLeaveTimeSlice)) { if (CollectionUtils.isEmpty(engineerLeaveTimeSlice)) {
return expectTimeBegin; return expectTimeBegin;
} }
for (int i = 0; i < engineerLeaveTimeSlice.size(); i++) { for (int i = 0; i < engineerLeaveTimeSlice.size(); i++) {
OccupyInfoDetail occupyInfoDetail = engineerLeaveTimeSlice.get(i); OccupyInfoDetail occupyInfoDetail = engineerLeaveTimeSlice.get(i);
// 处理请假开始时间 // 请假之中
if (expectTimeBegin.plusMinutes(takeTime).compareTo(occupyInfoDetail.getBeginTime()) <= 0) { if (isLeave && (expectTimeBegin.compareTo(occupyInfoDetail.getBeginTime()) >= 0 || expectTimeBegin.plusMinutes(takeTime + rodeTime).compareTo(occupyInfoDetail.getBeginTime()) >= 0) &&
(expectTimeEnd.compareTo(occupyInfoDetail.getEndTime()) <= 0 || expectTimeEnd.plusMinutes(takeTime + rodeTime).compareTo(occupyInfoDetail.getEndTime()) <= 0)) {
return null;
}
// 请假开始之前能插入
if (expectTimeBegin.plusMinutes(takeTime + rodeTime).compareTo(occupyInfoDetail.getBeginTime()) <= 0) {
return expectTimeBegin; return expectTimeBegin;
} }
if (expectTimeEnd.compareTo(occupyInfoDetail.getEndTime().plusMinutes(takeTime)) >= 0) { // 请假时间内则顺延
if (expectTimeBegin.compareTo(occupyInfoDetail.getBeginTime()) >= 0 || expectTimeBegin.compareTo(occupyInfoDetail.getEndTime()) <= 0) {
return occupyInfoDetail.getEndTime();
}
if (expectTimeEnd.compareTo(occupyInfoDetail.getEndTime().plusMinutes(takeTime + rodeTime)) >= 0) {
return expectTimeBegin.compareTo(occupyInfoDetail.getEndTime()) >= 0 ? expectTimeBegin : occupyInfoDetail.getEndTime(); return expectTimeBegin.compareTo(occupyInfoDetail.getEndTime()) >= 0 ? expectTimeBegin : occupyInfoDetail.getEndTime();
} }
return null;
} }
return null; return expectTimeBegin;
}
/**
* 处理工程师请假时间和工单计划开始时间
*
* @param engineerLeaveTimeSlice
* @param expectTimeBegin
* @param expectTimeEnd
* @return 工单计划开始时间
*/
private Result<?> handleLeaveTime(List<OccupyInfoDetail> engineerLeaveTimeSlice, LocalDateTime expectTimeBegin, LocalDateTime expectTimeEnd, Integer takeTime, boolean isLeave, OrderInfoEntity insertOrder) {
if (CollectionUtils.isEmpty(engineerLeaveTimeSlice)) {
return Result.success(expectTimeBegin);
}
for (int i = 0; i < engineerLeaveTimeSlice.size(); i++) {
OccupyInfoDetail occupyInfoDetail = engineerLeaveTimeSlice.get(i);
// 请假之中
if (isLeave && (expectTimeBegin.compareTo(occupyInfoDetail.getBeginTime()) >= 0 || expectTimeBegin.plusMinutes(takeTime).compareTo(occupyInfoDetail.getBeginTime()) >= 0) &&
(expectTimeEnd.compareTo(occupyInfoDetail.getEndTime()) <= 0 || expectTimeEnd.plusMinutes(takeTime).compareTo(occupyInfoDetail.getEndTime()) <= 0)) {
return Result.failed(StatusCodeEnum.ENGINEER_IS_LEAVE_TIME);
}
// 请假开始之前能插入
if (expectTimeBegin.plusMinutes(takeTime).compareTo(occupyInfoDetail.getBeginTime()) <= 0) {
return Result.success(expectTimeBegin);
}
// 请假时间内则顺延
if (expectTimeBegin.compareTo(occupyInfoDetail.getBeginTime()) >= 0 || expectTimeBegin.compareTo(occupyInfoDetail.getEndTime()) <= 0) {
return Result.success(occupyInfoDetail.getEndTime());
}
if (expectTimeEnd.compareTo(occupyInfoDetail.getEndTime().plusMinutes(takeTime)) >= 0) {
return Result.success(expectTimeBegin.compareTo(occupyInfoDetail.getEndTime()) >= 0 ? expectTimeBegin : occupyInfoDetail.getEndTime());
}
}
return Result.success(expectTimeBegin);
} }
/** /**
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!