Commit e54b9dbe by 张晓

技能组适配、拆分service方法增加事务支持

1 parent 03eef274
...@@ -46,10 +46,10 @@ ...@@ -46,10 +46,10 @@
<version>${mysql.version}</version> <version>${mysql.version}</version>
</dependency> </dependency>
<dependency> <!-- <dependency>-->
<groupId>com.alibaba.cloud</groupId> <!-- <groupId>com.alibaba.cloud</groupId>-->
<artifactId>spring-cloud-starter-alibaba-seata</artifactId> <!-- <artifactId>spring-cloud-starter-alibaba-seata</artifactId>-->
</dependency> <!-- </dependency>-->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
......
...@@ -4,6 +4,7 @@ package com.dituhui.pea.dispatch.controller; ...@@ -4,6 +4,7 @@ package com.dituhui.pea.dispatch.controller;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import com.dituhui.pea.common.Result; import com.dituhui.pea.common.Result;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider; 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.Customer;
import com.dituhui.pea.dispatch.pojo.DispatchSolution; import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.pojo.Technician; import com.dituhui.pea.dispatch.pojo.Technician;
...@@ -13,8 +14,7 @@ import com.dituhui.pea.dispatch.service.SolveService; ...@@ -13,8 +14,7 @@ import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore; import org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore;
import org.optaplanner.core.api.solver.SolverManager; import org.optaplanner.core.api.solver.*;
import org.optaplanner.core.api.solver.SolverStatus;
import org.optaplanner.core.config.solver.SolverConfig; import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig; import org.optaplanner.core.config.solver.SolverManagerConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -23,7 +23,6 @@ import org.springframework.web.bind.annotation.PathVariable; ...@@ -23,7 +23,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLException;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.*;
...@@ -48,6 +47,8 @@ public class PrepareController { ...@@ -48,6 +47,8 @@ public class PrepareController {
private SolverManager<DispatchSolution, UUID> solverManager; private SolverManager<DispatchSolution, UUID> solverManager;
private Solver<DispatchSolution> solver;
public PrepareController() { public PrepareController() {
SolverConfig solverConfig = new SolverConfig().withSolutionClass(DispatchSolution.class); SolverConfig solverConfig = new SolverConfig().withSolutionClass(DispatchSolution.class);
...@@ -55,6 +56,8 @@ public class PrepareController { ...@@ -55,6 +56,8 @@ public class PrepareController {
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class); solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10)); solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10));
SolverFactory<DispatchSolution> solverFactory = SolverFactory.create(solverConfig);
solver = solverFactory.buildSolver();
solverManager = SolverManager.create(solverConfig, new SolverManagerConfig()); solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
} }
...@@ -77,19 +80,36 @@ public class PrepareController { ...@@ -77,19 +80,36 @@ public class PrepareController {
} }
// 重新进行数据准备、异步任务运行 // 重新进行数据准备、异步任务运行
@GetMapping("/solveAsync/{groupId}/{day}") @GetMapping("/solveStart/{groupId}/{day}")
public Result<?> solveAsync(@PathVariable String groupId, @PathVariable String day) throws SQLException { public Result<?> solveAsync(@PathVariable String groupId, @PathVariable String day) {
log.info("调用引擎处理-异步处理, groupId:{}, day:{}", groupId, day); log.info("调用引擎处理-异步处理, groupId:{}, day:{}", groupId, day);
String batchNo = batchService.buildBatchData(groupId, day); String batchNo = null;
log.info("调用引擎处理-异步处理, groupId:{}, day:{}, batchNo:{}", groupId, day, batchNo); batchNo = batchService.buildBatchData(groupId, day);
UUID problemId = solveService.generateProblemId(groupId, batchNo);
// 提交问题开始求解 // DispatchBatch newBatch= batchService.queryBatchInfoByDay(groupId, day);
// log.info("newBatch:{}", newBatch);
log.info("调用引擎处理, groupId:{}, day:{}, batchNo:{}", groupId, day, batchNo);
DispatchSolution problem = solveService.prepareSolution(groupId, 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); log.error("调用引擎处理-异步处理, 已触发异步, groupId:{}, batchNo:{}", groupId, batchNo);
*/
return Result.success("已触发异步执行"); return Result.success("已触发异步执行");
} }
......
...@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.dao; ...@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchBatch; 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.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
......
package com.dituhui.pea.dispatch.dao; package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchOrder; 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.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
...@@ -12,7 +13,6 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L ...@@ -12,7 +13,6 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
List<DispatchOrder> findByGroupIdAndBatchNo(String groupId, String batchNo); 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!='' ") @Query("from DispatchOrder where groupId=?1 and batchNo=?2 and engineerCode is not null and engineerCode!='' ")
List<DispatchOrder> findAssigned(String groupId, String batchNo); 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 { ...@@ -83,14 +83,10 @@ public class BatchScheduler {
} }
if (null == dispatchBatch.getCutoffedTime()) { if (null == dispatchBatch.getCutoffedTime()) {
log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 执行首次confirm----- group:{}, day:{}", groupId, currDay); log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 执行首次confirm----- group:{}, day:{}", groupId, currDay);
try {
this.extractService.extractDispatchToOrder(groupId, dispatchBatch.getBatchNo(), true); this.extractService.extractDispatchToOrder(groupId, dispatchBatch.getBatchNo(), true);
dispatchBatch.setCutoffedTime(LocalDateTime.now()); dispatchBatch.setCutoffedTime(LocalDateTime.now());
batchService.saveDispatchBatch(dispatchBatch); batchService.saveDispatchBatch(dispatchBatch);
} catch (SQLException e) {
log.error("dispatchRun 已过cutoff时间,更新pre状态为confirm 异常 %s", e);
throw new RuntimeException(e);
}
} else { } else {
log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 已经执行confirm,跳过当次计算 ----- group:{}, day:{}", groupId, currDay); log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm, 已经执行confirm,跳过当次计算 ----- group:{}, day:{}", groupId, currDay);
...@@ -110,8 +106,9 @@ public class BatchScheduler { ...@@ -110,8 +106,9 @@ public class BatchScheduler {
} }
log.info("dispatchRun prepare done, group:{}, day:{}, batch:{}, problemId:{}", groupId, currDay, batchNo, problemId); log.info("dispatchRun prepare done, group:{}, day:{}, batch:{}, problemId:{}", groupId, currDay, batchNo, problemId);
DispatchSolution solution = solver.solve(problem); DispatchSolution solution = solver.solve(problem);
log.info("dispatchRun run done, group:{}, day:{}, batch:{}, problemId:{}, score:{}", groupId, currDay, batchNo, problemId, solution.getScore().toShortString()); log.info("dispatchRun solve done, group:{}, day:{}, batch:{}, problemId:{}, score:{}", groupId, currDay, batchNo, problemId, solution.getScore().toShortString());
this.extractService.saveAndExtractSolution(solution); this.solveService.saveSolutionWrp(solution);
this.extractService.extractDispatchToOrder(groupId, batchNo, false);
log.info("dispatchRun done ------ group:{}, day:{}", groupId, currDay); log.info("dispatchRun done ------ group:{}, day:{}", groupId, currDay);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class); JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
...@@ -128,9 +125,6 @@ public class BatchScheduler { ...@@ -128,9 +125,6 @@ public class BatchScheduler {
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.info("error %s", e); log.info("error %s", e);
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (SQLException e) {
log.info("error %s", e);
throw new RuntimeException(e);
} }
log.info("done"); log.info("done");
......
...@@ -16,8 +16,8 @@ public interface BatchService { ...@@ -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); DispatchBatch queryBatchInfoByDay(String groupId, String day);
......
...@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.service; ...@@ -2,6 +2,7 @@ package com.dituhui.pea.dispatch.service;
import com.dituhui.pea.dispatch.pojo.DispatchSolution; import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -15,27 +16,14 @@ import java.sql.SQLException; ...@@ -15,27 +16,14 @@ import java.sql.SQLException;
public interface ExtractService { 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 * 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新) * order_appointment(新增、更新)
* order_request(主要更新状态) * order_request(主要更新状态)
* */ * */
// @Transactional void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) ;
void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) throws SQLException;
} }
...@@ -25,4 +25,13 @@ public interface SolveService { ...@@ -25,4 +25,13 @@ public interface SolveService {
DispatchSolution prepareSolution(String groupId, String batchNo) ; DispatchSolution prepareSolution(String groupId, String batchNo) ;
/*
* 将计算结果回写到dispatch2个表
* 是下面两个方法的包装
* */
void saveSolutionWrp(DispatchSolution solution) throws RuntimeException;
} }
...@@ -9,11 +9,13 @@ import org.springframework.jdbc.core.JdbcTemplate; ...@@ -9,11 +9,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -34,9 +36,13 @@ import java.util.Optional; ...@@ -34,9 +36,13 @@ import java.util.Optional;
public class BatchServiceImpl implements BatchService { public class BatchServiceImpl implements BatchService {
@Autowired @Autowired
DispatchBatchRepository batchRepository; DispatchBatchRepository batchRepository;
@Autowired
private EntityManager entityManager;
@Autowired @Autowired
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
...@@ -54,9 +60,10 @@ public class BatchServiceImpl implements BatchService { ...@@ -54,9 +60,10 @@ public class BatchServiceImpl implements BatchService {
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建 // 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@Override @Override
public String buildBatchData(String groupId, String day) { public String buildBatchData(String groupId, String day) {
entityManager.clear();
log.info("准备批次数据, groupId:{}, day:{}", groupId, day); log.info("准备批次数据, groupId:{}, day:{}", groupId, day);
String batchNo = ""; String batchNo = "";
String batchDay = ""; String batchDay = "";
...@@ -125,8 +132,13 @@ public class BatchServiceImpl implements BatchService { ...@@ -125,8 +132,13 @@ public class BatchServiceImpl implements BatchService {
log.info("准备批次数据 orderCountPre :{}", orderCountPre); 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=?", 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); 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); log.info("准备批次数据完成, groupId:{}, day:{}, batchNo:{}", groupId, batchDay, batchNo);
...@@ -136,6 +148,7 @@ public class BatchServiceImpl implements BatchService { ...@@ -136,6 +148,7 @@ public class BatchServiceImpl implements BatchService {
@Override @Override
public String queryBatchNoByDay(String groupId, String day) { public String queryBatchNoByDay(String groupId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day); Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day);
if (optional.isPresent()) { if (optional.isPresent()) {
return optional.get().getBatchNo(); return optional.get().getBatchNo();
...@@ -146,6 +159,7 @@ public class BatchServiceImpl implements BatchService { ...@@ -146,6 +159,7 @@ public class BatchServiceImpl implements BatchService {
@Override @Override
public DispatchBatch queryBatchInfoByDay(String groupId, String day) { public DispatchBatch queryBatchInfoByDay(String groupId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day); Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day);
return optional.orElseGet(DispatchBatch::new); return optional.orElseGet(DispatchBatch::new);
} }
......
package com.dituhui.pea.dispatch.service.impl; package com.dituhui.pea.dispatch.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import com.dituhui.pea.dispatch.dao.*; import com.dituhui.pea.dispatch.dao.*;
import com.dituhui.pea.dispatch.entity.*; import com.dituhui.pea.dispatch.entity.DispatchOrder;
import com.dituhui.pea.dispatch.pojo.DispatchSolution; 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.dituhui.pea.dispatch.service.ExtractService;
import com.mysql.cj.util.StringUtils; import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j; 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.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource; import javax.persistence.EntityManager;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.util.List;
import java.util.*; import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;
/**
* @author zhangx
*/
@Slf4j @Slf4j
@Service @Service
...@@ -42,9 +38,6 @@ public class ExtractServiceImpl implements ExtractService { ...@@ -42,9 +38,6 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired @Autowired
DispatchBatchRepository batchRepository;
@Autowired
EngineerInfoRepository engineerInfoRepo; EngineerInfoRepository engineerInfoRepo;
...@@ -57,118 +50,28 @@ public class ExtractServiceImpl implements ExtractService { ...@@ -57,118 +50,28 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired @Autowired
OrderAppointmentRepository orderAppointmentRepo; OrderAppointmentRepository orderAppointmentRepo;
@Autowired
private EntityManager entityManager;
@Autowired @Autowired
private JdbcTemplate jdbcTemplate; 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 * 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新) * order_appointment(新增、更新)
* order_request(主要更新状态) * order_request(主要更新状态)
*/ */
// @Transactional @Transactional(isolation = READ_COMMITTED, propagation = Propagation.REQUIRED)
@Override @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); log.info("算法结果更新到工单, groupId:{}, batchNo:{}", groupId, batchNo);
entityManager.clear();
Map<String, EngineerInfo> engineerInfoMap = engineerInfoRepo.findByGroupId(groupId).stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode, y -> y)); 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()); log.info("算法结果更新到工单, step1-开始处理, groupId:{}, batchNo:{}, order-size:{}", groupId, batchNo, dispatchOrderList.size());
......
package com.dituhui.pea.dispatch.service.impl; 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 cn.hutool.crypto.SecureUtil;
import com.dituhui.pea.dispatch.common.GeoDistanceCalculator; import com.dituhui.pea.dispatch.common.GeoDistanceCalculator;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider; import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
import com.dituhui.pea.dispatch.dao.DispatchEngineerRepository; import com.dituhui.pea.dispatch.dao.DispatchEngineerRepository;
import com.dituhui.pea.dispatch.dao.DispatchOrderRepository; import com.dituhui.pea.dispatch.dao.DispatchOrderRepository;
import com.dituhui.pea.dispatch.dao.OrgGroupRepository; import com.dituhui.pea.dispatch.dao.OrgGroupRepository;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import com.dituhui.pea.dispatch.entity.OrgGroup; import com.dituhui.pea.dispatch.entity.OrgGroup;
import com.dituhui.pea.dispatch.pojo.*; import com.dituhui.pea.dispatch.pojo.*;
import com.dituhui.pea.dispatch.service.ExtractService; import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService; import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.util.FileUtil;
import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory; import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig; import org.optaplanner.core.config.solver.SolverConfig;
...@@ -19,18 +23,29 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -19,18 +23,29 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; 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.File;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.dituhui.pea.dispatch.common.DateUtil.dateToLocalDateTime; import static com.dituhui.pea.dispatch.common.DateUtil.dateToLocalDateTime;
/**
* @author zhangx
*/
@Slf4j @Slf4j
@Service @Service
public class SolveServiceImpl implements SolveService { public class SolveServiceImpl implements SolveService {
...@@ -48,6 +63,9 @@ public class SolveServiceImpl implements SolveService { ...@@ -48,6 +63,9 @@ public class SolveServiceImpl implements SolveService {
OrgGroupRepository groupRepository; OrgGroupRepository groupRepository;
@Autowired @Autowired
private EntityManager entityManager;
@Autowired
ExtractService extractService; ExtractService extractService;
@Autowired @Autowired
...@@ -56,13 +74,11 @@ public class SolveServiceImpl implements SolveService { ...@@ -56,13 +74,11 @@ public class SolveServiceImpl implements SolveService {
// 查询技术员所有技能集 // 查询技术员所有技能集
private List<String> queryEngineerSkills(String engineerCode) { 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" + 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" + " on a.skill_group_code= b.skill_group_code where a.engineer_code=? and a.status=1 \n" +
" and b.brand is not null "; " and b.brand is not null ";
Object[] param = {engineerCode}; Object[] param = {engineerCode};
result = jdbcTemplate.queryForList(sql, param, String.class); return jdbcTemplate.queryForList(sql, param, String.class);
return result;
} }
...@@ -71,11 +87,12 @@ public class SolveServiceImpl implements SolveService { ...@@ -71,11 +87,12 @@ public class SolveServiceImpl implements SolveService {
public DispatchSolution prepareSolution(String groupId, String batchNo) { public DispatchSolution prepareSolution(String groupId, String batchNo) {
log.info("组织问题对象, groupId:{}, batchNo:{}", groupId, batchNo); log.info("组织问题对象, groupId:{}, batchNo:{}", groupId, batchNo);
entityManager.clear();
// 统一出发地 // 统一出发地
Depot oneDepot; Depot oneDepot;
Optional<OrgGroup> optional = groupRepository.findByGroupId(groupId); Optional<OrgGroup> optional = groupRepository.findByGroupId(groupId);
if (!optional.isPresent()) { if (optional.isEmpty()) {
log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}", groupId, batchNo); log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}", groupId, batchNo);
throw new RuntimeException(String.format("组织问题对象, 未查询到组织信息 ,groupId:%s, batchNo:%s", groupId, batchNo)); throw new RuntimeException(String.format("组织问题对象, 未查询到组织信息 ,groupId:%s, batchNo:%s", groupId, batchNo));
} }
...@@ -87,7 +104,16 @@ public class SolveServiceImpl implements SolveService { ...@@ -87,7 +104,16 @@ public class SolveServiceImpl implements SolveService {
// customerlist // customerlist
ArrayList<Customer> customerList = new ArrayList<>(); ArrayList<Customer> customerList = new ArrayList<>();
dispatchOrderRepo.findByGroupIdAndBatchNo(groupId, batchNo).forEach(order -> { List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findByGroupIdAndBatchNo(groupId, batchNo);
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())); Location location = new Location(order.getId(), order.getOrderId(), "工单", Double.parseDouble(order.getX()), Double.parseDouble(order.getY()));
LocalDateTime ldt1 = dateToLocalDateTime(order.getExpectTimeBegin()); LocalDateTime ldt1 = dateToLocalDateTime(order.getExpectTimeBegin());
...@@ -101,6 +127,7 @@ public class SolveServiceImpl implements SolveService { ...@@ -101,6 +127,7 @@ public class SolveServiceImpl implements SolveService {
end = ldt2.getMinute(); end = ldt2.getMinute();
} }
// 40分钟兜低(技能未能正确匹配原因) // 40分钟兜低(技能未能正确匹配原因)
if (null == order.getTakeTime()) { if (null == order.getTakeTime()) {
order.setTakeTime(40); order.setTakeTime(40);
...@@ -156,7 +183,7 @@ public class SolveServiceImpl implements SolveService { ...@@ -156,7 +183,7 @@ public class SolveServiceImpl implements SolveService {
DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList); DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList);
distanceCalculator.initDistanceMaps(locationList); 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()); groupId, batchNo, technicianList.size(), customerList.size(), locationList.size());
return solution; return solution;
...@@ -209,4 +236,97 @@ public class SolveServiceImpl implements SolveService { ...@@ -209,4 +236,97 @@ public class SolveServiceImpl implements SolveService {
return uid; 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; ...@@ -9,6 +9,8 @@ import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService; import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; 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.api.solver.SolverManager;
import org.optaplanner.core.config.solver.SolverConfig; import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig; import org.optaplanner.core.config.solver.SolverManagerConfig;
...@@ -36,6 +38,7 @@ class SolveServiceTest { ...@@ -36,6 +38,7 @@ class SolveServiceTest {
private SolverManager<DispatchSolution, UUID> solverManager; private SolverManager<DispatchSolution, UUID> solverManager;
private Solver<DispatchSolution> solver;
public SolveServiceTest() { public SolveServiceTest() {
...@@ -44,6 +47,8 @@ class SolveServiceTest { ...@@ -44,6 +47,8 @@ class SolveServiceTest {
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class); solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10)); solverConfig.withTerminationSpentLimit(Duration.ofSeconds(10));
SolverFactory<DispatchSolution> solverFactory = SolverFactory.create(solverConfig);
solver = solverFactory.buildSolver();
solverManager = SolverManager.create(solverConfig, new SolverManagerConfig()); solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
} }
...@@ -67,11 +72,14 @@ class SolveServiceTest { ...@@ -67,11 +72,14 @@ class SolveServiceTest {
log.info("testAsync problemId:{}", problemId); log.info("testAsync problemId:{}", problemId);
DispatchSolution problem = solveService.prepareSolution(groupId, batchNo); 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"); 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!