Commit a988a1c8 by 王力

Merge branch 'dev_orderassigncheck0801' into 'develop'

Dev orderassigncheck0801

See merge request !318
2 parents f5a3de5e ef7041f2
package com.dituhui.pea.order.common;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dituhui.pea.order.dao.*;
import com.dituhui.pea.order.entity.*;
import lombok.Data;
......@@ -9,14 +8,19 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component
......@@ -24,55 +28,37 @@ import java.util.stream.Collectors;
public class OrderAssignCheck {
@Autowired
SkillInfoMPDao skillInfoMPDao;
private SkillInfoDao skillInfoDao;
@Autowired
EngineerBusinessMPDao engineerBusinessMPDao;
private EngineerBusinessDao engineerBusinessDao;
@Autowired
EngineerInfoMPDao engineerInfoMPDao;
private EngineerInfoDao engineerInfoDao;
@Autowired
OrgGroupMPDao orgGroupMPDao;
private OrgGroupDao orgGroupDao;
@Autowired
OrderInfoMPDao orderInfoMPDao;
private OrderInfoDao orderInfoDao;
@Data
public class Result {
private Boolean canAssign;
private String message;
private int minuteAddition;
private int distanceAddition;
private LocalDateTime start;
private LocalDateTime end;
private int index;
public Result(boolean canAssign, String message, int minuteAddition, int distanceAddition, LocalDateTime start, LocalDateTime end, int index){
this.canAssign = canAssign;
this.message = message;
this.minuteAddition = minuteAddition;
this.distanceAddition = distanceAddition;
this.start = start;
this.end = end;
this.index = index;
}
}
@Autowired
private EntityManager entityManager;
public Result orderAssignCheck(String orderId, String engineerCode) {
public Result orderAssignCheck(String orderId, LocalDate dt, String engineerCode) {
log.info("begin orderAssignCheck:orderId={}, engineerCode={}", orderId, engineerCode);
OrderInfo order = orderInfoMPDao.getByOrderId(orderId);
OrderInfoEntity order = orderInfoDao.getByOrderIdAndDt(orderId, dt);
double curX = Double.parseDouble(order.getX());
double curY = Double.parseDouble(order.getY());
// 获取技能需要的时长(分钟)
SkillInfo skillInfo = skillInfoMPDao.getByBrandAndTypeAndSkill(order.getBrand(), order.getType(), order.getSkill());
SkillInfoEntity skillInfo = skillInfoDao.getByBrandAndTypeAndSkill(order.getBrand(), order.getType(), order.getSkill());
int takeTime = skillInfo.getTakeTime();
// 获取客户期望时间段
int start = this.timestamp2Point(order.getExpectTimeBegin());
int end = this.timestamp2Point(order.getExpectTimeEnd());
int start = this.timestamp2Point(Timestamp.valueOf(order.getExpectTimeBegin()));
int end = this.timestamp2Point(Timestamp.valueOf(order.getExpectTimeEnd()));
log.info("客户期望的时间段:{}-{}, 技能所需时长:{}, 坐标:{},{}", start, end, takeTime, curX, curY);
// 获取技术员的已分配订单的时间段, 根据时间段排序
......@@ -84,18 +70,20 @@ public class OrderAssignCheck {
int index = SegmentInsertion.insertSegment(takeTime, start, end, segments);
if (index == -1) {
log.info("没有可连续插入的空间");
return new Result(false, "没有连续可插入空间(没计算时间)", 0, 0, null, null, index);
return new Result(-1, false, false, orderId, null,0,0, null, null, null);
}
log.info("插入位置为第{}单, 已分配单数(不包含本单):{}", index, orderSegments.size());
// 计算距离 & 时间
if(index == 0 && orderSegments.isEmpty()) {
if (index == 0 && orderSegments.isEmpty()) {
// 第一订单为出发地, 没有其他订单
// 技术员出发地
double[] location = this.getEngineerDepartureLocation(engineerCode);
double preX = location[0];
double preY = location[1];
OrderSegment pre = new OrderSegment(480, 480, preX, preY);
OrderSegment pre = new OrderSegment(480, 480, preX, preY, 0, 0, 0);
Pair p = this.getDistanceAndDuration(pre.getX(), pre.getY(), curX, curY);
// 最早可插入位置为技术员出发时间+行程时间
......@@ -106,21 +94,29 @@ public class OrderAssignCheck {
LocalDateTime startDateTime = this.point2LocalDateTime(startInsert, order.getDt());
LocalDateTime endDateTime = this.point2LocalDateTime(endInsert, order.getDt());
return new Result(true, "success", 0, 0, startDateTime, endDateTime, index);
}
else if(index == 0 && !orderSegments.isEmpty()) {
// 当前节点信息
OrderNode curOrder = new OrderNode();
curOrder.setOrderId(orderId);
curOrder.setArriveElapsed(p.getDuration());
curOrder.setArriveDistance(p.getDistance());
curOrder.setTakeTime(takeTime);
curOrder.setPlanStartTime(startDateTime);
curOrder.setPlanEndTime(endDateTime);
return new Result(index, true, true, orderId, "", p.getDistance(), p.getDuration(), curOrder, null, null);
} else if (index == 0 && !orderSegments.isEmpty()) {
// 第一订单为出发地, 且有其他订单
double[] location = this.getEngineerDepartureLocation(engineerCode);
double preX = location[0];
double preY = location[1];
OrderSegment pre = new OrderSegment(480, 480, preX, preY, 0, 0, 0);
OrderSegment cur = new OrderSegment(-1, -1, curX, curY, takeTime, -1, -1);
OrderSegment post = orderSegments.get(0);
OrderSegment pre = new OrderSegment(480, 480, preX, preY);
return this.getResult(curX, curY, pre, post, takeTime, order.getDt(), index);
}
else if(index == orderSegments.size()) {
// 最后一个订单出发
OrderSegment pre = orderSegments.get(index-1);
return this.getResult(index, cur, pre, post, order.getDt());
} else if (index == orderSegments.size()) {
// 有其他订单,最后一个订单出发
OrderSegment pre = orderSegments.get(index - 1);
Pair p = this.getDistanceAndDuration(pre.getX(), pre.getY(), curX, curY);
// 最早可插入位置为技术员上一单出发时间+行程时间
int startPos = pre.getEnd() + p.getDuration();
......@@ -129,83 +125,134 @@ public class OrderAssignCheck {
LocalDateTime startDateTime = this.point2LocalDateTime(startInsert, order.getDt());
LocalDateTime endDateTime = this.point2LocalDateTime(endInsert, order.getDt());
return new Result(true, "success", 0, 0, startDateTime, endDateTime, index);
// 当前节点信息
OrderNode curOrder = new OrderNode();
curOrder.setOrderId(orderId);
curOrder.setArriveElapsed(p.getDuration());
curOrder.setArriveDistance(p.getDistance());
curOrder.setTakeTime(takeTime);
curOrder.setPlanStartTime(startDateTime);
curOrder.setPlanEndTime(endDateTime);
return new Result(index, false, true, orderId, "", p.getDistance(), p.getDuration(), curOrder, null, null);
} else {
OrderSegment pre = orderSegments.get(index-1);
// 插入中间位置
OrderSegment pre = orderSegments.get(index - 1);
OrderSegment cur = new OrderSegment(-1, -1, curX, curY, takeTime, -1, -1);
OrderSegment post = orderSegments.get(index);
return this.getResult(curX, curY, pre, post, takeTime, order.getDt(), index);
return this.getResult(index, cur, pre, post, order.getDt());
}
}
private List<OrderSegment> getEngineerOrderSegments(String engineerCode, LocalDate dt) {
List<OrderSegment> orderSegments = new ArrayList<>();
LambdaQueryWrapper<OrderInfo> lqw = new LambdaQueryWrapper<>();
lqw.eq(OrderInfo::getDt, dt);
lqw.eq(OrderInfo::getEngineerCode, engineerCode);
lqw.eq(OrderInfo::getOrderStatus, "NORMAL");
lqw.in(OrderInfo::getAppointmentStatus, List.of("PRE", "CONFIRM"));
List<OrderInfo> appointments = orderInfoMPDao.selectList(lqw);
if(appointments.isEmpty()){
List<String> appointmentStatusList = Arrays.asList("PRE", "CONFIRM");
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<OrderInfoEntity> criteriaQuery = criteriaBuilder.createQuery(OrderInfoEntity.class);
Root<OrderInfoEntity> root = criteriaQuery.from(OrderInfoEntity.class);
Predicate dtPredicate = criteriaBuilder.equal(root.get("dt"), dt);
Predicate engineerCodePredicate = criteriaBuilder.equal(root.get("engineerCode"), engineerCode);
Predicate orderStatusPredicate = criteriaBuilder.equal(root.get("orderStatus"), "NORMAL");
Predicate appointmentStatusPredicate = root.get("appointmentStatus").in(appointmentStatusList);
criteriaQuery.where(dtPredicate, engineerCodePredicate, orderStatusPredicate, appointmentStatusPredicate);
List<OrderInfoEntity> appointments = entityManager.createQuery(criteriaQuery).getResultList();
if (appointments.isEmpty()) {
return orderSegments;
}
for(OrderInfo o: appointments) {
for (OrderInfoEntity o : appointments) {
OrderSegment seg = new OrderSegment();
seg.setOrderId(o.getOrderId());
seg.setX(Double.parseDouble(o.getX()));
seg.setY(Double.parseDouble(o.getY()));
seg.setStart(this.timestamp2Point(o.getPlanStartTime()));
seg.setEnd(this.timestamp2Point(o.getPlanEndTime()));
seg.setStart(this.timestamp2Point(Timestamp.valueOf(o.getPlanStartTime())));
seg.setEnd(this.timestamp2Point(Timestamp.valueOf(o.getPlanEndTime())));
seg.setElapsed(o.getArriveElapsed());
seg.setDistance(o.getArriveDistance());
orderSegments.add(seg);
}
return orderSegments.stream().sorted(Comparator.comparing(OrderSegment::getStart)).collect(Collectors.toList());
}
private List<SegmentInsertion.Segment> orderSegment2Segment(List<OrderSegment> orderSegments) {
List<SegmentInsertion.Segment> segments = new ArrayList<>();
for (OrderSegment s: orderSegments) {
for (OrderSegment s : orderSegments) {
segments.add(new SegmentInsertion.Segment(s.getOrderId(), s.getStart(), s.getEnd()));
}
return segments;
}
private Result getResult(double curX, double curY, OrderSegment pre, OrderSegment post, int takeTime, LocalDate dt, int index) {
Pair preCurPair = this.getDistanceAndDuration(pre.getX(), pre.getY(), curX, curY);
Pair postCurPair = this.getDistanceAndDuration(post.getX(), post.getY(), curX, curY);
private Result getResult(int index, OrderSegment cur, OrderSegment pre, OrderSegment post, LocalDate dt) {
Pair preCurPair = this.getDistanceAndDuration(pre.getX(), pre.getY(), cur.getX(), cur.getY());
Pair postCurPair = this.getDistanceAndDuration(post.getX(), post.getY(), cur.getX(), cur.getY());
Pair prePostPair = this.getDistanceAndDuration(post.getX(), post.getY(), pre.getX(), pre.getY());
log.info("pre-cur{}, post-cur:{}, pre-post:{}", preCurPair, postCurPair, prePostPair);
// 判断增加时间+距离后,时间是否重叠了
int distance = post.getStart() - postCurPair.getDuration() - (pre.getEnd() + preCurPair.getDuration());
if(distance < takeTime) {
if (distance < cur.getTakeTime()) {
// 不支持插入
return new Result(false, "增加时间路程后,不支持插入", 0, 0, null, null, index);
return new Result(-1, false, false, "", "", 0, 0, null, null, null);
}
// 插入点(时间点)
int startInsert = pre.getEnd() + preCurPair.getDuration();
int endInsert = startInsert + takeTime;
int endInsert = startInsert + cur.getTakeTime();
LocalDateTime startDateTime = this.point2LocalDateTime(startInsert, dt);
LocalDateTime endDateTime = this.point2LocalDateTime(endInsert, dt);
int minuteAddition = preCurPair.getDuration() + postCurPair.getDuration() - prePostPair.getDuration();
int distanceAddition = preCurPair.getDistance() + postCurPair.getDistance() - prePostPair.getDistance();
return new Result(true, "success", minuteAddition, distanceAddition, startDateTime, endDateTime, index);
// 当前节点
OrderNode curOrder = new OrderNode();
curOrder.setOrderId(cur.getOrderId());
curOrder.setArriveElapsed(cur.getElapsed());
curOrder.setArriveDistance(cur.getDistance());
curOrder.setTakeTime(cur.getTakeTime());
curOrder.setPlanStartTime(startDateTime);
curOrder.setPlanEndTime(endDateTime);
//后一个节点最新变更情况
OrderNode postOrder = new OrderNode();
postOrder.setOrderId(post.getOrderId());
postOrder.setTakeTime(post.getTakeTime());
postOrder.setPlanStartTime(this.point2LocalDateTime(post.getStart(), dt));
postOrder.setPlanEndTime(this.point2LocalDateTime(post.getEnd(), dt));
postOrder.setArriveDistance(postCurPair.getDistance());
postOrder.setArriveElapsed(postCurPair.getDuration());
// 后一个节点之前的情况
OrderNode postOrderOrg = new OrderNode();
postOrder.setOrderId(post.getOrderId());
postOrder.setTakeTime(post.getTakeTime());
postOrder.setPlanStartTime(this.point2LocalDateTime(post.getStart(), dt));
postOrder.setPlanEndTime(this.point2LocalDateTime(post.getEnd(), dt));
postOrder.setArriveDistance(post.getDistance());
postOrder.setArriveElapsed(post.getElapsed());
int additionDistance = preCurPair.getDistance() + postCurPair.getDistance() - prePostPair.getDistance();
int additionElapsed = preCurPair.getDuration() + postCurPair.getDuration() - prePostPair.getDuration();
return new Result(index, false, false, cur.getOrderId(), post.getOrderId(), additionDistance, additionElapsed, curOrder, postOrder, postOrderOrg);
}
private double[] getEngineerDepartureLocation(String engineerCode){
private double[] getEngineerDepartureLocation(String engineerCode) {
// 获取技术员出发坐标
// 从技术员配置中获取
EngineerBusiness b = engineerBusinessMPDao.getByEngineerCode(engineerCode);
if( b != null && StringUtils.isNotEmpty(b.getX()) && StringUtils.isNotEmpty(b.getY())){
EngineerBusinessEntity b = engineerBusinessDao.getByEngineerCode(engineerCode);
if (b != null && StringUtils.isNotEmpty(b.getX()) && StringUtils.isNotEmpty(b.getY())) {
return new double[]{Double.parseDouble(b.getX()), Double.parseDouble(b.getY())};
}
//从org_group中获取
EngineerInfo e = engineerInfoMPDao.getByEngineerCode(engineerCode);
OrgGroup g = orgGroupMPDao.getByGroupId(e.getGroupId());
EngineerInfoEntity e = engineerInfoDao.getByEngineerCode(engineerCode);
OrgGroupEntity g = orgGroupDao.getByGroupId(e.getGroupId());
return new double[]{Double.parseDouble(g.getX()), Double.parseDouble(g.getY())};
}
......@@ -224,40 +271,82 @@ public class OrderAssignCheck {
return LocalDateTime.of(dt, this.point2LocalTime(point));
}
private Pair getDistanceAndDuration(double x1, double y1, double x2, double y2){
Distance cal= new Distance();
private Pair getDistanceAndDuration(double x1, double y1, double x2, double y2) {
Distance cal = new Distance();
long distance = Math.round(cal.calculateDistance(x1, y1, x2, y2) * 1.4); // 单位为米
long duration = distance / (19 * 1000 / 60); // 时间为分钟,假设电动车速度为19km/h
return new Pair((int)distance, (int)duration);
return new Pair((int) distance, (int) duration);
}
@Data
public static class OrderNode {
private String orderId;
private LocalDateTime planStartTime;
private LocalDateTime planEndTime;
private int takeTime;
private int arriveDistance;
private int arriveElapsed;
}
@Data
public class Result {
private int index;
private boolean isHead;
private boolean isTail;
private String curOrderId;
private String postOrderId;
private int additionDistance;
private int additionElapsed;
private OrderNode curOrderNode;
private OrderNode postOrderNode;
private OrderNode postOrderNodeOrg;
public Result(int index, boolean isHead, boolean isTail, String curOrderId, String postOrderId, int additionDistance, int additionElapsed, OrderNode curOrderNode, OrderNode postOrderNode, OrderNode postOrderNodeOrg) {
this.index = index;
this.isHead = isHead;
this.isTail = isTail;
this.curOrderId = curOrderId;
this.postOrderId = postOrderId;
this.additionDistance = additionDistance;
this.additionElapsed = additionElapsed;
this.curOrderNode = curOrderNode;
this.postOrderNode = postOrderNode;
this.postOrderNodeOrg = postOrderNodeOrg;
}
}
}
@Data
class OrderSegment{
class OrderSegment {
private String orderId;
private int start;
private int end;
private double x;
private double y;
private int takeTime;
private int elapsed;
private int distance;
public OrderSegment(){
public OrderSegment() {
}
public OrderSegment(int start, int end, double x, double y){
public OrderSegment(int start, int end, double x, double y, int takeTime, int elapsed, int distance) {
this.start = start;
this.end = end;
this.x = x;
this.y = y;
this.takeTime = takeTime;
this.elapsed = elapsed;
this.distance = distance;
}
}
@Data
class Pair{
class Pair {
private int distance;
private int duration;
public Pair(int distance, int duration){
public Pair(int distance, int duration) {
this.distance = distance;
this.duration = duration;
}
......
......@@ -74,9 +74,9 @@ public class OrderAssignImpl implements OrderAssign {
List<OrderAssignRecommendResp.Engineer> items = new ArrayList<>();
for (EngineerInfo engineer : engineers) {
OrderAssignCheck.Result result = orderAssignCheck.orderAssignCheck(orderId, engineer.getEngineerCode());
OrderAssignCheck.Result result = orderAssignCheck.orderAssignCheck(orderId, order.getDt(), engineer.getEngineerCode());
log.info("指派检查结果:{}", result);
if (!result.getCanAssign()) {
if (result.getIndex() < 0) {
continue;
}
......@@ -87,7 +87,7 @@ public class OrderAssignImpl implements OrderAssign {
HashMap<String, List<LabelValueDTO>> orderTips = new HashMap<>();
List<String> orderIds = orderAppointments.stream().map(OrderInfo::getOrderId).collect(Collectors.toList());
if (!orderIds.isEmpty()) {
List<OrderInfo> orders = orderInfoMPDao.selectByDtAndOrderIds(TimeUtils.IsoDate2LocalDate(date), orderIds);
List<OrderInfo> orders = orderInfoMPDao.selectByDtAndOrderIds(order.getDt(), orderIds);
orderTips = this.packOrderTips(orders);
}
......@@ -95,8 +95,8 @@ public class OrderAssignImpl implements OrderAssign {
OrderAssignRecommendResp.InsertInfo insertInfo = new OrderAssignRecommendResp.InsertInfo();
insertInfo.setNumber(String.format("%d/%d", result.getIndex(), orderAppointments.size() + 1));
insertInfo.setTimeDesc(String.format("+%d分钟", result.getMinuteAddition()));
insertInfo.setDistanceDesc(String.format("+%d公里", result.getDistanceAddition() / 1000));
insertInfo.setTimeDesc(String.format("+%d分钟", result.getAdditionElapsed()));
insertInfo.setDistanceDesc(String.format("+%d公里", result.getAdditionDistance() / 1000));
item.setEngineerCode(engineer.getEngineerCode());
item.setEngineerName(engineer.getName());
......@@ -106,7 +106,7 @@ public class OrderAssignImpl implements OrderAssign {
item.setTimeDesc("");
int index = result.getIndex() + 1;
item.setDesc(String.format("将被插入在第%d单,受此影响原第%d单变化第%d单,第%d单将增加%d公里路程,比预计晚到%d分钟,建议调整;", index, index, index + 1, index + 1, result.getDistanceAddition() / 1000, result.getMinuteAddition()));
item.setDesc(String.format("将被插入在第%d单,受此影响原第%d单变化第%d单,第%d单将增加%d公里路程,比预计晚到%d分钟,建议调整;", index, index, index + 1, index + 1, result.getAdditionDistance() / 1000, result.getAdditionElapsed()));
item.setStartTime(String.format("%s 08:00:00", date));
item.setEndTime(String.format("%s 18:00:00", date));
item.setOrders(this.packTimelines(orderAppointments, orderTips));
......@@ -141,13 +141,14 @@ public class OrderAssignImpl implements OrderAssign {
throw new BusinessException(String.format("订单已指派个技术员[%s], 不必重复指派给同一个技术员", engineer.getName()));
}
OrderAssignCheck.Result result = orderAssignCheck.orderAssignCheck(orderId, engineerCode);
OrderAssignCheck.Result result = orderAssignCheck.orderAssignCheck(orderId, order.getDt(), engineerCode);
log.info("指派检查结果:{}", result);
if (!result.getCanAssign()) {
if (result.getIndex() < 0) {
throw new BusinessException("指派失败, 未能找到合适的时间段, 请选择其他技术员");
}
Timestamp planStartTime = Timestamp.valueOf(result.getStart());
Timestamp planEndTime = Timestamp.valueOf(result.getEnd());
OrderAssignCheck.OrderNode insertNode = result.getCurOrderNode();
Timestamp planStartTime = Timestamp.valueOf(insertNode.getPlanStartTime());
Timestamp planEndTime = Timestamp.valueOf(insertNode.getPlanEndTime());
// 更新order_info表状态
LambdaUpdateWrapper<OrderInfo> wrapper = new LambdaUpdateWrapper<>();
......
......@@ -210,9 +210,9 @@ public class OrderCreateServiceImpl implements OrderCreateService {
OrderAssignCheck.Result checkResult = null;
log.info("=== 准备调用指派,候选的技术员列表: {}", matchEngineerCodes);
for (String engineerCode : matchEngineerCodes) {
checkResult = orderAssignCheck.orderAssignCheck(entity.getOrderId(), engineerCode);
checkResult = orderAssignCheck.orderAssignCheck(entity.getOrderId(), entity.getDt(), engineerCode);
log.info("orderAssignCheck ===> orderId[{}]engineerCode[{}] ==> result[{}]", entity.getOrderId(), engineerCode, checkResult);
if (checkResult.getCanAssign()) {
if (checkResult.getIndex() < 0) {
assignEngineerCode = engineerCode;
break;
}
......@@ -221,16 +221,18 @@ public class OrderCreateServiceImpl implements OrderCreateService {
// 虚拟指派
if (StringUtils.isNotBlank(assignEngineerCode)) {
// 修改当前工单
OrderAssignCheck.OrderNode insertNode = checkResult.getCurOrderNode();
entity.setEngineerCode(assignEngineerCode);
EngineerInfoEntity engineerInfo = engineerInfoDao.getByEngineerCode(assignEngineerCode);
entity.setEngineerName(engineerInfo.getName());
entity.setEngineerPhone(engineerInfo.getPhone());
entity.setAppointmentStatus("PRE");
entity.setDispatcher("AUTO_NOW");
entity.setPlanStartTime(checkResult.getStart());
entity.setPlanEndTime(checkResult.getEnd());
entity.setArriveDistance(checkResult.getDistanceAddition());
entity.setArriveElapsed(checkResult.getMinuteAddition());
entity.setPlanStartTime(insertNode.getPlanStartTime());
entity.setPlanEndTime(insertNode.getPlanEndTime());
entity.setArriveDistance(checkResult.getAdditionDistance());
entity.setArriveElapsed(checkResult.getAdditionElapsed());
orderInfoDao.save(entity);
// 如果影响到原有工单,修改原有工单
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!