Commit b66966b1 by chamberone

Merge branch 'develop' of https://zhangguoping@gitlab.dituhui.com/bsh/project/pr…

…oject.git into develop
2 parents ee4072ca 6f6f6e9e
Showing with 428 additions and 57 deletions
......@@ -3,6 +3,9 @@ FROM openjdk:11-jdk-slim as builder
LABEL author="yuluo" \
email="yuluo829@aliyun.com"
ENV TZ=Asia/Shanghai
RUN echo -e "${TZ}" > /etc/timezone && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime
ADD ./project-dispatch/target/project-dispatch-*.jar /app.jar
ADD ./project-dispatch/src/main/resources/data /data
......
......@@ -24,7 +24,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* @author gpzhang
*/
@SpringBootApplication
//@EnableScheduling
@EnableScheduling
public class DispatchServiceApplication {
public static void main(String[] args) {
......
......@@ -95,7 +95,7 @@ public class BatchController {
public Result<?> buildBatch(@PathVariable String groupId, @PathVariable String day) {
log.info("buildBatch, groupId:{}, day:{}", groupId, day);
try {
String batchNo = batchService.buildBatchNo(groupId, day);
String batchNo = batchService.buildBatchData(groupId, day);
DispatchBatch batch = batchService.queryBatch(groupId, batchNo);
DispatchBatchDTO batchDTO = new DispatchBatchDTO();
BeanUtil.copyProperties(batch, batchDTO, CopyOptions.create().setIgnoreNullValue(true));
......
......@@ -2,6 +2,10 @@ 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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import java.io.Serializable;
......@@ -50,10 +54,14 @@ public class DispatchBatch implements Serializable {
@Column(name = "order_num")
private Integer orderNum;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "start_time")
private LocalDateTime startTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "end_time")
private LocalDateTime endTime;
......@@ -67,6 +75,8 @@ public class DispatchBatch implements Serializable {
@Column(name = "memo")
private String memo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private LocalDateTime updateTime;
......
......@@ -2,6 +2,10 @@ 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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import javax.persistence.*;
......@@ -49,6 +53,8 @@ public class DispatchEngineer implements Serializable {
private String memo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private LocalDateTime updateTime;
......
......@@ -5,6 +5,10 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import javax.persistence.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import java.io.Serializable;
......@@ -57,10 +61,14 @@ public class DispatchOrder implements Serializable {
private Integer seq;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "time_begin")
private LocalDateTime timeBegin;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "time_end")
private LocalDateTime timeEnd;
......@@ -71,6 +79,8 @@ public class DispatchOrder implements Serializable {
private String memo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private LocalDateTime updateTime;
......
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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import javax.persistence.*;
......@@ -53,10 +57,14 @@ public class EngineerInfo implements Serializable {
private String memo;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_time")
private LocalDateTime createTime;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "update_time")
private LocalDateTime updateTime;
......
......@@ -2,6 +2,10 @@ 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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import javax.persistence.*;
......@@ -41,22 +45,35 @@ public class OrderAppointment implements Serializable {
@Column(name = "is_workshop")
private Integer isWorkshop;
@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;
......@@ -68,10 +85,16 @@ public class OrderAppointment implements Serializable {
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;
......
......@@ -2,6 +2,10 @@ 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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import javax.persistence.*;
......@@ -53,10 +57,14 @@ public class OrderRequest implements Serializable {
@Column(name = "fault_describe")
private String faultDescribe;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "expect_time_begin")
private LocalDateTime expectTimeBegin;
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "expect_time_end")
private LocalDateTime expectTimeEnd;
......@@ -113,10 +121,14 @@ public class OrderRequest implements Serializable {
private String description;
@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 com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonFormat;
import javax.persistence.*;
......@@ -14,48 +20,52 @@ import javax.persistence.Table;
@Table(name = "org_group")
public class OrgGroup implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "group_id")
private String groupId;
@Column(name = "group_id")
private String groupId;
@Column(name = "group_name")
private String groupName;
@Column(name = "group_name")
private String groupName;
@Column(name = "cluster_id")
private String clusterId;
@Column(name = "cluster_id")
private String clusterId;
@Column(name = "branch_id")
private String branchId;
@Column(name = "branch_id")
private String branchId;
private String address;
private String address;
@Column(name = "x")
private String X;
@Column(name = "x")
private String X;
@Column(name = "y")
private String Y;
@Column(name = "y")
private String Y;
@Column(name = "city_code")
private String cityCode;
@Column(name = "city_code")
private String cityCode;
private Integer kind;
private Integer kind;
private Integer category;
private Integer category;
@Column(name = "warehouse_id")
private Integer warehouseId;
@Column(name = "warehouse_id")
private Integer warehouseId;
private String memo;
private String memo;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "create_time")
private Date createTime;
@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;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Column(name = "update_time")
private Date updateTime;
@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;
}
......@@ -173,7 +173,7 @@ public class Customer {
", endTime=" + endTime +
", serviceDuration=" + serviceDuration +
", requiredSkill='" + requiredSkill + '\'' +
", technician=" + technician.getCode() +
", technician=" + ((technician!=null) ? technician.getCode() : "null") +
", previousCustomer=" + ((previousCustomer != null) ? previousCustomer.getCode() : "null") +
", nextCustomer=" + ((nextCustomer != null) ? nextCustomer.getCode() : "null") +
", arrivalTime=" + arrivalTime +
......
......@@ -10,12 +10,10 @@ import com.dituhui.pea.dispatch.service.SolveService;
import lombok.extern.slf4j.Slf4j;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.api.solver.SolverJob;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig;
import org.optaplanner.persistence.jackson.impl.domain.solution.JacksonSolutionFileIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.scheduling.annotation.Scheduled;
......@@ -23,7 +21,6 @@ import java.io.File;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.UUID;
......@@ -34,7 +31,10 @@ public class BatchScheduler {
String groupId = "gsuzhou";
int nextDays = 3;
@Value("${dispatch.cron.next-day-limit}")
int nextDaysLimit = 3;
@Autowired
BatchService batchService;
......@@ -65,11 +65,11 @@ public class BatchScheduler {
public void dispatchRun() {
log.info("dispatchRun group:{}", groupId);
try {
for (int i = 0; i <= 2; i++) {
for (int i = 0; i <= nextDaysLimit; i++) {
String currDay = LocalDate.now().plusDays(i).format(DateTimeFormatter.ISO_LOCAL_DATE);
log.info("dispatchRun begin----- group:{}, day:{}", groupId, currDay);
String batchNo = batchService.buildBatchNo(groupId, currDay);
String batchNo = batchService.buildBatchData(groupId, currDay);
UUID problemId = solveService.generateProblemId(groupId, batchNo);
log.info("dispatchRun group:{}, day:{}, batch:{}, problemId:{}", groupId, currDay, batchNo, problemId);
......
......@@ -17,7 +17,7 @@ public interface BatchService {
// 检查指定日期的小组是否有在运行的批次任务,有则返回,没有则创建后返回批次码
@Transactional
String buildBatchNo(String groupId, String day) throws SQLException;
String buildBatchData(String groupId, String day) throws SQLException;
DispatchBatch queryBatch(String groupId, String day);
......
......@@ -49,14 +49,14 @@ public class BatchServiceImpl implements BatchService {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmm");
// 将当前时间转换为字符串
String result = LocalTime.now().format(formatter);
return day.replaceAll("-","") + "-" + result;
return day.replaceAll("-", "") + "-" + result;
}
// 检查给定小组、日期是否有在运行的批次任务,没则返回,没有则创建
@Transactional
@Override
public String buildBatchNo(String groupId, String day) {
public String buildBatchData(String groupId, String day) {
log.info("准备批次数据, groupId:{}, day:{}", groupId, day);
String batchNo = "";
String batchDay = "";
......@@ -71,7 +71,6 @@ public class BatchServiceImpl implements BatchService {
" VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sqlInsert, groupId, batchNo, batchDay, 0, 0, LocalDateTime.now(), null, "RUNNING");
// queryRunner.execute(sqlInsert, groupId, batchNo, batchDay, 0, 0, LocalDateTime.now(), null, "RUNNING");
log.info("生成新批次, groupId:{}, day:{}", groupId, batchDay);
} else {
batchNo = optional.get().getBatchNo();
......@@ -104,7 +103,8 @@ public class BatchServiceImpl implements BatchService {
" order by a.expect_time_begin asc ";
int orderCount = jdbcTemplate.update(sqlOrder, batchNo, groupId, batchDay + " 00:00:00", batchDay + " 23:59:59");
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=? where group_id=? and batch_no=?", engCount, orderCount, groupId, batchNo);
jdbcTemplate.update("update dispatch_batch set engineer_num=? , order_num=?, start_time=?, status='RUNNING' where group_id=? and batch_no=?",
engCount, orderCount, LocalDateTime.now(), groupId, batchNo);
log.info("准备批次数据完成, groupId:{}, day:{}, batchNo:{}", groupId, batchDay, batchNo);
......
......@@ -12,6 +12,8 @@ import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.service.ExtractService;
import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.util.FileUtil;
import org.drools.util.FileUtils;
import org.optaplanner.core.api.score.ScoreExplanation;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.solver.SolutionManager;
......@@ -19,12 +21,15 @@ import org.optaplanner.core.api.solver.SolutionUpdatePolicy;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.persistence.jackson.impl.domain.solution.JacksonSolutionFileIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
......@@ -129,6 +134,22 @@ public class ExtractServiceImpl implements ExtractService {
});
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(DispatchSolution.class);
String fileName = String.format("dispatchSolution-%s-%s.json", groupId, batchNo);
File tempFile = new File(fileName);
exporter.write(solution, tempFile);
String dispatchResultJson = "{}";
try {
dispatchResultJson = FileUtil.readAsString(tempFile);
} catch (IOException e) {
log.error("json算法结果回写 error , groupId:{}, batchNo:{} ", groupId, batchNo, e);
}
Object[] paramBatch = {LocalDateTime.now(), dispatchResultJson, groupId, batchNo};
jdbcTemplate.update(" update dispatch_batch set status='DONE', end_time=? , ext=? where group_id=? and batch_no=? ", paramBatch);
log.info("算法结果回写dispatch完成, groupId:{}, batchNo:{}", groupId, batchNo);
}
......
......@@ -58,7 +58,8 @@ public class SolveServiceImpl implements SolveService {
private List<String> queryEngineerSkills(String engineerCode) {
List<String> result = new ArrayList<>();
String sql = "select concat( b.brand, b.type, b.skill) as skill from engineer_skill a left join product_category b \n"
+ "\ton a.category_id= b.product_category_id where a.engineer_code=? and a.status=1 ";
+ "\t on a.category_id= b.product_category_id where a.engineer_code=? and a.status=1 " +
" and b.brand is not null ";
Object[] param = {engineerCode};
result = jdbcTemplate.queryForList(sql, param, String.class);
return result;
......@@ -75,7 +76,7 @@ public class SolveServiceImpl implements SolveService {
Depot oneDepot;
Optional<OrgGroup> optional = groupRepository.findByGroupId(groupId);
if (!optional.isPresent()) {
log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}");
log.error("组织问题对象, 未查询到组织信息 ,groupId:{}, batchNo:{}", groupId, batchNo);
throw new RuntimeException(String.format("组织问题对象, 未查询到组织信息 ,groupId:%s, batchNo:%s", groupId, batchNo));
}
......@@ -100,12 +101,25 @@ public class SolveServiceImpl implements SolveService {
end = ldt2.getMinute();
}
// 40分钟兜低(技能未能正确匹配原因)
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(), location, start, end,
order.getSkills(), order.getTakeTime());
customerList.add(customer);
});
log.info("组织问题对象, customer-list, groupId:{}, batchNo:{}, customer-list:{}", groupId, batchNo, customerList);
// depotlist 技术员中收点列表
ArrayList<Depot> depotList = new ArrayList<Depot>();
// technicianList
......@@ -115,10 +129,10 @@ public class SolveServiceImpl implements SolveService {
Depot depot = new Depot(engineer.getId(), engineer.getEngineerCode(), location, 60 * 8, 60 * 18);
depotList.add(depot);
log.info("组织问题对象, groupId:{}, batchNo:{}, engineer-code:{}", groupId, batchNo, engineer.getEngineerCode());
// log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{}", groupId, batchNo, engineer.getEngineerCode());
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
log.info("组织问题对象, groupId:{}, batchNo:{}, engineer-code:{} , skills:{}", groupId, batchNo, engineer.getEngineerCode(), String.join(";",skillList));
// log.debug("组织问题对象, technicianList groupId:{}, batchNo:{}, engineer-code:{} , skills:{}", groupId, batchNo, engineer.getEngineerCode(), String.join(";", skillList));
// 距离偏好map
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
......@@ -133,6 +147,9 @@ public class SolveServiceImpl implements SolveService {
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());
//locationlist
List<Location> locationList = Stream.concat(depotList.stream().map(Depot::getLocation), customerList.stream().map(Customer::getLocation)).collect(Collectors.toList());
......@@ -172,9 +189,8 @@ public class SolveServiceImpl implements SolveService {
JacksonSolutionFileIO<DispatchSolution> exporter = new JacksonSolutionFileIO<DispatchSolution>(
DispatchSolution.class);
// Set the output file.
exporter.write(solution, new File("dispatchSolution.json"));
String fileName = String.format("dispatchSolution-%s-%s.json", groupId, batchNo);
exporter.write(solution, new File(fileName));
return solution;
}
......
......@@ -3,7 +3,8 @@ server:
dispatch:
cron:
expr: 0 45 8-18 * * ?
expr: 0 36 8-18 * * ?
next-day-limit: 3
# expr: 0 */10 8-18 * * ?
spring:
......@@ -27,9 +28,9 @@ spring:
enabled: false
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/saas_aftersale_test?serverTimezone=Asia/Shanghai
url: jdbc:mysql://127.0.0.1:3388/saas_aftersale_test?serverTimezone=Asia/Shanghai
username: root
password: 12345678
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
jpa:
......
......@@ -4,6 +4,7 @@ server:
dispatch:
cron:
expr: 0 */30 8-18 * * ?
next-day-limit: 3
spring:
application:
......
......@@ -78,6 +78,12 @@
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>project-dispatch</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
......
package com.dituhui.pea.order.common;
import com.dituhui.pea.dispatch.pojo.Location;
import com.dituhui.pea.order.dao.EngineerBusinessMPDao;
import com.dituhui.pea.order.dao.OrderAppointmentMPDao;
import com.dituhui.pea.order.dao.OrderRequestMPDao;
import com.dituhui.pea.order.dao.SkillInfoMPDao;
import com.dituhui.pea.order.entity.EngineerBusiness;
import com.dituhui.pea.order.entity.OrderAppointment;
import com.dituhui.pea.order.entity.OrderRequest;
import com.dituhui.pea.order.entity.SkillInfo;
import com.dituhui.pea.dispatch.common.GeoDistanceCalculator;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class OrderAssignCheck {
private String orderId;
private String engineerCode;
@Autowired
OrderRequestMPDao orderRequestMPDao;
@Autowired
OrderAppointmentMPDao orderAppointmentMPDao;
@Autowired
SkillInfoMPDao skillInfoMPDao;
@Autowired
EngineerBusinessMPDao engineerBusinessMPDao;
public OrderAssignCheck(String orderId, String engineerCode) {
this.orderId = orderId;
this.engineerCode = engineerCode;
}
@Data
public class Result {
private Boolean canAssign;
private String message;
private int minuteAddition;
private int distanceAddition;
private LocalDateTime start;
private LocalDateTime end;
public Result(boolean canAssign, String message, int minuteAddition, int distanceAddition, LocalDateTime start, LocalDateTime end){
this.canAssign = canAssign;
this.message = message;
this.minuteAddition = minuteAddition;
this.distanceAddition = distanceAddition;
this.start = start;
this.end = end;
}
}
public Result orderAssignCheck() {
OrderRequest order = orderRequestMPDao.getByOrderId(this.orderId);
double curX = Double.parseDouble(order.getX());
double curY = Double.parseDouble(order.getY());
// 获取技能需要的时长(分钟)
SkillInfo skillInfo = skillInfoMPDao.getByBrandAndTypeAndSkill(order.getBrand(), order.getType(), order.getSkill());
int takeTime = skillInfo.getTakeTime();
// 获取客户期望时间段
int start = this.timestamp2Point(order.getExpectTimeBegin());
int end = this.timestamp2Point(order.getExpectTimeEnd());
// 获取技术员的已分配订单的时间段, 根据时间段排序
List<OrderSegment> orderSegments = getEngineerOrderSegments(engineerCode, order.getDt());
// 转化为SegmentInsertion需要的时间段
List<SegmentInsertion.Segment> segments = this.orderSegment2Segment(orderSegments);
int index = SegmentInsertion.insertSegment(takeTime, start, end, segments);
if (index == -1) {
return new Result(false, "没有连续可插入空间(没计算时间)", 0, 0, null, null);
}
// 计算距离 & 时间
if(index == 0) {
// 第一订单为出发地
EngineerBusiness b = engineerBusinessMPDao.getByEngineerCode(engineerCode);
OrderSegment post = orderSegments.get(1);
OrderSegment pre = new OrderSegment(480, post.getStart(), Double.parseDouble(b.getX()), Double.parseDouble(b.getY()));
return this.getResult(curX, curY, pre, post, takeTime, order.getDt());
}
else if(index == orderSegments.size()) {
// 最后一个订单出发
OrderSegment pre = orderSegments.get(index);
// 为当前订单
OrderSegment post = new OrderSegment(start, end, Double.parseDouble(order.getX()), Double.parseDouble(order.getY()));
} else {
OrderSegment pre = orderSegments.get(index);
OrderSegment post = orderSegments.get(index+1);
return this.getResult(curX, curY, pre, post, takeTime, order.getDt());
}
return null;
}
private List<OrderSegment> getEngineerOrderSegments(String engineerCode, LocalDate dt) {
List<OrderSegment> orderSegments = new ArrayList<>();
List<OrderAppointment> appointments = orderAppointmentMPDao.selectByEngineerCodeAndDt(engineerCode, dt);
List<String> orderIds = new ArrayList<>();
for (OrderAppointment o: appointments) {
// 过滤掉已经取消的订单
if(o.getStatus().equals("NOT_ASSIGNED") || o.getStatus().equals("CANCELED")) {
continue;
}
orderIds.add(o.getOrderId());
}
Map<String, List<OrderAppointment>> m = appointments.stream().collect(Collectors.groupingBy(OrderAppointment::getOrderId));
List<OrderRequest> orderRequests = orderRequestMPDao.selectByOrderIds(orderIds);
for(OrderRequest o: orderRequests) {
List<OrderAppointment> tmp = m.getOrDefault(o.getOrderId(), new ArrayList<>());
if(tmp.isEmpty()) {
continue;
}
OrderAppointment oa = tmp.get(0);
OrderSegment seg = new OrderSegment();
seg.setOrderId(o.getOrderId());
seg.setX(Double.parseDouble(o.getX()));
seg.setY(Double.parseDouble(o.getY()));
seg.setStart(this.timestamp2Point(oa.getExpectStartTime()));
seg.setEnd(this.timestamp2Point(oa.getExpectEndTime()));
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) {
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) {
Pair preCurPair = this.getDistanceAndDuration(pre.getX(), pre.getY(), curX, curY);
Pair postCurPair = this.getDistanceAndDuration(post.getX(), post.getY(), curX, curY);
Pair prePostPair = this.getDistanceAndDuration(post.getX(), post.getY(), pre.getX(), pre.getY());
// 判断增加时间+距离后,时间是否重叠了
int distance = post.getStart() - postCurPair.getDuration() - (pre.getEnd() + preCurPair.getDuration());
if(distance < takeTime) {
// 不支持插入
return new Result(false, "增加时间路程后,不支持插入", 0, 0, null, null);
}
// 插入点(时间点)
int startInsert = pre.getEnd() + preCurPair.getDuration();
int endInsert = startInsert + takeTime;
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);
}
private int timestamp2Point(Timestamp t) {
LocalDateTime dt = t.toLocalDateTime();
return dt.getHour() * 60 + dt.getMinute();
}
private LocalTime point2LocalTime(int point) {
int hour = point / 60;
int minute = point % 60;
return LocalTime.of(hour, minute, 0);
}
private LocalDateTime point2LocalDateTime(int point, LocalDate dt) {
return LocalDateTime.of(dt, this.point2LocalTime(point));
}
private Pair getDistanceAndDuration(double x1, double y1, double x2, double y2){
Location from = new Location(1, "1", "1", x1, y1);
Location to = new Location(2, "2", "2", x2, y2);
GeoDistanceCalculator cal= new GeoDistanceCalculator();
int distance = (int) cal.calculateDistance(from, to); // 单位为米
int duration = distance / (19 * 1000 / 60); // 时间为分钟,假设电动车速度为19km/h
return new Pair(distance, duration);
}
}
@Data
class OrderSegment{
private String orderId;
private int start;
private int end;
private double x;
private double y;
public OrderSegment(){
}
public OrderSegment(int start, int end, double x, double y){
this.start = start;
this.end = end;
this.x = x;
this.y = y;
}
}
@Data
class Pair{
private int distance;
private int duration;
public Pair(int distance, int duration){
this.distance = distance;
this.duration = duration;
}
}
......@@ -9,10 +9,12 @@ public class SegmentInsertion {
@Data
public static class Segment {
private String sid;
private int start;
private int end;
public Segment(int start, int end) {
public Segment(String sid, int start, int end) {
this.sid = sid;
this.start = start;
this.end = end;
}
......
......@@ -3,7 +3,11 @@ package com.dituhui.pea.order.dao;
import com.dituhui.pea.order.entity.EngineerBusiness;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface EngineerBusinessMPDao extends BaseMapper<EngineerBusiness> {
@Select("select * from engineer_business where engineer_code=#{engineerCode}")
EngineerBusiness getByEngineerCode(String engineerCode);
}
......@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDate;
import java.util.List;
@Mapper
......@@ -15,4 +16,7 @@ public interface OrderAppointmentMPDao extends BaseMapper<OrderAppointment> {
@Select("select * from order_appointment where order_id=#{orderId}")
List<OrderAppointment> selectByOrderId(String orderId);
@Select("select * from order_appointment where engineer_code=#{engineerCode} and dt=#{dt}")
List<OrderAppointment> selectByEngineerCodeAndDt(String engineerCode, LocalDate dt);
}
......@@ -3,7 +3,11 @@ package com.dituhui.pea.order.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dituhui.pea.order.entity.SkillInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface SkillInfoMPDao extends BaseMapper<SkillInfo> {
@Select("select * from skill_info where brand=#{brand} and type=#{type} and skill=#{skill}")
SkillInfo getByBrandAndTypeAndSkill(String brand, String type, String skill);
}
......@@ -12,6 +12,10 @@ public interface WarehouseInfoDao extends JpaRepository<OrgWarehouseInfoEntity,
@Query("select a from OrgWarehouseInfoEntity a join OrgTeamEntity o on a.branchId=o.branchId where o.teamId= :teamId")
List<OrgWarehouseInfoEntity> getAllByTeamId(String teamId);
// 返回工作队所在的分部所有的配件仓
@Query("select a from OrgWarehouseInfoEntity a join OrgGroupEntity o on a.branchId=o.branchId where o.groupId= :groupId")
List<OrgWarehouseInfoEntity> getAllByGroupId(String groupId);
// 返回工作队关联的配件仓
@Query("select a from OrgWarehouseInfoEntity a join OrgTeamEntity o on a.warehouseId=o.warehouseId where o.teamId= :teamId")
OrgWarehouseInfoEntity getByTeamAssigned(String teamId);
......
......@@ -2,7 +2,6 @@ package com.dituhui.pea.order.entity;
import lombok.Data;
import java.sql.Time;
import java.sql.Timestamp;
@Data
......@@ -17,6 +16,7 @@ public class OrgTeam {
private String warehouseId;
private String workdays;
private String memo;
private String workOn;
private Timestamp createTime;
private Timestamp updateTime;
}
......@@ -95,7 +95,7 @@ public class BusinessTeamServiceImpl implements BusinessTeamService {
@Override
public Result<?> getWarehouses(BusinessWarehousesDTO.Request req) {
List<OrgWarehouseInfoEntity> warehouseInfoEntityList = warehouseInfoDao.getAllByTeamId(req.getGroupId());
List<OrgWarehouseInfoEntity> warehouseInfoEntityList = warehouseInfoDao.getAllByGroupId(req.getGroupId());
List<BusinessWarehousesDTO.Content> contents = warehouseInfoEntityList.stream().map(entity -> {
BusinessWarehousesDTO.Content dto = new BusinessWarehousesDTO.Content();
dto.setWarehouseId(entity.getWarehouseId());
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!