Commit ab627f1b by Ren Ping

feat:初始化工程师容量定时任务 dispatch

1 parent c10eb803
......@@ -2,15 +2,24 @@ package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchEngineer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
import java.util.Map;
public interface DispatchEngineerRepository extends CrudRepository<DispatchEngineer, Long>, JpaRepository<DispatchEngineer, Long> {
List<DispatchEngineer> findByGroupId(String groupId);
List<DispatchEngineer> findByGroupIdAndBatchNo(String groupId, String batchNo);
List<DispatchEngineer> findByTeamIdAndBatchNo(String teamId, String batchNo);
@Query(value = "SELECT ?1 group_id, o.team_id,?2 batch_no,o.engineer_code, a.name engineer_name, b.x, b.y , max_num, max_minute, max_distance, b.vehicle vehicle_type 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=?3 AND o.`status`=1\n",
nativeQuery = true)
List<Map<String,Object>> getNewDispatchEngineer(String groupId, String batchNo, String teamId);
}
\ No newline at end of file
package com.dituhui.pea.dispatch.dao;
import com.dituhui.pea.dispatch.entity.DispatchEngineer;
import com.dituhui.pea.dispatch.entity.DispatchOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, Long>, JpaRepository<DispatchOrder, Long> {
......@@ -34,4 +36,30 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderIdAndDt(String groupId, String batchNo, String orderId, String dt);
List<DispatchOrder> findByTeamIdAndBatchNo(String teamId, String batchNo);
@Query(value = " SELECT ?1 group_id, ?2 batch_no, a.org_team_id team_id, a.order_id, date_format(a.dt,'%Y-%m-%d') 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 status\n"
+ " FROM order_info a \n" + " WHERE a.org_team_id=?3 AND a.dt = ?4 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",
nativeQuery = true)
List<Map<String,Object>> getNewDispatchOrder(String groupId, String batchNo, String teamId, String batchDay);
@Query(value = " select ?1 group_id, ?2 batch_no, a.org_team_id team_id, a.order_id, date_format(a.dt,'%Y-%m-%d') 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 status, \n" +
" a.engineer_code, date_format(a.plan_start_time,'%Y-%m-%d %H:%i:%s') time_begin, date_format(a.plan_end_time,'%Y-%m-%d %H:%i:%s') time_end \n" +
" from order_info a \n" +
" where a.org_team_id=?3 and a.dt = ?4 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 ",
nativeQuery = true)
List<Map<String,Object>> getNewDispatchConfirmOrder(String groupId, String batchNo, String teamId, String batchDay);
}
\ No newline at end of file
......@@ -19,202 +19,203 @@ import lombok.Data;
@PlanningEntity
public class Technician {
@PlanningId
private long id;
private String code;
@JsonIgnore
private Depot depot;
// 上班时间窗 分钟480-1080 8-18点
// private int startTime;
// private int endTime;
/**
* 上班多时间窗
* 时间窗格式:[[起始时间段1,结束时间段1],[起始时间段2,结束时间段2]...]
* 时间格式:从0点开始的分钟数,如早上8点为480.
*/
private int[][] timeWindows;
// 技能
private Set<String> skills;
// 偏好坐标
private Location preferredlocation;
// technician code : customer code , distance
@JsonIgnore
private Map<String, Long> preferredlocationDistanceMap = new HashMap<String, Long>();
// 每日最大单量
private int maxCount;
// 每日最大工作时长
private int maxMinute;
// 单位是米,这里要注意
private int maxDistanceMeter;
// 交通方式 1汽车;2电动车;3自行车;4步行 默认是汽车
private Integer vehicleType;
@PlanningListVariable
private List<Customer> customerList = new ArrayList<>();
public Technician() {
}
public Technician(long id, String code, Depot depot, int startTime, int endTime, Set<String> skills,
Map<String, Long> preferredlocationDistanceMap, Location preferredlocation) {
this.id = id;
this.code = code;
this.depot = depot;
this.timeWindows = new int[][] { new int[] { startTime, endTime } };
this.skills = skills;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
this.preferredlocation = preferredlocation;
}
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, Depot depot,
int startTime, int endTime, Set<String> skills, Map<String, Long> preferredlocationDistanceMap) {
this.id = id;
this.code = code;
this.depot = depot;
this.timeWindows = new int[][] { new int[] { startTime, endTime } };
this.skills = skills;
@PlanningId
private long id;
private String code;
@JsonIgnore
private Depot depot;
// 上班时间窗 分钟480-1080 8-18点
// private int startTime;
// private int endTime;
/**
* 上班多时间窗
* 时间窗格式:[[起始时间段1,结束时间段1],[起始时间段2,结束时间段2]...]
* 时间格式:从0点开始的分钟数,如早上8点为480.
*/
private int[][] timeWindows;
// 技能
private Set<String> skills;
// 偏好坐标
private Location preferredlocation;
// technician code : customer code , distance
@JsonIgnore
private Map<String, Long> preferredlocationDistanceMap = new HashMap<String, Long>();
// 每日最大单量
private int maxCount;
// 每日最大工作时长
private int maxMinute;
// 单位是米,这里要注意
private int maxDistanceMeter;
// 交通方式 1汽车;2电动车;3自行车;4步行 默认是汽车
private Integer vehicleType;
@PlanningListVariable
private List<Customer> customerList = new ArrayList<>();
public Technician() {
}
public Technician(long id, String code, Depot depot, int startTime, int endTime, Set<String> skills,
Map<String, Long> preferredlocationDistanceMap, Location preferredlocation) {
this.id = id;
this.code = code;
this.depot = depot;
this.timeWindows = new int[][]{new int[]{startTime, endTime}};
this.skills = skills;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
this.preferredlocation = preferredlocation;
}
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, Depot depot,
int startTime, int endTime, Set<String> skills, Map<String, Long> preferredlocationDistanceMap) {
this.id = id;
this.code = code;
this.depot = depot;
this.timeWindows = new int[][]{new int[]{startTime, endTime}};
this.skills = skills;
this.maxCount = maxCount;
this.maxMinute = maxMinute;
this.maxDistanceMeter = maxDistanceMeter;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
}
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, Integer vehicleType, Depot depot,
int startTime, int endTime, Set<String> skills, Map<String, Long> preferredlocationDistanceMap) {
this.id = id;
this.code = code;
this.maxCount = maxCount;
this.maxMinute = maxMinute;
this.maxDistanceMeter = maxDistanceMeter;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
}
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, Integer vehicleType, Depot depot,
int startTime, int endTime, Set<String> skills, Map<String, Long> preferredlocationDistanceMap) {
this.id = id;
this.code = code;
this.depot = depot;
this.timeWindows = new int[][] { new int[] { startTime, endTime } };
this.skills = skills;
this.maxCount = maxCount;
this.maxMinute = maxMinute;
this.maxDistanceMeter = maxDistanceMeter;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
}
// ************************************************************************
// Complex methods
// ************************************************************************
/**
* @return route of the vehicle
*/
@JsonIgnore
public List<Location> getRoute() {
if (customerList.isEmpty()) {
return Collections.emptyList();
}
List<Location> route = new ArrayList<Location>();
route.add(depot.getLocation());
for (Customer customer : customerList) {
route.add(customer.getLocation());
}
return route;
}
/**
* 总路线距离
*
* @return
*/
public long getTotalDistanceMeters() {
if (customerList.isEmpty()) {
return 0;
}
long totalDistance = 0;
Location previousLocation = depot.getLocation();
for (Customer customer : customerList) {
totalDistance += previousLocation.getDistanceTo(this.getVehicleType(),customer.getLocation());
previousLocation = customer.getLocation();
}
totalDistance += previousLocation.getDistanceTo(this.getVehicleType(), depot.getLocation());
return totalDistance;
}
/**
* 获取偏好总距离 所有customer与该技术员的PreferredLocation距离之和
*
* @return
*/
public long getPreferredTotalDistanceMeters() {
if (customerList.isEmpty()) {
return 0;
}
long totalDistance = 0;
for (Customer customer : customerList) {
totalDistance += preferredlocationDistanceMap.get(customer.getCode());
}
return totalDistance;
}
public int getCustomerSize() {
return customerList.size();
}
/**
* 获取总上班时间,第一个订单到最后一个订单时间跨度
*
* @return
*/
public int getWorkTime() {
int size = customerList.size();
if (0 == size) {
return 0;
} else {
return customerList.get(size - 1).getArrivalTime() + customerList.get(size - 1).getServiceDuration()
- customerList.get(0).getArrivalTime();
}
}
/**
* 获取下班时间,最后一个订单完成时间
*
* @return
*/
public int getOffWorkTime() {
int size = customerList.size();
if (0 == size) {
return 0;
} else {
Customer lastCustomer = customerList.get(size - 1);
return lastCustomer.getDepartureTime();
}
}
@Override
public int hashCode() {
return Long.valueOf(this.id).hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Technician))
return false;
if (obj == this)
return true;
return this.id == ((Technician) obj).getId();
}
@Override
public String toString() {
return "Technician{" + "id=" + id + ", code='" + code + '\'' + ", depot=" + depot + ", timeWindows=" + timeWindows
+ ", skills=" + skills + ", maxCount=" + maxCount + ", maxMinute=" + maxMinute
+ ", maxDistanceMeter=" + maxDistanceMeter + '}';
}
this.vehicleType = vehicleType;
this.depot = depot;
this.timeWindows = new int[][]{new int[]{startTime, endTime}};
this.skills = skills;
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
}
// ************************************************************************
// Complex methods
// ************************************************************************
/**
* @return route of the vehicle
*/
@JsonIgnore
public List<Location> getRoute() {
if (customerList.isEmpty()) {
return Collections.emptyList();
}
List<Location> route = new ArrayList<Location>();
route.add(depot.getLocation());
for (Customer customer : customerList) {
route.add(customer.getLocation());
}
return route;
}
/**
* 总路线距离
*
* @return
*/
public long getTotalDistanceMeters() {
if (customerList.isEmpty()) {
return 0;
}
long totalDistance = 0;
Location previousLocation = depot.getLocation();
for (Customer customer : customerList) {
totalDistance += previousLocation.getDistanceTo(this.getVehicleType(), customer.getLocation());
previousLocation = customer.getLocation();
}
totalDistance += previousLocation.getDistanceTo(this.getVehicleType(), depot.getLocation());
return totalDistance;
}
/**
* 获取偏好总距离 所有customer与该技术员的PreferredLocation距离之和
*
* @return
*/
public long getPreferredTotalDistanceMeters() {
if (customerList.isEmpty()) {
return 0;
}
long totalDistance = 0;
for (Customer customer : customerList) {
totalDistance += preferredlocationDistanceMap.get(customer.getCode());
}
return totalDistance;
}
public int getCustomerSize() {
return customerList.size();
}
/**
* 获取总上班时间,第一个订单到最后一个订单时间跨度
*
* @return
*/
public int getWorkTime() {
int size = customerList.size();
if (0 == size) {
return 0;
} else {
return customerList.get(size - 1).getArrivalTime() + customerList.get(size - 1).getServiceDuration()
- customerList.get(0).getArrivalTime();
}
}
/**
* 获取下班时间,最后一个订单完成时间
*
* @return
*/
public int getOffWorkTime() {
int size = customerList.size();
if (0 == size) {
return 0;
} else {
Customer lastCustomer = customerList.get(size - 1);
return lastCustomer.getDepartureTime();
}
}
@Override
public int hashCode() {
return Long.valueOf(this.id).hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Technician))
return false;
if (obj == this)
return true;
return this.id == ((Technician) obj).getId();
}
@Override
public String toString() {
return "Technician{" + "id=" + id + ", code='" + code + '\'' + ", depot=" + depot + ", timeWindows=" + timeWindows
+ ", skills=" + skills + ", maxCount=" + maxCount + ", maxMinute=" + maxMinute
+ ", maxDistanceMeter=" + maxDistanceMeter + '}';
}
}
......@@ -230,40 +230,54 @@ 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"
+ " 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 !=''"
+ " order by a.engineer_code asc";
int engCount = jdbcTemplate.update(sqlEngineer, groupId, batchNo, teamId);
// 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 !=''"
// + " order by a.engineer_code asc";
// int engCount = jdbcTemplate.update(sqlEngineer, groupId, batchNo, teamId);
List<Map<String, Object>> engMapList = dispatchEngineerRepository.getNewDispatchEngineer(groupId, batchNo, teamId);
List<DispatchEngineer> engList = JSONObject.parseArray(JSONObject.toJSONString(engMapList), DispatchEngineer.class);
engList.forEach(eng -> eng.setExt(null));
dispatchEngineerRepository.saveAll(engList);
int engCount = engList.size();
// 未派过的工单(已派过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 ? group_id, ? batch_no, a.org_team_id team_id, a.order_id, a.dt 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 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);
List<Map<String, Object>> orderList = dispatchOrderRepository.getNewDispatchOrder(groupId, batchNo, teamId, batchDay);
dispatchOrderRepository.saveAll(JSONObject.parseArray(JSONObject.toJSONString(orderList), DispatchOrder.class));
int orderCount = orderList.size();
// 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 ? group_id, ? batch_no, a.org_team_id 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 status, \n" +
// " a.engineer_code, a.plan_start_time time_begin, a.plan_end_time time_end \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);
List<Map<String, Object>> orderConfirmList = dispatchOrderRepository.getNewDispatchConfirmOrder(groupId, batchNo, teamId, batchDay);
dispatchOrderRepository.saveAll(JSONObject.parseArray(JSONObject.toJSONString(orderConfirmList), DispatchOrder.class));
int orderConfirmCount = orderConfirmList.size();
log.info("准备批次数据 engCount:{}, orderCount:{}, orderConfirmCount:{}", engCount, orderCount, orderConfirmCount);
......
......@@ -3,7 +3,7 @@ server:
dispatch:
cron:
expr: 0 10 8-22 * * ?
expr: 0 47 8-22 * * ?
next-day-limit: 2
# expr: 0 */10 8-18 * * ?
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!