Commit 32188e2e by 张晓

解决合并冲突

2 parents afa28734 f36179c2
Showing with 267 additions and 406 deletions
......@@ -10,13 +10,14 @@ import java.util.Optional;
public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, Long> {
// 查看未指派非confirm的,供算法计算
@Query("from DispatchOrder where groupId=?1 and batchNo=?2 and status !='CONFIRM' and (engineerCode is null or engineerCode='' ) ")
List<DispatchOrder> findNotAssigned(String groupId, String batchNo);
// 查看算法指派成功且非confirm状态的
@Query("from DispatchOrder where groupId=?1 and batchNo=?2 and status !='CONFIRM' and engineerCode is not null and engineerCode!='' ")
@Query("from DispatchOrder where groupId=?1 and batchNo=?2 and status !='CONFIRM' and ( engineerCode is not null and engineerCode!='') ")
List<DispatchOrder> findAssigned(String groupId, String batchNo);
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderId(String groupId, String batchNo, String orderId);
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderIdAndDt(String groupId, String batchNo, String orderId, String dt);
}
\ No newline at end of file
package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.OrderAppointment;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.Optional;
public interface OrderAppointmentRepository extends CrudRepository<OrderAppointment, Long> {
@Query(value = "SELECT * FROM order_appointment WHERE order_id=:orderId ORDER BY id DESC LIMIT 1", nativeQuery = true)
Optional<OrderAppointment> findByOrderId(String orderId);
}
package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.OrderInfo;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.time.LocalDate;
import java.util.Optional;
public interface OrderInfoRepository extends CrudRepository<OrderInfo, Long> {
// @Query(value = "SELECT * FROM order_info WHERE order_id=:orderId ORDER BY dt DESC LIMIT 1",nativeQuery = true)
Optional<OrderInfo> findOrderInfoByOrderIdAndDt(String orderId, LocalDate dt);
}
package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.OrderRequest;
import org.springframework.data.repository.CrudRepository;
import java.util.Optional;
public interface OrderRequestRepository extends CrudRepository<OrderRequest, Long> {
Optional<OrderRequest> findByOrderId(String orderId);
}
......@@ -30,9 +30,15 @@ public class DispatchOrder implements Serializable {
@Column(name = "batch_no")
private String batchNo;
@Column(name = "team_id")
private String teamId;
@Column(name = "order_id")
private String orderId;
@Column(name = "dt")
private String dt;
@Column(name = "x")
private String X;
......@@ -73,6 +79,14 @@ public class DispatchOrder implements Serializable {
@Column(name = "time_end")
private LocalDateTime timeEnd;
//到达耗时(分钟)
@Column(name = "path_time")
private Integer pathTime;
//到达距离(米)
@Column(name = "path_distance")
private Integer pathDistance;
private String status;
private String ext;
......
......@@ -45,11 +45,6 @@ public class EngineerInfo implements Serializable {
private String credentials;
private Integer vehicle;
@Column(name = "vehicle_no")
private String vehicleNo;
@Column(name = "bean_status")
private Integer beanStatus;
......
package com.dituhui.pea.dispatch.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "order_appointment")
public class OrderAppointment implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name = "order_id")
private String orderId;
@Column(name = "suborder_id")
private String suborderId;
@Column(name = "main_sub")
private Integer mainSub;
@Column(name = "engineer_code")
private String engineerCode;
@Column(name = "engineer_name")
private String engineerName;
@Column(name = "engineer_phone")
private String engineerPhone;
@Column(name = "engineer_age")
private Integer engineerAge;
@Column(name = "is_workshop")
private Integer isWorkshop;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Column(name = "dt")
private LocalDate dt;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "expect_start_time")
private LocalDateTime expectStartTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "expect_end_time")
private LocalDateTime expectEndTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "actual_time")
private LocalDateTime actualTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "actual_start_time")
private LocalDateTime actualStartTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "actual_end_time")
private LocalDateTime actualEndTime;
@Column(name = "pre_status")
private String preStatus;
private String status;
private String memo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "create_time")
private LocalDateTime createTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "update_time")
private LocalDateTime updateTime;
}
package com.dituhui.pea.dispatch.entity;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
......@@ -17,20 +18,34 @@ import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "order_request")
public class OrderRequest implements Serializable {
@Table(name = "order_info")
public class OrderInfo implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private String id;
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "order_id")
private String orderId;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dt;
@Column(name = "sub_id")
private String subId;
private String name;
private String phone;
private String province;
private String city;
private String county;
private String address;
@Column(name = "x")
......@@ -39,11 +54,8 @@ public class OrderRequest implements Serializable {
@Column(name = "y")
private String Y;
private String province;
private String city;
private String county;
@Column(name = "bean_brand")
private String beanBrand;
private String brand;
......@@ -51,19 +63,17 @@ public class OrderRequest implements Serializable {
private String skill;
@Column(name = "apply_note")
private String applyNote;
@Column(name = "take_time")
private Integer takeTime;
@Column(name = "is_workshop")
private Integer isWorkshop;
@Column(name = "fault_describe")
private String faultDescribe;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Column(name = "dt")
private LocalDate dt;
@Column(name = "apply_note")
private String applyNote;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
......@@ -82,53 +92,96 @@ public class OrderRequest implements Serializable {
private String source;
@Column(name = "bean_priority")
private String beanPriority;
@Column(name = "bean_tags")
private String beanTags;
@Column(name = "bean_status")
private String beanStatus;
@Column(name = "bean_sub_status")
private String beanSubStatus;
@Column(name = "area_id")
private String areaId;
@Column(name = "order_priority")
private String orderPriority;
@Column(name = "org_cluster_id")
private String orgClusterId;
@Column(name = "order_tags")
private String orderTags;
@Column(name = "org_branch_id")
private String orgBranchId;
@Column(name = "org_group_id")
private String orgGroupId;
@Column(name = "org_team_id")
private String orgTeamId;
private Integer priority;
private String tags;
private String status;
@Column(name = "appointment_method")
private String appointmentMethod;
@Column(name = "appointment_status")
private String appointmentStatus;
@Column(name = "appointment_method")
private String appointmentMethod;
@Column(name = "order_status")
private String orderStatus;
@Column(name = "org_cluster_id")
private String orgClusterId;
@Column(name = "service_status")
private String serviceStatus;
@Column(name = "org_cluster_name")
private String orgClusterName;
@Column(name = "engineer_code")
private String engineerCode;
@Column(name = "org_branch_id")
private String orgBranchId;
@Column(name = "engineer_name")
private String engineerName;
@Column(name = "org_branch_name")
private String orgBranchName;
@Column(name = "engineer_phone")
private String engineerPhone;
@Column(name = "org_group_id")
private String orgGroupId;
@Column(name = "engineer_code_sub")
private String engineerCodeSub;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "plan_start_time")
private LocalDateTime planStartTime;
@Column(name = "org_group_name")
private String orgGroupName;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "plan_end_time")
private LocalDateTime planEndTime;
@Column(name = "org_team_id")
private String orgTeamId;
@Column(name = "arrive_elapsed")
private Integer arriveElapsed;
@Column(name = "org_team_name")
private String orgTeamName;
@Column(name = "arrive_distance")
private Integer arriveDistance;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "actual_start_time")
private LocalDateTime actualStartTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "actual_end_time")
private LocalDateTime actualEndTime;
private String description;
@Column(name = "extra_info")
private String extraInfo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
......
......@@ -21,6 +21,8 @@ public class Customer {
private long id;
private String code;
// orderid(code)+dt 确定唯一一条工单
private String dt;
@JsonIgnore
private Location location;
// 时间窗 分钟
......@@ -45,10 +47,11 @@ public class Customer {
public Customer() {
}
public Customer(long id, String code, Location location, int startTime, int endTime, String requiredSkill,
public Customer(long id, String code, String dt, Location location, int startTime, int endTime, String requiredSkill,
int serviceDuration) {
this.id = id;
this.code = code;
this.dt = dt;
this.location = location;
this.startTime = startTime;
this.endTime = endTime;
......@@ -116,7 +119,7 @@ public class Customer {
}
/**
* 与前一个订单或者出发地depot的距离
* 与前一个订单或者出发地depot的距离(米)
*
* @return
*/
......@@ -132,7 +135,7 @@ public class Customer {
}
/**
* 与前一个订单或者出发地depot的路程时间
* 与前一个订单或者出发地depot的路程时间(分钟)
*
* @return
*/
......@@ -168,16 +171,17 @@ public class Customer {
return "Customer{" +
"id=" + id +
", code='" + code + '\'' +
", dt='" + dt + '\'' +
", location=" + location +
", startTime=" + startTime +
", endTime=" + endTime +
", serviceDuration=" + serviceDuration +
", requiredSkill='" + requiredSkill + '\'' +
", technician=" + ((technician!=null) ? technician.getCode() : "null") +
", technician=" + ((technician != null) ? technician.getCode() : "null") +
", previousCustomer=" + ((previousCustomer != null) ? previousCustomer.getCode() : "null") +
", nextCustomer=" + ((nextCustomer != null) ? nextCustomer.getCode() : "null") +
", arrivalTime=" + arrivalTime +
", departureTime=" + ((getDepartureTime() != null )? getDepartureTime() : 0) +
", departureTime=" + ((getDepartureTime() != null) ? getDepartureTime() : 0) +
'}';
}
}
......@@ -38,7 +38,7 @@ public class BatchScheduler {
@Value("${dispatch.cron.next-day-limit}")
int nextDaysLimit = 3;
int nextDaysLimit = 2;
@Autowired
BatchService batchService;
......@@ -75,10 +75,11 @@ public class BatchScheduler {
log.info("dispatchRun begin----- group:{}, day:{}", groupId, currDay);
LocalTime currentTime = LocalTime.now();
LocalTime cutoffTime = LocalTime.parse("16:00:00", DateTimeFormatter.ISO_LOCAL_TIME);
LocalTime cutoffTime = LocalTime.parse("18:00:00", DateTimeFormatter.ISO_LOCAL_TIME);
// 明天单才有cutoff
if (i == 1 && currentTime.isAfter(cutoffTime)) {
/*if (i == 1 && currentTime.isAfter(cutoffTime)) {
log.info("dispatchRun 已过cutoff时间,更新pre状态为confirm----- group:{}, day:{}", groupId, currDay);
DispatchBatch dispatchBatch = batchService.queryBatchInfoByDay(groupId, currDay);
if (null == dispatchBatch.getBatchNo()) {
......@@ -97,6 +98,7 @@ public class BatchScheduler {
}
continue;
}
*/
String batchNo = batchService.buildBatchData(groupId, currDay);
UUID problemId = solveService.generateProblemId(groupId, batchNo);
......
......@@ -11,7 +11,7 @@ import java.sql.SQLException;
* @author zhangx
* <p>
* 排班算法数据准备
* 排班结果解析到dispatch_order(更新补充技术员工号、上门时间) ,order_appointment、order_request
* 排班结果解析到dispatch_order(更新补充技术员工号、上门时间) ,order_info
*/
public interface ExtractService {
......@@ -19,9 +19,7 @@ public interface ExtractService {
/*
* 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新)
* order_request(主要更新状态)
* 将dispath_order 中的计算结果,回写到 order_info
* */
void extractDispatchToOrder(String groupId, String batchNo, boolean isConfirm) ;
......
......@@ -101,54 +101,39 @@ public class BatchServiceImpl implements BatchService {
" order by a.engineer_code asc";
int engCount = jdbcTemplate.update(sqlEngineer, batchNo, groupId);
// 未派过的工单(虚拟指派不更改order_request.appointment_status,所以也包含在当前条件中)
String sqlOrder = "INSERT INTO dispatch_order (group_id, batch_no, order_id , x, y, expect_time_begin, expect_time_end, tags, priority , skills , take_time )\n" +
" select a.org_group_id, ? , a.order_id, a.x, a.y , \n" +
" a.expect_time_begin, a.expect_time_end, a.tags, a.priority , concat(a.brand, '-', a.type, '-', a.skill) skills , b.take_time \n" +
" from order_request a left join skill_info b on (a.brand=b.brand and a.type=b.type and a.skill=b.skill )\n" +
" where a.org_group_id=? and a.status='OPEN' " +
" and a.dt = ? " +
" and a.appointment_status ='NOT_ASSIGNED' and a.appointment_method like 'AUTO%' \n" +
" order by a.expect_time_begin asc ";
// 未派过的工单(已派过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.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" +
" from order_info a \n" +
" where a.org_group_id=? and a.dt = ? and bean_status='OPEN'\n" +
" and appointment_method like 'AUTO%' and a.appointment_status in ('INIT','VIRTUAL', 'PRE')\n" +
" and order_status ='NORMAL' and service_status='INIT'\n" +
" order by a.expect_time_begin asc ";
int orderCount = jdbcTemplate.update(sqlOrder, batchNo, groupId, batchDay);
// 已派过PRE状态还可以再次派
String sqlOrderPre = "INSERT INTO dispatch_order (group_id, batch_no, order_id , x, y, expect_time_begin, expect_time_end, tags, priority , skills , take_time )\n" +
" select a.org_group_id, ?, a.order_id, a.x, a.y , \n" +
" a.expect_time_begin, a.expect_time_end, a.tags, a.priority , concat(a.brand,'-', a.type, '-', a.skill) skills , b.take_time \n" +
" from order_request a " +
" left join skill_info b on (a.brand=b.brand and a.type=b.type and a.skill=b.skill )\n" +
" left join order_appointment o on (a.order_id =o.order_id)\n" +
" where a.org_group_id=? and a.status='OPEN' \n" +
" and a.dt = ? " +
" and a.appointment_status = 'ASSIGNED' and a.appointment_method like 'AUTO%' \n" +
" and o.pre_status in ('PRE') \n" +
" order by a.expect_time_begin asc ";
int orderCountPre = jdbcTemplate.update(sqlOrderPre, batchNo, groupId, batchDay);
// CONFIRM的订单再次入表(1占用时间窗 2,可视化需要展示)
String sqlOrderConfirm = "INSERT INTO dispatch_order (group_id, batch_no, order_id , x, y, expect_time_begin, expect_time_end, tags, priority , skills , take_time, " +
" engineer_code, seq, time_begin, time_end, status )\n" +
" select a.org_group_id, ?, a.order_id, a.x, a.y , \n" +
" a.expect_time_begin, a.expect_time_end, a.tags, a.priority , concat(a.brand,'-', a.type, '-', a.skill) skills , b.take_time, \n" +
" o.engineer_code, -1, o.expect_start_time, o.expect_end_time, o.pre_status " +
" from order_request a " +
" left join skill_info b on (a.brand=b.brand and a.type=b.type and a.skill=b.skill )\n" +
" left join order_appointment o on (a.order_id =o.order_id)\n" +
" where a.org_group_id=? and a.status='OPEN' \n" +
" and a.dt = ? " +
" and o.pre_status in ('CONFIRM') \n" +
" order by a.expect_time_begin asc ";
// int orderCountConfirm = jdbcTemplate.update(sqlOrderConfirm, batchNo, groupId, batchDay);
log.info("准备批次数据 orderCount :{}", orderCount);
log.info("准备批次数据 orderCountPre :{}", orderCountPre);
// log.info("准备批次数据 orderCountConfirm :{}", orderCountConfirm);
if (orderCount + orderCountPre > 0) {
// 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_group_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, groupId, batchDay);
log.info("准备批次数据 orderCount:{}, orderConfirmCount:{}", orderCount, orderConfirmCount);
if (orderCount + orderConfirmCount > 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);
engCount, orderCount + orderConfirmCount, 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, 0, LocalDateTime.now(), LocalDateTime.now(), groupId, batchNo);
......
......@@ -16,7 +16,9 @@ import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
......@@ -34,7 +36,6 @@ import static org.springframework.transaction.annotation.Isolation.READ_COMMITTE
@Service
public class ExtractServiceImpl implements ExtractService {
@Autowired
EngineerInfoRepository engineerInfoRepo;
......@@ -43,10 +44,8 @@ public class ExtractServiceImpl implements ExtractService {
DispatchOrderRepository dispatchOrderRepo;
@Autowired
OrderRequestRepository orderRequestRepo;
OrderInfoRepository orderInfoRepo;
@Autowired
OrderAppointmentRepository orderAppointmentRepo;
@Autowired
OrderLogRepository orderLogRepo;
......@@ -62,9 +61,8 @@ public class ExtractServiceImpl implements ExtractService {
/**
* 将dispath_order 中的计算结果,回写到 order_request, order_appointment
* order_appointment(新增、更新)
* order_request(主要更新状态)
* 将dispath_order 中的计算结果,回写到 order_info
* order_info(主要更新状态)
*/
@Transactional(isolation = READ_COMMITTED, propagation = Propagation.REQUIRED)
@Override
......@@ -84,21 +82,29 @@ public class ExtractServiceImpl implements ExtractService {
int idx = atomicInteger.getAndIncrement();
String orderId = dispatchOrder.getOrderId();
String engCode = dispatchOrder.getEngineerCode();
log.info("算法结果更新到工单, step1.1-loop, {}/{}, groupId:{}, batchNo:{}, orderId:{}, engCode:{}",
idx, dispatchOrderList.size(), groupId, batchNo, orderId, engCode);
String dt = dispatchOrder.getDt();
log.info("算法结果更新到工单, step1.1-loop, {}/{}, groupId:{}, batchNo:{}, orderId:{}, dt:{}, engCode:{}",
idx, dispatchOrderList.size(), groupId, batchNo, orderId, dt, engCode);
Optional<OrderRequest> orderOpt = orderRequestRepo.findByOrderId(orderId);
if (!orderOpt.isPresent()) {
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, 工单不存在, groupId:{}, batchNo:{}, orderId:{}",
groupId, batchNo, orderId);
return;
}
OrderRequest orderRequest = orderOpt.get();
OrderInfo orderInfo = orderOpt.get();
if (!("OPEN".equals(orderInfo.getBeanStatus()) &&
Set.of("AUTO_NOW", "AUTO_BATCH").contains(orderInfo.getAppointmentMethod()) &&
Set.of("INIT", "VIRTUAL", "PRE").contains(orderInfo.getAppointmentStatus()) &&
"NORMAL".equals(orderInfo.getOrderStatus()) &&
"INIT".equals(orderInfo.getServiceStatus())
if (!("OPEN".equals(orderRequest.getStatus()) && Set.of("ASSIGNED", "NOT_ASSIGNED").contains(orderRequest.getAppointmentStatus()))) {
log.warn("算法结果更新到工单, step1.1-loop, 工单状态异常, groupId:{}, batchNo:{}, orderId:{}, status:{}, appointment-status:{}",
groupId, batchNo, orderId, orderRequest.getStatus(), orderRequest.getAppointmentStatus());
)) {
log.warn("算法结果更新到工单, step1.1-loop, 工单状态异常, groupId:{}, batchNo:{}, orderId:{}, bean-status:{}, appointment-status:{}, order-status:{}, service-status:{}",
groupId, batchNo, orderId, orderInfo.getBeanStatus(), orderInfo.getAppointmentStatus(), orderInfo.getOrderStatus(), orderInfo.getServiceStatus());
return;
}
......@@ -111,102 +117,36 @@ public class ExtractServiceImpl implements ExtractService {
}
String engName = engineerInfo.getName();
String phone = engineerInfo.getPhone();
int age = 0;
/*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.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>", engCode, engName)).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
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);
}
if ("NOT_ASSIGNED".equals(orderRequest.getAppointmentStatus())) {
jdbcTemplate.update("update order_request set appointment_status ='ASSIGNED' where order_id =? and appointment_status='NOT_ASSIGNED'", orderId);
}
// 会有多次上门情况( pre_status='PRE' and status in ('NOT_ASSIGNED', 'ASSIGNED') ,直接更新,其它情况新增)
Optional<OrderAppointment> appointmentOpt = orderAppointmentRepo.findByOrderId(orderId);
if (appointmentOpt.isEmpty()) {
// 没有则插入一条
OrderAppointment appointment = new OrderAppointment();
appointment.setOrderId(orderId);
String subId = String.format("%s_%s", orderId, DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss"));
appointment.setSuborderId(subId);
appointment.setMainSub(1);
appointment.setEngineerCode(engCode);
appointment.setEngineerName(engName);
appointment.setEngineerPhone(phone);
appointment.setEngineerAge(age);
appointment.setIsWorkshop(0);
appointment.setDt(orderRequest.getDt());
appointment.setExpectStartTime(dispatchOrder.getTimeBegin());
appointment.setExpectEndTime(dispatchOrder.getTimeEnd());
appointment.setPreStatus(isConfirm ? "CONFIRM" : "PRE");
appointment.setStatus("ASSIGNED");
appointment.setMemo("");
appointment.setCreateTime(LocalDateTime.now());
appointment.setUpdateTime(LocalDateTime.now());
orderAppointmentRepo.save(appointment);
// 写order_log
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(subId).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", engCode, engName)).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
if (isConfirm) {
OrderEvent orderEvent = new OrderEvent().setOrderId(orderId).setSuborderId(subId).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);
}
} else {
// 如果有记录:1虚拟指派 2排量预派 3
OrderAppointment appointment = appointmentOpt.get();
if (Set.of("PRE", "VIRTUAL").contains(appointment.getPreStatus()) &&
Set.of("NOT_ASSIGNED", "ASSIGNED").contains(appointment.getStatus())) {
String lastContent = String.format("批量自动指派:<%s,%s>", appointment.getEngineerCode(), appointment.getEngineerName());
appointment.setStatus("ASSIGNED");
appointment.setPreStatus(isConfirm ? "CONFIRM" : "PRE");
appointment.setEngineerCode(engCode);
appointment.setEngineerName(engName);
appointment.setEngineerPhone(phone);
appointment.setEngineerAge(age);
appointment.setDt(orderRequest.getDt());
appointment.setExpectStartTime(dispatchOrder.getTimeBegin());
appointment.setExpectEndTime(dispatchOrder.getTimeEnd());
appointment.setUpdateTime(LocalDateTime.now());
orderAppointmentRepo.save(appointment);
if (null == appointment.getSuborderId()) {
appointment.setSuborderId(appointment.getOrderId());
}
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(appointment.getSuborderId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", engCode, engName)).setContentOld(lastContent)
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
if (isConfirm) {
OrderEvent orderEvent = new OrderEvent().setOrderId(orderId).setSuborderId(appointment.getSuborderId()).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);
}
} else {
// 其它情况待补充(改约) todo
if (Set.of("CONFIRM").contains(appointmentOpt.get().getPreStatus()) &&
Set.of("TODO", "???").contains(appointmentOpt.get().getStatus())) {
}
}
}
});
log.info("算法结果更新到工单完成, groupId:{}, batchNo:{}", groupId, batchNo);
......
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;
import com.dituhui.pea.dispatch.dao.DispatchEngineerRepository;
import com.dituhui.pea.dispatch.dao.DispatchOrderRepository;
import com.dituhui.pea.dispatch.dao.OrderLogRepository;
import com.dituhui.pea.dispatch.dao.OrgGroupRepository;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import com.dituhui.pea.dispatch.entity.OrderLog;
import com.dituhui.pea.dispatch.entity.OrgGroup;
import com.dituhui.pea.dispatch.pojo.*;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.dituhui.pea.dispatch.service.SolveService;
import com.dituhui.pea.dispatch.utils.DispatchSolutionUtils;
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;
......@@ -29,12 +23,11 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
......@@ -68,7 +61,6 @@ public class SolveServiceImpl implements SolveService {
private EntityManager entityManager;
@Autowired
ExtractService extractService;
......@@ -78,9 +70,7 @@ public class SolveServiceImpl implements SolveService {
// 查询技术员所有技能集
private List<String> queryEngineerSkills(String engineerCode) {
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 ";
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};
return jdbcTemplate.queryForList(sql, param, String.class);
}
......@@ -123,12 +113,12 @@ public class SolveServiceImpl implements SolveService {
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();
}
if (ldt1 != null) {
start = ldt1.getHour() * 60 + ldt1.getMinute();
}
if (ldt2 != null) {
end = ldt2.getHour() * 60 + ldt2.getMinute();
}
// 40分钟兜低(技能未能正确匹配原因)
......@@ -142,8 +132,7 @@ public class SolveServiceImpl implements SolveService {
order.setPriority(0);
}
Customer customer = new Customer(order.getId(), order.getOrderId(), location, start, end,
order.getSkills(), order.getTakeTime());
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime());
customerList.add(customer);
});
......@@ -171,14 +160,13 @@ public class SolveServiceImpl implements SolveService {
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(),
engineer.getMaxNum(), engineer.getMaxMinute(), engineer.getMaxDistance() * 1000, depot,
60 * 8, 60 * 18, Set.copyOf(skillList), preferedLoctionDistanceMap);
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), engineer.getMaxDistance() * 1000, depot, 60 * 8, 60 * 18, Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
});
log.info("组织问题对象, depotList-list, groupId:{}, batchNo:{}, depotList-list:{}", groupId, batchNo, depotList.size());
log.info("组织问题对象, technician-list, groupId:{}, batchNo:{}, technician-list:{}", groupId, batchNo, technicianList.size());
log.info("组织问题对象, technician-list, groupId:{}, batchNo:{}, customer-list:{}", groupId, batchNo, customerList.size());
//locationlist
List<Location> locationList = Stream.concat(depotList.stream().map(Depot::getLocation), customerList.stream().map(Customer::getLocation)).collect(Collectors.toList());
......@@ -186,8 +174,7 @@ public class SolveServiceImpl implements SolveService {
DispatchSolution solution = new DispatchSolution(groupId, batchNo, locationList, oneDepot, technicianList, customerList);
distanceCalculator.initDistanceMaps(locationList);
log.info("组织问题对象 done, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}",
groupId, batchNo, technicianList.size(), customerList.size(), locationList.size());
log.info("组织问题对象 done, groupId:{}, batchNo:{}, technician-size:{}, customer-size:{}, location-size:{}", groupId, batchNo, technicianList.size(), customerList.size(), locationList.size());
return solution;
......@@ -204,11 +191,10 @@ public class SolveServiceImpl implements SolveService {
// Load the problem
DispatchSolution problem = prepareSolution(groupId, batchNo);
if (problem.getCustomerList().size() <= 0) {
log.info("dispatchRun 当前批次没有待指派工单 , group:{}, batch:{}, order-size:{}", groupId, batchNo, problem.getCustomerList().size());
log.info("dispatchRun 当前批次没有待指派工单 , group:{}, batch:{}, order-size:{}", groupId, batchNo, 0);
throw new RuntimeException("当前批次没有待指派工单");
}
SolverConfig solverConfig = new SolverConfig().withSolutionClass(DispatchSolution.class);
solverConfig.withEntityClassList(Arrays.asList(Technician.class, Customer.class));// 这里不能漏掉,否则约束不生效
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
......@@ -222,8 +208,7 @@ public class SolveServiceImpl implements SolveService {
DispatchSolutionUtils.removeHardConstraintCustomer(solution, solverFactory);
log.info("调用引擎处理-结束, groupId:{}, batchNo:{}, score:{}", groupId, batchNo, solution.getScore());
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(
DispatchSolution.class);
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
String fileName = String.format("dispatchSolution-%s-%s.json", groupId, batchNo);
exporter.write(solution, new File(fileName));
......@@ -289,46 +274,36 @@ public class SolveServiceImpl implements SolveService {
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);
String sqlReset = "update dispatch_order set engineer_code='', seq=0, time_begin=null, time_end=null, path_time=0, path_distance=0 " + "where group_id=? and batch_no=? and status!='CONFIRM' ";
jdbcTemplate.update(sqlReset, 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());
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());
// 时间相加操作
// LocalDateTime localExpectBegin = LocalDateTime.ofInstant(expectBegin[0].toInstant(), ZoneId.systemDefault());
// LocalDateTime localEndTime = localExpectBegin.plusMinutes(dOrder.getTakeTime());
// Date end = Date.from(localEndTime.atZone(ZoneId.systemDefault()).toInstant());
Date arriveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getArrivalTime());
Date leaveTime = DateUtil.beginOfDay(dOrder.getExpectTimeBegin()).offset(DateField.MINUTE, customer.getDepartureTime());
log.info("算法结果回写dispatch, step3-逐个客户处理, groupId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} ", groupId, batchNo, technician.getCode(), customer.getCode(), customer.getServiceDuration());
log.info(customer.toString());
LocalDateTime customDateTime = LocalDateTime.parse(customer.getDt()+" 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime arriveTime = customDateTime.plusMinutes(customer.getArrivalTime());
LocalDateTime leaveTime = customDateTime.plusMinutes(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);
}
int pathTime = customer.getPathTimeFromPreviousStandstill();
long pathDistance = customer.getDistanceFromPreviousStandstill();
String sql = "update dispatch_order set engineer_code=?, seq=?, time_begin=? ,time_end=?, path_time=?, path_distance=? " + " where group_id=? and batch_no=? and order_id=? and dt=? and status!='CONFIRM' ";
Object[] param = {technician.getCode(), idx, arriveTime, leaveTime, pathTime, pathDistance, groupId, batchNo, customer.getCode(), customer.getDt()};
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);
});
......
......@@ -264,7 +264,7 @@ public class DataUtils {
// 初始化订单+技能服务时间
List<Customer> customerList = new ArrayList<>();
for (int i = 0; i < customerIndexMap.keySet().size(); i++) {
customerList.add(new Customer(i + 1, customerIndexMap.get(i + 1), locationIndex.get(i + 2),
customerList.add(new Customer(i + 1, customerIndexMap.get(i + 1), "2000-01-01", locationIndex.get(i + 2),
customerStartMap.get(i + 1), customerEndMap.get(i + 1), customerSkillMap.get(i + 1),
// 初始化技能服务时间
customerCodeServiceTimeMap.get(customerIndexMap.get(i + 1))));
......
......@@ -44,6 +44,7 @@ class SolveServiceTest {
String groupId = "gsuzhou";
String day = "2023-07-25";
private SolverManager<DispatchSolution, UUID> solverManager;
private SolverFactory<DispatchSolution> solverFactory;
private Solver<DispatchSolution> solver;
......@@ -55,6 +56,7 @@ class SolveServiceTest {
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(20));
solverFactory = SolverFactory.create(solverConfig);
solver = solverFactory.buildSolver();
solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
......@@ -81,7 +83,17 @@ class SolveServiceTest {
solveService.saveSolutionWrp(solution);
extractService.extractDispatchToOrder(groupId, batchNo, false);
log.info("testAsync done");
}
@Test
public void testExtraceToOrder() throws InterruptedException {
log.info("testExtraceToOrder init");
String batchNo = "20230723-0810";
extractService.extractDispatchToOrder(groupId, batchNo, false);
log.info("testExtraceToOrder done");
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!