Commit 39c7d181 by Ren Ping

fix:自动任务优化

1 parent 83792934
......@@ -214,6 +214,11 @@ public class BatchServiceImpl implements BatchService {
} else {
batchNo = optional.get().getBatchNo();
batchDay = optional.get().getBatchDate();
// 执行数据库操作
String sqlInsert = "update dispatch_batch set" +
" group_id=?, engineer_num=0, order_num=0, start_time=?, end_time=null,status='RUNNING'" +
" where team_id=? and batch_no=?";
jdbcTemplate.update(sqlInsert, groupId, LocalDateTime.now(), teamId, batchNo);
}
log.info("清理原批次数据, teamId:{}, day:{}, batchNo:{}", teamId, batchDay, batchNo);
......@@ -225,40 +230,40 @@ public class BatchServiceImpl implements BatchService {
//dispatchOrderRepository.deleteAllInBatch(dispatchOrderRepository.findByTeamIdAndBatchNo(teamId,batchNo));
log.info("写入新批次技术员、工单数据, teamId:{}, day:{}, batchNo:{}", teamId, batchDay, batchNo);
String sqlEngineer = "INSERT INTO dispatch_engineer (group_id, team_id, batch_no, engineer_code, engineer_name, x, y, max_num, max_minute, max_distance, vehicle_type)\n"
+ "SELECT ?, o.team_id,?,o.engineer_code, a.name , b.x, b.y , max_num, max_minute, max_distance, b.vehicle FROM `org_team_engineer` o\n"
String sqlEngineer = "INSERT INTO dispatch_engineer (group_id, team_id, batch_no, engineer_code, engineer_name, x, y, max_num, max_minute, max_distance, vehicle_type)\n"
+ "SELECT ?, o.team_id,?,o.engineer_code, a.name , b.x, b.y , max_num, max_minute, max_distance, b.vehicle FROM `org_team_engineer` o\n"
+ " join engineer_info a on o.engineer_code=a.engineer_code\n"
+ " left join engineer_business b on a.engineer_code = b.engineer_code\n"
+ " WHERE o.team_id=? AND o.`status`=1\n"
//+ " AND b.x IS NOT NULL AND b.x !=''"
+ " WHERE o.team_id=? AND o.`status`=1\n"
//+ " AND b.x IS NOT NULL AND b.x !=''"
+ " order by a.engineer_code asc";
int engCount = jdbcTemplate.update(sqlEngineer, groupId, batchNo, teamId);
int engCount = jdbcTemplate.update(sqlEngineer, groupId, 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_team_id , a.order_id, ?, 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_team_id=? AND a.dt = ? AND bean_status='OPEN'\n"
+ " AND appointment_method LIKE 'AUTO%' AND a.appointment_status IN ('INIT', 'PRE')\n"
+ " AND order_status ='NORMAL' AND service_status='INIT'\n"
+ " ORDER BY a.expect_time_begin ASC \n";
int orderCount = jdbcTemplate.update(sqlOrder, groupId, batchNo, batchDay, teamId, batchDay);
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_team_id , a.order_id, ?, 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_team_id=? AND a.dt = ? AND bean_status='OPEN'\n"
+ " AND appointment_method LIKE 'AUTO%' AND a.appointment_status IN ('INIT', 'PRE')\n"
+ " AND order_status ='NORMAL' AND service_status='INIT'\n"
+ " ORDER BY a.expect_time_begin ASC \n";
int orderCount = jdbcTemplate.update(sqlOrder, groupId, 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_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, groupId, batchNo, teamId, batchDay);
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_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, groupId, batchNo, teamId, batchDay);
log.info("准备批次数据 engCount:{}, orderCount:{}, orderConfirmCount:{}", engCount, orderCount, orderConfirmCount);
......
package com.dituhui.pea.dispatch.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.dituhui.pea.dispatch.common.GeoDistanceCalculator;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
......@@ -70,9 +71,9 @@ public class SolveServiceImpl implements SolveService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
OrgTeamDao orgTeamDao;
@Autowired
OrgTeamDao orgTeamDao;
// 查询技术员所有技能集
private List<String> queryEngineerSkills(String engineerCode) {
......@@ -85,7 +86,7 @@ public class SolveServiceImpl implements SolveService {
// 按小组、批次号组装问题对象
@Override
public DispatchSolution prepareSolution(String groupId, String batchNo) {
return null;
return null;
// log.info("组织问题对象, groupId:{}, batchNo:{}", groupId, batchNo);
//
// entityManager.clear();
......@@ -189,123 +190,123 @@ 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()));
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(), "中心点", StrUtil.isEmpty(engineer.getX()) ? 0 : Double.parseDouble(engineer.getX()), StrUtil.isEmpty(engineer.getY()) ? 0 : 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());
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);
});
// 硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), maxDistance, engineer.getVehicleType() , 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();
// 根据不同的交通工具,初始化不同的路网数据
List<Integer> vehicleTypes = technicianList.stream()
.map(tec -> tec.getVehicleType() == null ? 1 : tec.getVehicleType()).distinct()
.collect(Collectors.toList());
distanceCalculator.initDistanceMaps(locationList, vehicleTypes);
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;
// 距离偏好map
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
// 硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(), engineer.getMaxMinute(), maxDistance, engineer.getVehicleType(), 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();
// 根据不同的交通工具,初始化不同的路网数据
List<Integer> vehicleTypes = technicianList.stream()
.map(tec -> tec.getVehicleType() == null ? 1 : tec.getVehicleType()).distinct()
.collect(Collectors.toList());
distanceCalculator.initDistanceMaps(locationList, vehicleTypes);
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;
}
/*
......@@ -449,7 +450,7 @@ public class SolveServiceImpl implements SolveService {
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 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());
......@@ -500,7 +501,7 @@ public class SolveServiceImpl implements SolveService {
log.info("算法结果回写dispatch, step3-逐个客户处理, teamId:{}, batchNo:{}, employ: {}, customer:{}, service-duration:{} ", teamId, 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 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());
......
......@@ -60,12 +60,13 @@ spring:
#调度实例失效的检查时间间隔
clusterCheckinInterval: 10000
useProperties: false
misfireThreshold: 600000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
# 指定在线程池里面创建的线程是否是守护线程
makeThreadsDaemons: false
#指定线程数,至少为1(无默认值,一般设置为1-100)
threadCount: 5
threadCount: 10
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!