Commit 23e3d84e by Ren Ping

feat:移除不满足工程师时间窗强约束的订单

1 parent 1181ddaf
......@@ -30,7 +30,7 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
List<DispatchOrder> findAllWithoutConfirm(String groupId, String batchNo);
// 查看算法指派成功(也有抹掉技术员、时间情况),非confirm状态的
@Query("from DispatchOrder where teamId=?1 and batchNo=?2 and status !='CONFIRM' ")
@Query("from DispatchOrder where teamId=?1 and batchNo=?2 ")
List<DispatchOrder> findAllWithoutConfirm2(String teamId, String batchNo);
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderIdAndDt(String groupId, String batchNo, String orderId, String dt);
......
......@@ -20,8 +20,11 @@ import lombok.Setter;
public class Customer {
private long id;
private String code;
private String status;
// 已分配
private String dispatchedTechnicianCode;
// 已排除
......@@ -56,7 +59,7 @@ public class Customer {
}
public Customer(long id, String code, String dt, Location location, int startTime, int endTime, String requiredSkill,
int serviceDuration) {
int serviceDuration, String status) {
this.id = id;
this.code = code;
this.dt = dt;
......@@ -65,6 +68,7 @@ public class Customer {
this.endTime = endTime;
this.requiredSkill = requiredSkill;
this.serviceDuration = serviceDuration;
this.status = status;
}
@InverseRelationShadowVariable(sourceVariableName = "customerList")
......
......@@ -26,7 +26,7 @@ public interface ExtractService {
/*
* 按小队将dispath_order 中的计算结果,回写到 order_info
* */
void extractDispatchToOrder2(String TeamId, String batchNo, boolean isConfirm) ;
void extractDispatchToOrder2(String TeamId, String batchNo, boolean cutOff) ;
}
......@@ -259,7 +259,7 @@ public class SolveServiceImpl implements SolveService {
order.setPriority(0);
}
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime());
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime(), order.getStatus());
OrderInfo orderInfo = orderInfoRepository.findByOrderId(order.getOrderId()).get(0);
if ((StrUtil.equals("MANUAL", orderInfo.getAppointmentMethod())
&& StrUtil.equals("CONFIRM", orderInfo.getAppointmentStatus()))
......@@ -271,11 +271,11 @@ public class SolveServiceImpl implements SolveService {
customer.setExclusiveTechnicianCode(Objects.nonNull(orderInfo.getAppointEngineerCodes()) ?
orderInfo.getAppointEngineerCodes().trim() : null);
}
log.info(
"订单指定排除工程师, teamId:{}, batchNo:{}, orderId:{}, dispatchedTechnicianCode:{}, exclusiveTechnicianCode:{}",
teamId, batchNo, order.getOrderId(), customer.getDispatchedTechnicianCode(),
customer.getExclusiveTechnicianCode());
log.info(
"订单指定排除工程师, teamId:{}, batchNo:{}, orderId:{}, dispatchedTechnicianCode:{}, exclusiveTechnicianCode:{}",
teamId, batchNo, order.getOrderId(), customer.getDispatchedTechnicianCode(),
customer.getExclusiveTechnicianCode());
customerList.add(customer);
});
......@@ -284,62 +284,62 @@ public class SolveServiceImpl implements SolveService {
// technicianList
ArrayList<Technician> technicianList = new ArrayList<>();
dispatchEngineerRepo.findByTeamIdAndBatchNo(teamId, batchNo).forEach(engineer -> {
// 技术员时间窗
int[][] timeWindows = engineerCalendarService.timeWindows(engineer.getEngineerCode(), teamId,
LocalDateTimeUtil.parseDate(currDay, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
if (null != timeWindows && timeWindows.length > 0) {
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, timeWindows:{}", teamId, batchNo,
engineer.getEngineerName(), new Gson().toJson(timeWindows));
// 技术员技能
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
// 距离偏好map
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点",
StrUtil.isEmpty(engineer.getX()) ? 0 : Double.parseDouble(engineer.getX()),
StrUtil.isEmpty(engineer.getY()) ? 0 : Double.parseDouble(engineer.getY()));
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
if (location.getLongitude() > 1 && location.getLatitude() > 1) {
// 设置了偏好位置
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
} else {
// 未设置,不参与计算
}
// 最大距离,硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
// 技术员出发地
Double x = null;
Double y = null;
if (ObjectUtil.equal(engineer.getDeparture(), DepartureEnum.WORK_ADDR.getCode())) {
x = Double.parseDouble(engineer.getWorkX());
y = Double.parseDouble(engineer.getWorkY());
} else {
x = Double.parseDouble(orgWarehouseInfoEntity.getX());
y = Double.parseDouble(orgWarehouseInfoEntity.getY());
}
Location engineerLocation = new Location(engineer.getId(), engineer.getEngineerCode(), "起点", x, y);
Depot engineerDepot = new Depot(engineer.getId(), engineer.getEngineerCode(), engineerLocation, 60 * 8,
60 * 18);
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(),
engineer.getMaxMinute(), maxDistance, engineer.getVehicleType(), engineerDepot, timeWindows,
Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
} else {
// 没有时间窗技术员,不参与派工
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, no timeWindows", teamId, batchNo,
engineer.getEngineerName());
}
});
dispatchEngineerRepo.findByTeamIdAndBatchNo(teamId, batchNo).forEach(engineer -> {
// 技术员时间窗
int[][] timeWindows = engineerCalendarService.timeWindows(engineer.getEngineerCode(), teamId,
LocalDateTimeUtil.parseDate(currDay, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
if (null != timeWindows && timeWindows.length > 0) {
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, timeWindows:{}", teamId, batchNo,
engineer.getEngineerName(), new Gson().toJson(timeWindows));
// 技术员技能
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
// 距离偏好map
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点",
StrUtil.isEmpty(engineer.getX()) ? 0 : Double.parseDouble(engineer.getX()),
StrUtil.isEmpty(engineer.getY()) ? 0 : Double.parseDouble(engineer.getY()));
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
if (location.getLongitude() > 1 && location.getLatitude() > 1) {
// 设置了偏好位置
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
} else {
// 未设置,不参与计算
}
// 最大距离,硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
// 技术员出发地
Double x = null;
Double y = null;
if (ObjectUtil.equal(engineer.getDeparture(), DepartureEnum.WORK_ADDR.getCode())) {
x = Double.parseDouble(engineer.getWorkX());
y = Double.parseDouble(engineer.getWorkY());
} else {
x = Double.parseDouble(orgWarehouseInfoEntity.getX());
y = Double.parseDouble(orgWarehouseInfoEntity.getY());
}
Location engineerLocation = new Location(engineer.getId(), engineer.getEngineerCode(), "起点", x, y);
Depot engineerDepot = new Depot(engineer.getId(), engineer.getEngineerCode(), engineerLocation, 60 * 8,
60 * 18);
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(),
engineer.getMaxMinute(), maxDistance, engineer.getVehicleType(), engineerDepot, timeWindows,
Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
} else {
// 没有时间窗技术员,不参与派工
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, no timeWindows", teamId, batchNo,
engineer.getEngineerName());
}
});
log.info("组织问题对象, technician-list, teamId:{}, batchNo:{}, technician-list:{}", teamId, batchNo, technicianList.size());
// locationlist 起点+订单地点
......@@ -535,7 +535,8 @@ public class SolveServiceImpl implements SolveService {
log.info("算法结果回写dispatch, step1-清除历史, groupId:{}, batchNo:{}", teamId, batchNo);
Object[] paramClear = {teamId, batchNo};
String sqlReset = "update dispatch_order set engineer_code='', seq=0, time_begin=null, time_end=null, path_time=0, path_distance=0 " + "where team_id=? and batch_no=? and status!='CONFIRM' ";
// confirm 状态工单不清除
String sqlReset = "update dispatch_order set engineer_code=null, seq=0, time_begin=null, time_end=null, path_time=0, path_distance=0 " + "where team_id=? and batch_no=? and status!='CONFIRM' ";
jdbcTemplate.update(sqlReset, paramClear);
log.info("算法结果回写dispatch, step2-开始回写, teamId:{}, batchNo:{}", teamId, batchNo);
......@@ -561,7 +562,7 @@ public class SolveServiceImpl implements SolveService {
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 team_id=? and batch_no=? and order_id=? and dt=? and status!='CONFIRM' ";
String sql = "update dispatch_order set engineer_code=?, seq=?, time_begin=? ,time_end=?, path_time=?, path_distance=? " + " where team_id=? and batch_no=? and order_id=? and dt=?";
Object[] param = {technician.getCode(), idx, arriveTime, leaveTime, pathTime, pathDistance, teamId, 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);
......
......@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.optaplanner.constraint.streams.drools.DroolsConstraintStreamScoreDirector;
......@@ -400,7 +401,8 @@ public class DispatchSolutionUtils {
List<Customer> customerList = new ArrayList<>();
customerList.addAll(((Technician) indictedObject).getCustomerList());
for (Customer customer : customerList) {
if (!customer.isInTechnicianTimeWindows()) {
if (!customer.isInTechnicianTimeWindows()
&& !StrUtil.equals("CONFIRM", customer.getStatus())) {
// 更新shadow变量
updateShadowVariable(customer);
// 移除技术员
......@@ -411,6 +413,19 @@ public class DispatchSolutionUtils {
}
}
break;
case dispatchedMatch:
for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反硬约束对象,根据具体约束返回不同类型对象
if (indictedObject instanceof Customer) {
Customer customer = (Customer) indictedObject;
// 更新shadow变量
updateShadowVariable(customer);
// 移除技术员
customer.getTechnician().getCustomerList().remove(customer);
solution.getUnDispatchedCustomers().add(customer);
}
}
break;
default:
break;
}
......
......@@ -3,7 +3,7 @@ server:
dispatch:
cron:
expr: 0 10 8-23 * * ?
expr: 0 9 8-23 * * ?
next-day-limit: 2
scheduler:
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!