Commit c427a1bc by 刘鑫

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

2 parents 25d10fa9 5b510ce2
Showing with 717 additions and 9 deletions
......@@ -16,9 +16,11 @@ public interface DispatchBatchRepository extends CrudRepository<DispatchBatch, L
List<DispatchBatch> findByGroupId(String groupId);
Optional<DispatchBatch> findByGroupIdAndBatchDate(String groupId, String batchDay);
@Query(value = "from DispatchBatch where groupId = ?1 and batchNo=?2 ")
Optional<DispatchBatch> findByGroupIdAndBatchNo(String groupId, String batchNo);
Optional<DispatchBatch> findByTeamIdAndBatchDate(String teamId, String batchDay);
}
\ No newline at end of file
......@@ -10,4 +10,6 @@ public interface DispatchEngineerRepository extends CrudRepository<DispatchEngin
List<DispatchEngineer> findByGroupId(String groupId);
List<DispatchEngineer> findByGroupIdAndBatchNo(String groupId, String batchNo);
List<DispatchEngineer> findByTeamIdAndBatchNo(String teamId, String batchNo);
}
\ No newline at end of file
......@@ -17,10 +17,18 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
// 查看未指派非confirm的,供算法计算
@Query("from DispatchOrder where groupId=?1 and batchNo=?2")
List<DispatchOrder> findAll(String groupId, String batchNo);
// 查看未指派非confirm的,供算法计算
@Query("from DispatchOrder where teamId=?1 and batchNo=?2")
List<DispatchOrder> findAll2(String teamId, String batchNo);
// 查看算法指派成功(也有抹掉技术员、时间情况),非confirm状态的
@Query("from DispatchOrder where groupId=?1 and batchNo=?2 and status !='CONFIRM' ")
List<DispatchOrder> findAllWithoutConfirm(String groupId, String batchNo);
// 查看算法指派成功(也有抹掉技术员、时间情况),非confirm状态的
@Query("from DispatchOrder where teamId=?1 and batchNo=?2 and status !='CONFIRM' ")
List<DispatchOrder> findAllWithoutConfirm2(String teamId, String batchNo);
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderIdAndDt(String groupId, String batchNo, String orderId, String dt);
}
\ No newline at end of file
......@@ -11,4 +11,7 @@ public interface EngineerInfoRepository extends CrudRepository<EngineerInfo, Lon
List<EngineerInfo> findByGroupId(String groupId);
Optional<EngineerInfo> findByEngineerCode(String engineerCode);
List<EngineerInfo> findByEngineerCodeIn(List<String> engineerCodes);
}
\ No newline at end of file
package com.dituhui.pea.dispatch.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.dituhui.pea.dispatch.entity.OldSkillMapper;
@Repository
public interface OldSkillMapperDao
extends JpaRepository<OldSkillMapper, Integer>, JpaSpecificationExecutor<OldSkillMapper> {
OldSkillMapper findByBrandAndTypeAndSkill(String brand, String type, String skill);
}
package com.dituhui.pea.dispatch.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import com.dituhui.pea.dispatch.entity.OrgTeamEntity;
@Repository
public interface OrgTeamDao extends JpaRepository<OrgTeamEntity, Integer>, JpaSpecificationExecutor<OrgTeamEntity> {
OrgTeamEntity findByTeamNameAndGroupId(String teamName, String groupId);
List<OrgTeamEntity> findByGroupId(String groupId);
OrgTeamEntity findByTeamId(String teamId);
}
package com.dituhui.pea.dispatch.dao;
import org.hibernate.annotations.Where;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.dituhui.pea.dispatch.entity.OrgTeamEngineerEntity;
import java.util.List;
@Repository
public interface OrgTeamEngineerDao extends JpaRepository<OrgTeamEngineerEntity, Integer> {
@Query("select t from OrgTeamEngineerEntity t where t.teamId = :teamId and t.status=1")
List<OrgTeamEngineerEntity> findAllByTeamId(String teamId);
@Query("select t from OrgTeamEngineerEntity t where t.teamId = :teamId and t.engineerCode=:engineerCode")
OrgTeamEngineerEntity findByTeamIdAndEngineerCode(String teamId, String engineerCode);
@Query("select t from OrgTeamEngineerEntity t where t.teamId in :teamIds and t.status=1")
List<OrgTeamEngineerEntity> findAllByTeamIdIn(List<String> teamIds);
@Query("select a.teamId from OrgTeamEngineerEntity a where a.engineerCode=:engineerCode")
List<String> getTeamIdsByEngineerCode(String engineerCode);
@Modifying
@Query("UPDATE OrgTeamEngineerEntity tt SET tt.status = :status WHERE tt.teamId = :teamId")
void updateStatusAllEngineers(String teamId, int status);
@Modifying
@Query("UPDATE OrgTeamEngineerEntity tt SET tt.status = :status WHERE tt.teamId = :teamId AND tt.engineerCode IN :engineerCodes")
void updateStatusByEngineerCodes(String teamId, List<String> engineerCodes, int status);
}
......@@ -29,6 +29,9 @@ public class DispatchBatch implements Serializable {
@Column(name = "group_id")
private String groupId;
@Column(name = "team_id")
private String teamId;
/**
* 批次号
......
......@@ -24,6 +24,9 @@ public class DispatchEngineer implements Serializable {
@Column(name = "group_id")
private String groupId;
@Column(name = "team_id")
private String teamId;
@Column(name = "batch_no")
private String batchNo;
......
......@@ -26,6 +26,7 @@ public class DispatchOrder implements Serializable {
@Column(name = "group_id")
private String groupId;
@Column(name = "batch_no")
private String batchNo;
......
package com.dituhui.pea.dispatch.entity;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
import lombok.experimental.Accessors;
@Entity
@Table(name = "old_skill_mapper")
@Data
@Accessors(chain = true)
public class OldSkillMapper {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "brand", nullable = false)
private String brand;
@Column(name = "type", nullable = false)
private String type;
@Column(name = "skill", nullable = false)
private String skill;
@Column(name = "brand2", nullable = false)
private String brand2;
@Column(name = "type2", nullable = false)
private String type2;
@Column(name = "skill2", nullable = false)
private String skill2;
@Column(name = "memo", nullable = false)
private String memo;
@Column(name = "create_time", nullable = false, updatable = false, columnDefinition = "timestamp default current_timestamp")
private LocalDateTime createTime;
@Column(name = "update_time", nullable = false, columnDefinition = "timestamp default current_timestamp on update current_timestamp")
private LocalDateTime updateTime;
}
package com.dituhui.pea.dispatch.entity;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
@Entity
@Data
@Accessors(chain = true)
@Table(name = "org_team_engineer")
public class OrgTeamEngineerEntity {
private static final long serialVersionUID = 1L;
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 工作队id
*/
private String teamId;
/**
* 工程师工号
*/
private String engineerCode;
/**
* 状态(0无效 1有效)
*/
private Integer status;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private LocalDateTime createTime = LocalDateTime.now();
/**
* 更新时间
*/
private LocalDateTime updateTime = LocalDateTime.now();
public OrgTeamEngineerEntity() {
}
}
package com.dituhui.pea.dispatch.entity;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "org_team")
@Data
@Accessors(chain = true)
public class OrgTeamEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "team_id", nullable = false, unique = true)
private String teamId;
@Column(name = "team_name", nullable = false)
private String teamName;
@Column(name = "team_type", columnDefinition = "int default 1")
private Integer teamType = 1;
@Column(name = "cluster_id", nullable = false)
private String clusterId;
@Column(name = "branch_id", nullable = false)
private String branchId;
@Column(name = "group_id", nullable = false)
private String groupId;
@Column(name = "warehouse_id", nullable = false)
private String warehouseId;
@Column(name = "workdays", nullable = false, columnDefinition = "varchar(30) default '1,2,3,4,5,6,7'")
private String workdays = "1,2,3,4,5,6,7";
@Column(nullable = false)
private String memo;
@Column(name = "create_time", nullable = false, updatable = false, columnDefinition = "timestamp default current_timestamp")
private LocalDateTime createTime;
@Column(name = "update_time", nullable = false, columnDefinition = "timestamp default current_timestamp on update current_timestamp")
private LocalDateTime updateTime;
@Column(name = "work_on", nullable = false, columnDefinition = "varchar(5) default '08:00'")
private String workOn = "08:00";
@Column(name = "work_off", nullable = false, columnDefinition = "varchar(5) default '18:00'")
private String workOff = "18:00";
@Column(name = "work_duration", nullable = false, columnDefinition = "int default 600")
private Integer workDuration = 600;
@Column(name = "transport_mode", columnDefinition = "varchar(10) default '电动车'")
private String transportMode = "电动车";
@Column(name = "strategy_schedule", columnDefinition = "varchar(10) default '营销优先'")
private String strategySchedule = "营销优先";
@Column(name = "strategy_appointment", columnDefinition = "varchar(10) default '指派给信息员'")
private String strategyAppointment = "指派给信息员";
@Column(name = "interval_schedule", nullable = false, columnDefinition = "int default 100")
private Integer intervalSchedule = 100;
@Column(name = "interval_schedule_next", nullable = false, columnDefinition = "int default 200")
private Integer intervalScheduleNext = 200;
@Column(name = "cute_off", nullable = false, columnDefinition = "varchar(5) default '18:00'")
private String cuteOff = "18:00";
@Column(nullable = false, columnDefinition = "int default 1")
private Integer status = 1;
// 其他字段和关联关系的定义
// ...
}
......@@ -18,6 +18,8 @@ import lombok.Data;
public class DispatchSolution {
private String groupId;
private String teamId;
private String batchNo;
......@@ -65,6 +67,16 @@ public class DispatchSolution {
this.technicianList = technicianList;
this.customerList = customerList;
}
public DispatchSolution(String groupId, String teamId, String batchNo, List<Location> locationList, Depot depot, List<Technician> technicianList, List<Customer> customerList) {
this.groupId = groupId;
this.teamId = teamId;
this.batchNo = batchNo;
this.locationList = locationList;
this.depot = depot;
this.technicianList = technicianList;
this.customerList = customerList;
}
// ************************************************************************
// Complex methods
......
......@@ -3,6 +3,8 @@ package com.dituhui.pea.dispatch.scheduler;
import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.optaplanner.core.api.solver.Solver;
......@@ -13,6 +15,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.dituhui.pea.dispatch.dao.OrgTeamDao;
import com.dituhui.pea.dispatch.entity.OrgTeamEntity;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.service.BatchService;
import com.dituhui.pea.dispatch.service.ExtractService;
......@@ -25,10 +29,6 @@ import lombok.extern.slf4j.Slf4j;
@Component
public class BatchScheduler {
String groupId = "gsuzhou";
@Value("${dispatch.cron.next-day-limit}")
int nextDaysLimit = 2;
......@@ -40,6 +40,9 @@ public class BatchScheduler {
@Autowired
ExtractService extractService;
@Autowired
OrgTeamDao orgTeamDao;
private DefaultSolverFactory<DispatchSolution> solverFactory;
private Solver<DispatchSolution> solver;
......@@ -49,11 +52,55 @@ public class BatchScheduler {
solver = solverFactory.buildSolver();
}
/*
* 异步执行任务开始
*/
@Scheduled(cron = "${dispatch.cron.expr}")
public void dispatchRun2() {
String groupId = "gsuzhou";
log.info("dispatchRun group:{}", groupId);
// TODO 分布式任务,循环大区/分部/分站/小队
// 循环分站/网点下面的小队
List<OrgTeamEntity> teams = orgTeamDao.findByGroupId(groupId);
for (OrgTeamEntity team : teams) {
String teamId = team.getTeamId();
for (int i = 1; i <= nextDaysLimit; i++) {
String currDay = LocalDate.now().plusDays(i).format(DateTimeFormatter.ISO_LOCAL_DATE);
log.info("dispatchRun begin----- teamId:{}, day:{}", teamId, currDay);
String batchNo = batchService.buildBatchData2(teamId, currDay);
UUID problemId = solveService.generateProblemId(teamId, batchNo);
log.info("dispatchRun teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
DispatchSolution problem = solveService.prepareSolution2(teamId, batchNo);
if (problem.getCustomerList().size() <= 0) {
log.info("dispatchRun 当前批次没有待指派工单 , teamId:{}, day:{}, batch:{}, problemId:{}, order-size:{}", teamId, currDay, batchNo, problemId, problem.getCustomerList().size());
continue;
}
log.info("dispatchRun prepare done, teamId:{}, day:{}, batch:{}, problemId:{}", teamId, currDay, batchNo, problemId);
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.saveSolutionWrp(solution);
this.extractService.extractDispatchToOrder2(teamId, batchNo, false);
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",groupId, currDay)));
}
}
log.info("dispatchRun group:{} done", groupId);
}
/*
* 异步执行任务开始
* */
@Scheduled(cron = "${dispatch.cron.expr}")
//@Scheduled(cron = "${dispatch.cron.expr}")
public void dispatchRun() {
String groupId = "gsuzhou";
log.info("dispatchRun group:{}", groupId);
try {
for (int i = 0; i <= nextDaysLimit; i++) {
......
......@@ -15,8 +15,6 @@ import java.sql.SQLException;
public interface BatchService {
// 检查指定日期的小组是否有在运行的批次任务,有则返回,没有则创建后返回批次码
String buildBatchData(String groupId, String day);
DispatchBatch queryBatchInfoByDay(String groupId, String day);
......@@ -24,5 +22,12 @@ public interface BatchService {
String queryBatchNoByDay(String groupId, String day);
void saveDispatchBatch(DispatchBatch batchInfo);
// 小队id
public String buildBatchData2(String teamId, String day);
public DispatchBatch queryBatchInfoByDay2(String teamId, String day);
public String queryBatchNoByDay2(String teamId, String day);
}
......@@ -22,6 +22,11 @@ public interface ExtractService {
* 将dispath_order 中的计算结果,回写到 order_info
* */
void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) ;
/*
* 按小队将dispath_order 中的计算结果,回写到 order_info
* */
void extractDispatchToOrder2(String TeamId, String batchNo, boolean isConfirm) ;
}
......@@ -23,6 +23,9 @@ public interface SolveService {
UUID generateProblemId(String groupId, String batchNo);
DispatchSolution prepareSolution(String groupId, String batchNo) ;
DispatchSolution prepareSolution2(String teamId, String batchNo) ;
/*
......
......@@ -179,6 +179,85 @@ public class BatchServiceImpl implements BatchService {
return batchNo;
}
// 检查给定小队、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional(isolation = Isolation.READ_COMMITTED)
@Override
public String buildBatchData2(String teamId, String day) {
entityManager.clear();
log.info("准备批次数据, teamId:{}, day:{}", teamId, day);
String batchNo = "";
String batchDay = "";
Optional<DispatchBatch> optional = batchRepository.findByTeamIdAndBatchDate(teamId, day);
if (!optional.isPresent()) {
// 创建batch
batchNo = calcBatchNo(day);
batchDay = day;
// 执行数据库操作
String sqlInsert = "INSERT INTO `dispatch_batch` ( `team_id`, `batch_no`, `batch_date`, `engineer_num`, `order_num`, `start_time`, `end_time`, `status`) " +
" VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sqlInsert, teamId, batchNo, batchDay, 0, 0, LocalDateTime.now(), null, "RUNNING");
log.info("生成新批次, teamId:{}, day:{}", teamId, batchDay);
} else {
batchNo = optional.get().getBatchNo();
batchDay = optional.get().getBatchDate();
}
log.info("清理原批次数据, teamId:{}, day:{}, batchNo:{}", teamId, batchDay, batchNo);
jdbcTemplate.update("delete from dispatch_engineer where team_id=? and batch_no=?", teamId, batchNo);
jdbcTemplate.update("delete from dispatch_order where team_id=? and batch_no=?", teamId, batchNo);
log.info("写入新批次技术员、工单数据, teamId:{}, day:{}, batchNo:{}", teamId, batchDay, batchNo);
String sqlEngineer = "INSERT INTO dispatch_engineer (team_id, batch_no, engineer_code, engineer_name, x, y, max_num, max_minute, max_distance)\n"
+ "SELECT o.team_id,?,o.engineer_code, a.name , b.x, b.y , max_num, max_minute, max_distance FROM `org_team_engineer` o,engineer_info a,engineer_business b \r\n"
+ " WHERE o.team_id=? AND `status`=1\r\n"
+ " AND o.engineer_code=a.engineer_code AND a.engineer_code = b.engineer_code \r\n"
+ " AND b.x IS NOT NULL AND b.x !=''" + " order by a.engineer_code asc";
int engCount = jdbcTemplate.update(sqlEngineer, batchNo, teamId);
// 未派过的工单(已派过PRE状态还可以再次派)
String sqlOrder = "INSERT INTO dispatch_order (group_id, batch_no, team_id, order_id , dt, x, y, \n"
+ " expect_time_begin, expect_time_end, tags, priority , skills , take_time, status )\n"
+ " SELECT a.org_group_id, ?, a.org_team_id , a.order_id, ?, a.x, a.y , \r\n"
+ " a.expect_time_begin, a.expect_time_end, a.tags, a.priority , \r\n"
+ " CONCAT(a.brand, '-', a.type, '-', a.skill) skills , a.take_time , a.appointment_status\r\n"
+ " FROM order_info a \r\n" + " WHERE a.org_team_id=? AND a.dt = ? AND bean_status='OPEN'\r\n"
+ " AND appointment_method LIKE 'AUTO%' AND a.appointment_status IN ('INIT', 'PRE')\r\n"
+ " AND order_status ='NORMAL' AND service_status='INIT'\r\n"
+ " ORDER BY a.expect_time_begin ASC \r\n";
int orderCount = jdbcTemplate.update(sqlOrder, batchNo, batchDay, teamId, batchDay);
// confirm的要做预占用,所以也加入进来
String sqlOrderConfirm = "INSERT INTO dispatch_order (group_id, batch_no, team_id, order_id , dt, x, y, \n" +
" expect_time_begin, expect_time_end, tags, priority , skills , take_time, status, engineer_code, time_begin, time_end )\n" +
" select a.org_group_id, ?, a.org_team_id , a.order_id, a.dt, a.x, a.y , \n" +
" a.expect_time_begin, a.expect_time_end, a.tags, a.priority , \n" +
" concat(a.brand, '-', a.type, '-', a.skill) skills , a.take_time, a.appointment_status, \n" +
" a.engineer_code, a.plan_start_time, a.plan_end_time \n" +
" from order_info a \n" +
" where a.org_team_id=? and a.dt = ? and bean_status='OPEN'\n" +
" and appointment_method like 'AUTO%' and a.appointment_status in ('CONFIRM')\n" +
" and order_status ='NORMAL' and service_status='INIT'\n" +
" order by a.expect_time_begin asc ";
int orderConfirmCount = jdbcTemplate.update(sqlOrderConfirm, batchNo, teamId, batchDay);
log.info("准备批次数据 engCount:{}, orderCount:{}, orderConfirmCount:{}", engCount, orderCount, orderConfirmCount);
if (orderCount + orderConfirmCount > 0) {
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=?, start_time=?, end_time=null, status='RUNNING' where team_id=? and batch_no=?",
engCount, orderCount + orderConfirmCount, LocalDateTime.now(), teamId, batchNo);
} else {
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=?, start_time=?, end_time=?, status='DONE' where team_id=? and batch_no=?",
engCount, 0, LocalDateTime.now(), LocalDateTime.now(), teamId, batchNo);
}
log.info("准备批次数据完成, teamId:{}, day:{}, batchNo:{}", teamId, batchDay, batchNo);
return batchNo;
}
@Override
......@@ -191,6 +270,17 @@ public class BatchServiceImpl implements BatchService {
return "";
}
}
@Override
public String queryBatchNoByDay2(String teamId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByTeamIdAndBatchDate(teamId, day);
if (optional.isPresent()) {
return optional.get().getBatchNo();
} else {
return "";
}
}
@Override
public DispatchBatch queryBatchInfoByDay(String groupId, String day) {
......@@ -198,6 +288,13 @@ public class BatchServiceImpl implements BatchService {
Optional<DispatchBatch> optional = batchRepository.findByGroupIdAndBatchDate(groupId, day);
return optional.orElseGet(DispatchBatch::new);
}
@Override
public DispatchBatch queryBatchInfoByDay2(String teamId, String day) {
entityManager.clear();
Optional<DispatchBatch> optional = batchRepository.findByTeamIdAndBatchDate(teamId, day);
return optional.orElseGet(DispatchBatch::new);
}
@Override
public void saveDispatchBatch(DispatchBatch batchInfo) {
......
......@@ -2,9 +2,13 @@ package com.dituhui.pea.dispatch.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.dituhui.pea.dispatch.dao.*;
import com.dituhui.pea.dispatch.entity.*;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.google.common.collect.Maps;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
......@@ -46,7 +50,9 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired
OrderInfoRepository orderInfoRepo;
@Autowired
OrgTeamEngineerDao orgTeamEngineerDao;
@Autowired
OrderLogRepository orderLogRepo;
......@@ -174,6 +180,133 @@ public class ExtractServiceImpl implements ExtractService {
log.info("算法结果更新到工单完成, groupId:{}, batchNo:{}", groupId, batchNo);
}
/**
* 将dispath_order 中的计算结果,回写到 order_info
* order_info(主要更新状态)
*/
@Transactional(isolation = READ_COMMITTED, propagation = Propagation.REQUIRED)
@Override
public void extractDispatchToOrder2(String teamId, String batchNo, boolean isConfirm) {
log.info("算法结果更新到工单, teamId:{}, batchNo:{}", teamId, batchNo);
entityManager.clear();
// 工程师数据准备
// Map<String, EngineerInfo> engineerInfoMap =
// engineerInfoRepo.findByGroupId(groupId).stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode,
// y -> y));
final Map<String, EngineerInfo> engineerInfoMap = Maps.newHashMap();
List<OrgTeamEngineerEntity> engineers = orgTeamEngineerDao.findAllByTeamId(teamId);
if (CollectionUtils.isNotEmpty(engineers)) {
List<String> codes = engineers.stream().map(o -> o.getEngineerCode()).collect(Collectors.toList());
List<EngineerInfo> engineers2 = engineerInfoRepo.findByEngineerCodeIn(codes);
if (CollectionUtils.isNotEmpty(engineers2)) {
engineerInfoMap
.putAll(engineers2.stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode, y -> y)));
}
}
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findAllWithoutConfirm2(teamId, batchNo);
log.info("算法结果更新到工单, step1-开始处理, teamId:{}, batchNo:{}, order-size:{}", teamId, batchNo, dispatchOrderList.size());
AtomicInteger atomicInteger = new AtomicInteger();
dispatchOrderList.forEach(dispatchOrder -> {
int idx = atomicInteger.getAndIncrement();
String orderId = dispatchOrder.getOrderId();
String dt = dispatchOrder.getDt();
String engCode = dispatchOrder.getEngineerCode();
log.info("算法结果更新到工单, step1.1-loop, {}/{}, teamId:{}, batchNo:{}, orderId:{}, dt:{}, engCode:{}",
idx, dispatchOrderList.size(), teamId, batchNo, orderId, dt, engCode);
LocalDate localDt = LocalDate.parse(dt, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Optional<OrderInfo> orderOpt = orderInfoRepo.findOrderInfoByOrderIdAndDt(orderId, localDt);
if (orderOpt.isEmpty()) {
log.warn("算法结果更新到工单, step1.1-loop, 工单不存在, teamId:{}, batchNo:{}, orderId:{}",
teamId, batchNo, orderId);
return;
}
OrderInfo orderInfo = orderOpt.get();
if (!("OPEN".equals(orderInfo.getBeanStatus()) &&
Set.of("AUTO_NOW", "AUTO_BATCH").contains(orderInfo.getAppointmentMethod()) &&
Set.of("INIT", "PRE").contains(orderInfo.getAppointmentStatus()) &&
"NORMAL".equals(orderInfo.getOrderStatus()) && "INIT".equals(orderInfo.getServiceStatus()))) {
log.warn("算法结果更新到工单, step1.1-loop, 工单状态异常, teamId:{}, batchNo:{}, orderId:{}, bean-status:{}, appointment-status:{}, order-status:{}, service-status:{}",
teamId, batchNo, orderId, orderInfo.getBeanStatus(), orderInfo.getAppointmentStatus(), orderInfo.getOrderStatus(), orderInfo.getServiceStatus());
return;
}
// 未分配技术员时,清理原分配的信息
if (!StrUtil.isEmpty(engCode)) {
EngineerInfo engineerInfo = engineerInfoMap.get(engCode);
if (engineerInfo == null) {
log.warn("算法结果更新到工单, step1.1-loop, 未找到技术员, teamId:{}, batchNo:{}, engCode:{}",
teamId, batchNo, engCode);
return;
}
String engName = engineerInfo.getName();
String phone = engineerInfo.getPhone();
/*int age = 0;
if (!StringUtils.isNullOrEmpty(engineerInfo.getBirth())) {
DateTime birthDate = DateUtil.parse(engineerInfo.getBirth(), "yyyy-MM-dd");
age = DateUtil.age(birthDate.toJdkDate(), DateUtil.date());
}*/
orderInfo.setEngineerCode(engCode);
orderInfo.setEngineerName(engName);
orderInfo.setEngineerPhone(phone);
orderInfo.setPlanStartTime(dispatchOrder.getTimeBegin());
orderInfo.setPlanEndTime(dispatchOrder.getTimeEnd());
orderInfo.setArriveElapsed(dispatchOrder.getPathTime());
orderInfo.setArriveDistance(dispatchOrder.getPathDistance());
orderInfo.setAppointmentStatus(isConfirm ? "CONFIRM" : "PRE");
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
if (isConfirm) {
OrderEvent orderEvent = new OrderEvent().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setHappen(LocalDateTime.now())
.setEvent("批量自动指派").setOperator("DISPATCH").setOperatorName("算法批量指派").setSource("PEA-DISPATCH")
.setDescription(String.format("批量自动指派:<%s,%s>", engCode, engName)).setMemo("")
.setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderEventRepo.save(orderEvent);
}
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", engCode, engName)).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
} else {
log.warn("算法结果更新到工单, step1.2-loop, 未能分配到技术员, teamId:{}, batchNo:{}, orderId:{}, dt:{}",
teamId, batchNo, orderId, dt);
orderInfo.setEngineerCode("");
orderInfo.setEngineerName("");
orderInfo.setEngineerPhone("");
orderInfo.setPlanStartTime(null);
orderInfo.setPlanEndTime(null);
orderInfo.setArriveElapsed(0);
orderInfo.setArriveDistance(0);
orderInfo.setAppointmentStatus("INIT");
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", "", "抹掉技术员")).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
}
});
log.info("算法结果更新到工单完成, teamId:{}, batchNo:{}", teamId, batchNo);
}
}
......@@ -6,8 +6,10 @@ import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
import com.dituhui.pea.dispatch.dao.DispatchEngineerRepository;
import com.dituhui.pea.dispatch.dao.DispatchOrderRepository;
import com.dituhui.pea.dispatch.dao.OrgGroupRepository;
import com.dituhui.pea.dispatch.dao.OrgTeamDao;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import com.dituhui.pea.dispatch.entity.OrgGroup;
import com.dituhui.pea.dispatch.entity.OrgTeamEntity;
import com.dituhui.pea.dispatch.pojo.*;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService;
......@@ -68,7 +70,9 @@ public class SolveServiceImpl implements SolveService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
OrgTeamDao orgTeamDao;
// 查询技术员所有技能集
private List<String> queryEngineerSkills(String engineerCode) {
......@@ -184,6 +188,117 @@ public class SolveServiceImpl implements SolveService {
return solution;
}
// 按小队,批号组装问题对象
@Override
public DispatchSolution prepareSolution2(String teamId, String batchNo) {
log.info("组织问题对象, teamId:{}, batchNo:{}", teamId, batchNo);
entityManager.clear();
OrgTeamEntity team = orgTeamDao.findByTeamId(teamId);
// 统一出发地
Depot oneDepot;
Optional<OrgGroup> optional = groupRepository.findByGroupId(team.getGroupId());
if (optional.isEmpty()) {
log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}", team.getGroupId(), batchNo);
throw new RuntimeException(String.format("组织问题对象, 未查询到组织信息 ,groupId:%s, batchNo:%s", team.getGroupId(), batchNo));
}
OrgGroup oneGroup = optional.get();
Location deptLocation = new Location(oneGroup.getId(), oneGroup.getGroupId(), "起点", Double.parseDouble(oneGroup.getX()), Double.parseDouble(oneGroup.getY()));
oneDepot = new Depot(oneGroup.getId(), oneGroup.getGroupId(), deptLocation, 60 * 8, 60 * 18);
// customerlist
ArrayList<Customer> customerList = new ArrayList<>();
// 已分配和未分配一起排班,因为已分配会影响整体排班结果
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findAll2(teamId, batchNo);
// List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findNotAssigned(groupId, batchNo);
log.info("组织问题对象, dispatchorder-list, teamId:{}, batchNo:{}, dispatchorder-size:{}", teamId, batchNo, dispatchOrderList.size());
if (dispatchOrderList.isEmpty()) {
log.error("组织问题对象, 未查询到工单信息 ,teamId:{}, batchNo:{}", teamId, batchNo);
}
dispatchOrderList.forEach(order -> {
Location location = new Location(order.getId(), order.getOrderId(), "工单", Double.parseDouble(order.getX()), Double.parseDouble(order.getY()));
LocalDateTime ldt1 = dateToLocalDateTime(order.getExpectTimeBegin());
LocalDateTime ldt2 = dateToLocalDateTime(order.getExpectTimeEnd());
int start = 60 * 8;
int end = 60 * 18;
if (ldt1 != null) {
start = ldt1.getHour() * 60 + ldt1.getMinute();
}
if (ldt2 != null) {
end = ldt2.getHour() * 60 + ldt2.getMinute();
}
// 40分钟兜低(技能未能正确匹配原因) FIXME 需要跟客户沟通
if (null == order.getTakeTime()) {
order.setTakeTime(40);
}
if (null == order.getTags()) {
order.setTags("");
}
if (null == order.getPriority()) {
order.setPriority(0);
}
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime());
customerList.add(customer);
});
log.info("组织问题对象, customer-list, teamId:{}, batchNo:{}, customer-list:{}", teamId, batchNo, customerList.size());
// technicianList
ArrayList<Technician> technicianList = new ArrayList<>();
dispatchEngineerRepo.findByTeamIdAndBatchNo(teamId, batchNo).forEach(engineer -> {
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点", Double.parseDouble(engineer.getX()), Double.parseDouble(engineer.getY()));
// Depot depot = new Depot(engineer.getId(), engineer.getEngineerCode(), location, 60 * 8, 60 * 18);
// depotList.add(depot);
// log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{}", groupId, batchNo, engineer.getEngineerCode());
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
// log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{} , skills:{}", groupId, batchNo, engineer.getEngineerCode(), String.join(";", skillList));
// 距离偏好map
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
// FIXME 硬约束:电动车固定40km上限,其他无限制
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), engineer.getMaxDistance() * 1000, oneDepot, 60 * 8, 60 * 18, Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
});
log.info("组织问题对象, depotList-list, teamId:{}, batchNo:{}", teamId, batchNo);
log.info("组织问题对象, technician-list, teamId:{}, batchNo:{}, technician-list:{}", teamId, batchNo, technicianList.size());
// locationlist 起点+订单地点
List<Location> locationList = Stream.concat(Lists.newArrayList(oneDepot).stream().map(Depot::getLocation), customerList.stream().map(Customer::getLocation)).collect(Collectors.toList());
DispatchSolution solution = new DispatchSolution(team.getGroupId(), teamId, batchNo, locationList, oneDepot, technicianList, customerList);
// path 路网数据初始化,FIXME 需要专门路网数据缓存库
long time1 = System.currentTimeMillis();
distanceCalculator.initDistanceMaps(locationList);
long time2 = System.currentTimeMillis();
log.info("组织问题对象 done, teamId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}, 路网耗时path:{}ms", teamId, batchNo, technicianList.size(), customerList.size(), locationList.size(), time2-time1);
return solution;
}
/*
* 按小组、批次号组装问题对象
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!