Commit 106e7b20 by chamberone

Merge branch 'develop' of https://zhangguoping@gitlab.dituhui.com/bsh/project/pr…

…oject.git into develop
2 parents 0ae1e257 bdbbc32b
......@@ -46,10 +46,10 @@
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-seata</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
......
......@@ -4,6 +4,7 @@ package com.dituhui.pea.dispatch.controller;
import cn.hutool.core.map.MapUtil;
import com.dituhui.pea.common.Result;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
import com.dituhui.pea.dispatch.entity.DispatchBatch;
import com.dituhui.pea.dispatch.pojo.Customer;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.pojo.Technician;
......@@ -13,8 +14,7 @@ import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.api.solver.SolverStatus;
import org.optaplanner.core.api.solver.*;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -23,7 +23,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLException;
import java.time.Duration;
import java.util.*;
......@@ -48,6 +47,8 @@ public class PrepareController {
private SolverManager<DispatchSolution, UUID> solverManager;
private Solver<DispatchSolution> solver;
public PrepareController() {
SolverConfig solverConfig = new SolverConfig().withSolutionClass(DispatchSolution.class);
......@@ -55,6 +56,8 @@ public class PrepareController {
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10));
SolverFactory<DispatchSolution> solverFactory = SolverFactory.create(solverConfig);
solver = solverFactory.buildSolver();
solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
}
......@@ -77,19 +80,36 @@ public class PrepareController {
}
// 重新进行数据准备、异步任务运行
@GetMapping("/solveAsync/{groupId}/{day}")
public Result<?> solveAsync(@PathVariable String groupId, @PathVariable String day) throws SQLException {
@GetMapping("/solveStart/{groupId}/{day}")
public Result<?> solveAsync(@PathVariable String groupId, @PathVariable String day) {
log.info("调用引擎处理-异步处理, groupId:{}, day:{}", groupId, day);
String batchNo = batchService.buildBatchData(groupId, day);
log.info("调用引擎处理-异步处理, groupId:{}, day:{}, batchNo:{}", groupId, day, batchNo);
UUID problemId = solveService.generateProblemId(groupId, batchNo);
// 提交问题开始求解
String batchNo = null;
batchNo = batchService.buildBatchData(groupId, day);
// DispatchBatch newBatch= batchService.queryBatchInfoByDay(groupId, day);
// log.info("newBatch:{}", newBatch);
log.info("调用引擎处理, groupId:{}, day:{}, batchNo:{}", groupId, day, batchNo);
DispatchSolution problem = solveService.prepareSolution(groupId, batchNo);
solverManager.solveAndListen(problemId, id -> problem,
this.extractService::saveAndExtractSolution);
DispatchSolution solution = solver.solve(problem);
solveService.saveSolutionWrp(solution);
extractService.extractDispatchToOrder(groupId, batchNo, false);
/*
会多次触发save回调, 暂不使用
// 提交问题开始求解
UUID problemId = solveService.generateProblemId(groupId, batchNo);
solverManager.solveAndListen(problemId, id -> problem,
this.solveService::saveAndExtractSolution);
log.error("调用引擎处理-异步处理, 已触发异步, groupId:{}, batchNo:{}", groupId, batchNo);
*/
return Result.success("已触发异步执行");
}
......
......@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchBatch;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
......
package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
......@@ -12,7 +13,6 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
List<DispatchOrder> findByGroupIdAndBatchNo(String groupId, String batchNo);
List<DispatchOrder> findByGroupIdAndBatchNoAndEngineerCodeNot(String groupId, String batchNo, String code);
@Query("from DispatchOrder where groupId=?1 and batchNo=?2 and engineerCode is not null and engineerCode!='' ")
List<DispatchOrder> findAssigned(String groupId, String batchNo);
......
package com.dituhui.pea.dispatch.exception;
import com.dituhui.pea.common.Result;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice(basePackages = "com.dituhui.pea.dispatch")
public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseBody
public Result<?> handleBindException(BindException e) {
// 处理 BindException 异常并返回自定义错误信息
return Result.failed("Invalid request parameters," + e.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseBody
public Result<?> handleException(Exception e) {
return Result.failed(e.getMessage());
}
}
......@@ -83,14 +83,10 @@ public class BatchScheduler {
}
if (null == dispatchBatch.getCutoffedTime()) {
log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 执行首次confirm----- group:{}, day:{}", groupId, currDay);
try {
this.extractService.extractDispatchToOrder(groupId, dispatchBatch.getBatchNo(), true);
dispatchBatch.setCutoffedTime(LocalDateTime.now());
batchService.saveDispatchBatch(dispatchBatch);
} catch (SQLException e) {
log.error("dispatchRun 已过cutoff时间,更新pre状态为confirm 异常 %s", e);
throw new RuntimeException(e);
}
} else {
log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 已经执行confirm,跳过当次计算 ----- group:{}, day:{}", groupId, currDay);
......@@ -110,8 +106,9 @@ public class BatchScheduler {
}
log.info("dispatchRun prepare done, group:{}, day:{}, batch:{}, problemId:{}", groupId, currDay, batchNo, problemId);
DispatchSolution solution = solver.solve(problem);
log.info("dispatchRun run done, group:{}, day:{}, batch:{}, problemId:{}, score:{}", groupId, currDay, batchNo, problemId, solution.getScore().toShortString());
this.extractService.saveAndExtractSolution(solution);
log.info("dispatchRun solve done, group:{}, day:{}, batch:{}, problemId:{}, score:{}", groupId, currDay, batchNo, problemId, solution.getScore().toShortString());
this.solveService.saveSolutionWrp(solution);
this.extractService.extractDispatchToOrder(groupId, batchNo, false);
log.info("dispatchRun done ------ group:{}, day:{}", groupId, currDay);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
......@@ -128,9 +125,6 @@ public class BatchScheduler {
} catch (InterruptedException e) {
log.info("error %s", e);
throw new RuntimeException(e);
} catch (SQLException e) {
log.info("error %s", e);
throw new RuntimeException(e);
}
log.info("done");
......
......@@ -16,8 +16,8 @@ public interface BatchService {
// 检查指定日期的小组是否有在运行的批次任务,有则返回,没有则创建后返回批次码
@Transactional
String buildBatchData(String groupId, String day) throws SQLException;
String buildBatchData(String groupId, String day);
DispatchBatch queryBatchInfoByDay(String groupId, String day);
......
......@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.service;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.SQLException;
......@@ -15,27 +16,14 @@ import java.sql.SQLException;
public interface ExtractService {
/*
* 将计算结果回写到dispatch2个表、以及order两个表
* 是下面两个方法的包装
* */
// @Transactional
void saveAndExtractSolution(DispatchSolution solution) throws RuntimeException;
/*
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
* */
// @Transactional
void saveSolutionToDispatch(String groupId, String batchNo, DispatchSolution solution) throws RuntimeException;
/*
* 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新)
* order_request(主要更新状态)
* */
// @Transactional
void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) throws SQLException;
void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) ;
}
......@@ -25,4 +25,13 @@ public interface SolveService {
DispatchSolution prepareSolution(String groupId, String batchNo) ;
/*
* 将计算结果回写到dispatch2个表
* 是下面两个方法的包装
* */
void saveSolutionWrp(DispatchSolution solution) throws RuntimeException;
}
......@@ -9,11 +9,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
......@@ -34,9 +36,13 @@ import java.util.Optional;
public class BatchServiceImpl implements BatchService {
@Autowired
DispatchBatchRepository batchRepository;
@Autowired
private EntityManager entityManager;
@Autowired
private JdbcTemplate jdbcTemplate;
......@@ -54,9 +60,10 @@ public class BatchServiceImpl implements BatchService {
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional
@Transactional(isolation = Isolation.READ_COMMITTED)
@Override
public String buildBatchData(String groupId, String day) {
entityManager.clear();
log.info("准备批次数据, groupId:{}, day:{}", groupId, day);
String batchNo = "";
String batchDay = "";
......@@ -125,8 +132,13 @@ public class BatchServiceImpl implements BatchService {
log.info("准备批次数据 orderCountPre :{}", orderCountPre);
if (orderCount + orderCountPre > 0) {
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=?, start_time=?, end_time=null, status='RUNNING' where group_id=? and batch_no=?",
engCount, orderCount + orderCountPre, LocalDateTime.now(), groupId, batchNo);
} else {
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=?, start_time=?, end_time=?, status='DONE' where group_id=? and batch_no=?",
engCount, orderCount + orderCountPre, LocalDateTime.now(), LocalDateTime.now(), groupId, batchNo);
}
log.info("准备批次数据完成, groupId:{}, day:{}, batchNo:{}", groupId, batchDay, batchNo);
......@@ -136,6 +148,7 @@ public class BatchServiceImpl implements BatchService {
@Override
public String queryBatchNoByDay(String groupId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day);
if (optional.isPresent()) {
return optional.get().getBatchNo();
......@@ -146,6 +159,7 @@ public class BatchServiceImpl implements BatchService {
@Override
public DispatchBatch queryBatchInfoByDay(String groupId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day);
return optional.orElseGet(DispatchBatch::new);
}
......
package com.dituhui.pea.dispatch.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.dituhui.pea.dispatch.dao.*;
import com.dituhui.pea.dispatch.entity.*;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import com.dituhui.pea.dispatch.entity.EngineerInfo;
import com.dituhui.pea.dispatch.entity.OrderAppointment;
import com.dituhui.pea.dispatch.entity.OrderRequest;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.util.FileUtil;
import org.drools.util.FileUtils;
import org.optaplanner.core.api.score.ScoreExplanation;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.solver.SolutionManager;
import org.optaplanner.core.api.solver.SolutionUpdatePolicy;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.persistence.jackson.impl.domain.solution.JacksonSolutionFileIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import javax.persistence.EntityManager;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;
/**
* @author zhangx
*/
@Slf4j
@Service
......@@ -42,9 +38,6 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired
DispatchBatchRepository batchRepository;
@Autowired
EngineerInfoRepository engineerInfoRepo;
......@@ -57,118 +50,28 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired
OrderAppointmentRepository orderAppointmentRepo;
@Autowired
private EntityManager entityManager;
@Autowired
private JdbcTemplate jdbcTemplate;
/*
* 将计算结果回写到dispatch2个表、以及order两个表
* */
// @Transactional
@Override
public void saveAndExtractSolution(DispatchSolution solution) throws RuntimeException {
String groupId = solution.getGroupId();
String batchNo = solution.getBatchNo();
log.info("算法结果回写包装方法, groupId:{}, batchNo:{}", groupId, batchNo);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
String fileName = String.format("dispatchSolution-%s-%s.json", groupId, batchNo);
File tempFile = new File(fileName);
exporter.write(solution, tempFile);
String dispatchResultJson = "{}";
try {
dispatchResultJson = FileUtil.readAsString(tempFile);
} catch (IOException e) {
log.error("json算法结果回写 error , groupId:{}, batchNo:{} ", groupId, batchNo, e);
}
Object[] paramBatch = {LocalDateTime.now(), dispatchResultJson, groupId, batchNo};
jdbcTemplate.update(" update dispatch_batch set status='DONE', end_time=? , ext=? where group_id=? and batch_no=? ", paramBatch);
this.saveSolutionToDispatch(groupId, batchNo, solution);
try {
this.extractDispatchToOrder(solution.getGroupId(), solution.getBatchNo(), false);
} catch (SQLException e) {
log.error("算法结果回写包装方法异常, groupId:{}, batchNo:{}", groupId, batchNo, e);
throw new RuntimeException(e);
}
}
/**
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
*/
// @Transactional
@Override
public void saveSolutionToDispatch(String groupId, String batchNo, DispatchSolution solution) throws RuntimeException {
log.info("算法结果回写dispatch, groupId:{}, batchNo:{}", groupId, batchNo);
// 清理当前批次指派结果
log.info("算法结果回写dispatch, step1-清除历史, groupId:{}, batchNo:{}", groupId, batchNo);
Object[] paramClear = {groupId, batchNo};
jdbcTemplate.update(" update dispatch_order set engineer_code='' , seq=0, time_begin=null ,time_end=null where group_id=? and batch_no=? ", paramClear);
log.info("算法结果回写dispatch, step2-开始回写, groupId:{}, batchNo:{}", groupId, batchNo);
// 保存当前批次指派结果
solution.getTechnicianList().forEach(technician -> {
log.info("算法结果回写dispatch, step2.1-按技术员逐个回写, groupId:{}, batchNo:{}, technician: {}, max-minute:{}, customlist.size:{}",
groupId, batchNo, technician.getCode(), technician.getMaxMinute(), technician.getCustomerList().size());
AtomicInteger seq = new AtomicInteger();
technician.getCustomerList().forEach(customer -> {
int idx = seq.getAndIncrement();
// 统计按8:00开始 +take_time + 20分钟路程向后累积
Optional<DispatchOrder> optional = dispatchOrderRepo.findByGroupIdAndBatchNoAndOrderId(groupId, batchNo, customer.getCode());
if (optional.isPresent()) {
DispatchOrder dOrder = optional.get();
// 时间相加操作
// LocalDateTime localExpectBegin = LocalDateTime.ofInstant(expectBegin[0].toInstant(), ZoneId.systemDefault());
// LocalDateTime localEndTime = localExpectBegin.plusMinutes(dOrder.getTakeTime());
// Date end = Date.from(localEndTime.atZone(ZoneId.systemDefault()).toInstant());
log.info("算法结果回写dispatch, step3-逐个客户处理, groupId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} ",
groupId, batchNo, technician.getCode(), customer.getCode(), customer.getServiceDuration());
log.info(customer.toString());
Date arriveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getArrivalTime());
Date leaveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getDepartureTime());
Object[] param = {technician.getCode(), idx, arriveTime, leaveTime, groupId, batchNo, customer.getCode()};
String sql = "update dispatch_order set engineer_code=? , seq=?, time_begin=? ,time_end=? where group_id=? and batch_no=? and order_id=? ";
int rowUpdated = jdbcTemplate.update(sql, param);
log.info("算法结果回写dispatch, step3-逐个客户处理, order_id:{}, engineer_code:{}, seq: {}, begin:{}, end:{} ,rowUpdated:{}",
customer.getCode(), technician.getCode(), seq, arriveTime, leaveTime, rowUpdated);
}
});
});
log.info("算法结果回写dispatch完成, groupId:{}, batchNo:{}", groupId, batchNo);
}
/**
* 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新)
* order_request(主要更新状态)
*/
// @Transactional
@Transactional(isolation = READ_COMMITTED, propagation = Propagation.REQUIRED)
@Override
public void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) throws SQLException {
public void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) {
log.info("算法结果更新到工单, groupId:{}, batchNo:{}", groupId, batchNo);
entityManager.clear();
Map<String, EngineerInfo> engineerInfoMap = engineerInfoRepo.findByGroupId(groupId).stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode, y -> y));
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findByGroupIdAndBatchNoAndEngineerCodeNot(groupId, batchNo, "");
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findAssigned(groupId, batchNo);
log.info("算法结果更新到工单, step1-开始处理, groupId:{}, batchNo:{}, order-size:{}", groupId, batchNo, dispatchOrderList.size());
......
package com.dituhui.pea.dispatch.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import com.dituhui.pea.dispatch.common.GeoDistanceCalculator;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
......@@ -12,6 +14,7 @@ import com.dituhui.pea.dispatch.pojo.*;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.util.FileUtil;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
......@@ -20,18 +23,29 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.dituhui.pea.dispatch.common.DateUtil.dateToLocalDateTime;
/**
* @author zhangx
*/
@Slf4j
@Service
public class SolveServiceImpl implements SolveService {
......@@ -49,6 +63,9 @@ public class SolveServiceImpl implements SolveService {
OrgGroupRepository groupRepository;
@Autowired
private EntityManager entityManager;
@Autowired
ExtractService extractService;
@Autowired
......@@ -57,13 +74,11 @@ public class SolveServiceImpl implements SolveService {
// 查询技术员所有技能集
private List<String> queryEngineerSkills(String engineerCode) {
List<String> result = new ArrayList<>();
String sql = "select concat( b.brand, '-', b.type, '-', b.skill) as skill from engineer_skill_group a left join skill_info b \n" +
" on a.skill_group_code= b.skill_group_code where a.engineer_code=? and a.status=1 \n" +
" and b.brand is not null ";
Object[] param = {engineerCode};
result = jdbcTemplate.queryForList(sql, param, String.class);
return result;
return jdbcTemplate.queryForList(sql, param, String.class);
}
......@@ -72,11 +87,12 @@ public class SolveServiceImpl implements SolveService {
public DispatchSolution prepareSolution(String groupId, String batchNo) {
log.info("组织问题对象, groupId:{}, batchNo:{}", groupId, batchNo);
entityManager.clear();
// 统一出发地
Depot oneDepot;
Optional<OrgGroup> optional = groupRepository.findByGroupId(groupId);
if (!optional.isPresent()) {
if (optional.isEmpty()) {
log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}", groupId, batchNo);
throw new RuntimeException(String.format("组织问题对象, 未查询到组织信息 ,groupId:%s, batchNo:%s", groupId, batchNo));
}
......@@ -92,6 +108,11 @@ public class SolveServiceImpl implements SolveService {
log.info("组织问题对象, dispatchorder-list, groupId:{}, batchNo:{}, dispatchorder-size:{}", groupId, batchNo, dispatchOrderList.size());
if (dispatchOrderList.isEmpty()) {
log.error("组织问题对象, 未查询到工单信息 ,groupId:{}, batchNo:{}", groupId, batchNo);
throw new RuntimeException(String.format("组织问题对象, 未查询到工单信息 ,groupId:%s, batchNo:%s", groupId, batchNo));
}
dispatchOrderList.forEach(order -> {
Location location = new Location(order.getId(), order.getOrderId(), "工单", Double.parseDouble(order.getX()), Double.parseDouble(order.getY()));
......@@ -162,7 +183,7 @@ public class SolveServiceImpl implements SolveService {
DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList);
distanceCalculator.initDistanceMaps(locationList);
log.info("组织问题对象, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}",
log.info("组织问题对象 done, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}",
groupId, batchNo, technicianList.size(), customerList.size(), locationList.size());
return solution;
......@@ -215,4 +236,97 @@ public class SolveServiceImpl implements SolveService {
return uid;
}
/*
* 将计算结果回写到dispatch2个表、以及order两个表
* */
@Transactional(isolation = Isolation.READ_COMMITTED)
@Override
public void saveSolutionWrp(DispatchSolution solution) throws RuntimeException {
String groupId = solution.getGroupId();
String batchNo = solution.getBatchNo();
log.info("算法结果回写包装方法, groupId:{}, batchNo:{}", groupId, batchNo);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
String fileName = String.format("dispatchSolution-%s-%s.json", groupId, batchNo);
File tempFile = new File(fileName);
exporter.write(solution, tempFile);
String dispatchResultJson = "{}";
try {
dispatchResultJson = FileUtil.readAsString(tempFile);
} catch (IOException e) {
log.error("json算法结果回写 error , groupId:{}, batchNo:{} ", groupId, batchNo, e);
}
Object[] paramBatch = {LocalDateTime.now(), dispatchResultJson, groupId, batchNo};
jdbcTemplate.update(" update dispatch_batch set status='DONE', end_time=? , ext=? where group_id=? and batch_no=? ", paramBatch);
saveSolutionToDispatch(groupId, batchNo, solution);
}
/**
* 将计算结果回写到dispatch_order表(更新补充技术员工号、上门时间)
*/
void saveSolutionToDispatch(String groupId, String batchNo, DispatchSolution solution) throws RuntimeException {
log.info("算法结果回写dispatch, groupId:{}, batchNo:{}", groupId, batchNo);
// 清理当前批次指派结果
entityManager.clear();
log.info("算法结果回写dispatch, step1-清除历史, groupId:{}, batchNo:{}", groupId, batchNo);
Object[] paramClear = {groupId, batchNo};
jdbcTemplate.update(" update dispatch_order set engineer_code='' , seq=0, time_begin=null ,time_end=null where group_id=? and batch_no=? ", paramClear);
log.info("算法结果回写dispatch, step2-开始回写, groupId:{}, batchNo:{}", groupId, batchNo);
// 保存当前批次指派结果
solution.getTechnicianList().forEach(technician -> {
log.info("算法结果回写dispatch, step2.1-按技术员逐个回写, groupId:{}, batchNo:{}, technician: {}, max-minute:{}, customlist.size:{}",
groupId, batchNo, technician.getCode(), technician.getMaxMinute(), technician.getCustomerList().size());
AtomicInteger seq = new AtomicInteger();
technician.getCustomerList().forEach(customer -> {
int idx = seq.getAndIncrement();
// 统计按8:00开始 +take_time + 20分钟路程向后累积
Optional<DispatchOrder> optional = dispatchOrderRepo.findByGroupIdAndBatchNoAndOrderId(groupId, batchNo, customer.getCode());
if (optional.isPresent()) {
DispatchOrder dOrder = optional.get();
// 时间相加操作
// LocalDateTime localExpectBegin = LocalDateTime.ofInstant(expectBegin[0].toInstant(), ZoneId.systemDefault());
// LocalDateTime localEndTime = localExpectBegin.plusMinutes(dOrder.getTakeTime());
// Date end = Date.from(localEndTime.atZone(ZoneId.systemDefault()).toInstant());
log.info("算法结果回写dispatch, step3-逐个客户处理, groupId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} ",
groupId, batchNo, technician.getCode(), customer.getCode(), customer.getServiceDuration());
log.info(customer.toString());
Date arriveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getArrivalTime());
Date leaveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getDepartureTime());
Object[] param = {technician.getCode(), idx, arriveTime, leaveTime, groupId, batchNo, customer.getCode()};
String sql = "update dispatch_order set engineer_code=? , seq=?, time_begin=? ,time_end=? where group_id=? and batch_no=? and order_id=? ";
int rowUpdated = jdbcTemplate.update(sql, param);
log.info("算法结果回写dispatch, step3-逐个客户处理, order_id:{}, engineer_code:{}, seq: {}, begin:{}, end:{} ,rowUpdated:{}",
customer.getCode(), technician.getCode(), seq, arriveTime, leaveTime, rowUpdated);
}
});
});
log.info("算法结果回写dispatch完成, groupId:{}, batchNo:{}", groupId, batchNo);
}
}
......@@ -9,6 +9,8 @@ import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig;
......@@ -36,6 +38,7 @@ class SolveServiceTest {
private SolverManager<DispatchSolution, UUID> solverManager;
private Solver<DispatchSolution> solver;
public SolveServiceTest() {
......@@ -44,6 +47,8 @@ class SolveServiceTest {
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10));
SolverFactory<DispatchSolution> solverFactory = SolverFactory.create(solverConfig);
solver = solverFactory.buildSolver();
solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
}
......@@ -67,11 +72,14 @@ class SolveServiceTest {
log.info("testAsync problemId:{}", problemId);
DispatchSolution problem = solveService.prepareSolution(groupId, batchNo);
DispatchSolution solution = solver.solve(problem);
solveService.saveSolutionWrp(solution);
solverManager.solveAndListen(problemId, id -> problem,
this.extractService::saveAndExtractSolution);
sleep(10*60*1000);
/* solverManager.solveAndListen(problemId, id -> problem,
this.solveService::saveSolutionWrp);*/
log.info("testAsync done");
}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!