Commit 1a09ae86 by 张晓

Merge branch 'develop' of https://gitlab.dituhui.com/bsh/project/project into develop

2 parents 5f0bc92b 9037490e
package com.dituhui.pea.dispatch.constraint;
/**
* 约束枚举
*
* @author zhangguoping
*
*/
public enum ConstraintNameEnum {
// 硬约束
/**
* 技术员技能跟订单相匹配
*/
skillMatch,
/**
* 技术员到达时间跟时间窗吻合
*/
customerTimeWindowsMatch,
// 软约束
/**
* 订单数量均衡
*/
technicianBalanceSoft,
/**
* 总路程最小
*/
totalDistance,
/**
* 技术员中心点偏好
*/
preferredTotalDistance
}
...@@ -12,38 +12,53 @@ public class DispatchConstraintProvider implements ConstraintProvider { ...@@ -12,38 +12,53 @@ public class DispatchConstraintProvider implements ConstraintProvider {
@Override @Override
public Constraint[] defineConstraints(ConstraintFactory factory) { public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] { greaterThanZero(factory), customerTimeWindowsMatch1(factory), return new Constraint[] {
customerTimeWindowsMatch2(factory), skillMatch(factory), technicianBalance(factory), // 硬约束
technicianBalance2(factory), technicianBalanceSoft(factory), totalDistance(factory), // 运行时长提升效果
preferredTotalDistance(factory) }; // 5s/60s: 技能权重4-技能匹配问题0个,时间窗问题14个
// 10s/300s: 技能权重4-技能匹配问题0个,时间窗问题9个
customerTimeWindowsMatch(factory),
skillMatch(factory),
// 软约束
technicianBalanceSoft(factory),
totalDistance(factory),
preferredTotalDistance(factory)
};
} }
// ************************************************************************ // ************************************************************************
// Hard constraints // Hard constraints
// ************************************************************************ // ************************************************************************
// 1,每个技术员至少分配一个greaterThanZero // 1,技术员到达时间跟时间窗吻合customerTimeWindowsMatch
// 2,技术员到达时间跟时间窗吻合customerTimeWindowsMatch // 2,技术员时间窗吻合technicianTimeWindowsMatch
// 3,技术员时间窗吻合technicianTimeWindowsMatch // 3,技术员技能跟订单相匹配skillMatch
// 4,技术员技能跟订单相匹配skillMatch
// 5,订单均分technicianBalance
public Constraint greaterThanZero(ConstraintFactory factory) { public Constraint greaterThanZero(ConstraintFactory factory) {
return factory.forEach(Technician.class).filter(technician -> technician.getCustomerList().size() == 0) return factory.forEach(Technician.class).filter(technician -> technician.getCustomerList().size() == 0)
.penalizeLong(HardSoftLongScore.ONE_HARD, technician -> 1).asConstraint("每个技术员至少分配一个单子"); .penalizeLong(HardSoftLongScore.ONE_HARD, technician -> 1).asConstraint("每个技术员至少分配一个单子");
} }
protected Constraint customerTimeWindowsMatch1(ConstraintFactory factory) { protected Constraint customerTimeWindowsMatch(ConstraintFactory factory) {
return factory.forEach(Customer.class).filter( return factory.forEach(Customer.class).filter(
customer -> customer.getTechnician() != null && customer.getArrivalTime() > customer.getEndTime()) customer -> customer.getTechnician() != null && customer.getArrivalTime() > customer.getEndTime())
.penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 1).asConstraint("技术员到达时间跟时间窗吻合1"); .penalizeLong(HardSoftLongScore.ONE_HARD,
// 迟到每2小时扣一分
customer -> (long) Math.ceil((customer.getArrivalTime() - customer.getEndTime()) / 120F))
.asConstraint(ConstraintNameEnum.customerTimeWindowsMatch.name());
} }
protected Constraint customerTimeWindowsMatch2(ConstraintFactory factory) { // protected Constraint customerTimeWindowsMatch1(ConstraintFactory factory) {
// 迟到2小时惩罚 // return factory.forEach(Customer.class).filter(
return factory.forEach(Customer.class).filter( // customer -> customer.getTechnician() != null && customer.getArrivalTime() > customer.getEndTime())
customer -> customer.getTechnician() != null && customer.getArrivalTime() > customer.getEndTime() + 120) // .penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 1).asConstraint("技术员到达时间跟时间窗吻合1");
.penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 1).asConstraint("技术员到达时间跟时间窗吻合2"); // }
} //
// protected Constraint customerTimeWindowsMatch2(ConstraintFactory factory) {
// // 迟到2小时惩罚
// return factory.forEach(Customer.class).filter(
// customer -> customer.getTechnician() != null && customer.getArrivalTime() > customer.getEndTime() + 120)
// .penalizeLong(HardSoftLongScore.ONE_HARD, customer -> 1).asConstraint("技术员到达时间跟时间窗吻合2");
// }
protected Constraint skillMatch(ConstraintFactory factory) { protected Constraint skillMatch(ConstraintFactory factory) {
return factory.forEach(Customer.class) return factory.forEach(Customer.class)
...@@ -54,38 +69,21 @@ public class DispatchConstraintProvider implements ConstraintProvider { ...@@ -54,38 +69,21 @@ public class DispatchConstraintProvider implements ConstraintProvider {
// 3-技能匹配问题1个,时间窗问题8个 // 3-技能匹配问题1个,时间窗问题8个
// 4-技能匹配问题0个,时间窗问题14个 // 4-技能匹配问题0个,时间窗问题14个
customer -> 4) customer -> 4)
.asConstraint("技术员技能跟订单相匹配skillMatch"); .asConstraint(ConstraintNameEnum.skillMatch.name());
}
protected Constraint technicianBalance(ConstraintFactory factory) {
// 会导致剩余单子集中?
return factory.forEach(Technician.class).filter(technician -> technician.getOffWorkTime() > 1140)
.penalizeLong(HardSoftLongScore.ONE_HARD,
technician -> (long) Math.ceil(technician.getOffWorkTime() / 1440f))
.asConstraint("订单均分");
}
protected Constraint technicianBalance2(ConstraintFactory factory) {
// 单量不能过少 FIXME
return factory.forEach(Technician.class).filter(technician -> technician.getOffWorkTime() <= 960)
.penalizeLong(HardSoftLongScore.ONE_HARD, technician -> 1).asConstraint("订单均分2");
} }
// protected Constraint technicianBalance(ConstraintFactory factory) { // protected Constraint technicianBalance(ConstraintFactory factory) {
// return factory.forEachUniquePair(Technician.class, Joiners.filtering( // // 会导致剩余单子集中?
// // 一个人的工作时长超过另外一个人的2倍 FIXME 无效 // return factory.forEach(Technician.class).filter(technician -> technician.getOffWorkTime() > 1140)
// (t1, t2) -> (t1.getWorkTime() > t2.getWorkTime() * 2) || (t2.getWorkTime() > t1.getWorkTime() * 2))) // .penalizeLong(HardSoftLongScore.ONE_HARD,
// .penalizeLong(HardSoftLongScore.ONE_HARD, (c1, c2) -> 1).asConstraint("订单均分"); // technician -> (long) Math.ceil(technician.getOffWorkTime() / 1440f))
// .asConstraint("订单均分");
// } // }
//
// protected Constraint technicianBalance(ConstraintFactory factory) { // protected Constraint technicianBalance2(ConstraintFactory factory) {
// return factory.forEachUniquePair(Technician.class, Joiners.filtering( // // 单量不能过少 FIXME
// // 一个人的数量是另外一个人的3倍 FIXME 无效 // return factory.forEach(Technician.class).filter(technician -> technician.getOffWorkTime() <= 960)
// (t1, t2) -> { // .penalizeLong(HardSoftLongScore.ONE_HARD, technician -> 1).asConstraint("订单均分2");
// int size1 = t1.getCustomerSize();
// int size2 = t2.getCustomerSize();
// return (size1 > size2 * 3) || (size2 > size1 * 3);
// })).penalizeLong(HardSoftLongScore.ONE_HARD, (c1, c2) -> 1).asConstraint("订单均分");
// } // }
// ************************************************************************ // ************************************************************************
...@@ -97,19 +95,21 @@ public class DispatchConstraintProvider implements ConstraintProvider { ...@@ -97,19 +95,21 @@ public class DispatchConstraintProvider implements ConstraintProvider {
protected Constraint totalDistance(ConstraintFactory factory) { protected Constraint totalDistance(ConstraintFactory factory) {
return factory.forEach(Technician.class) return factory.forEach(Technician.class)
.penalizeLong(HardSoftLongScore.ONE_SOFT, Technician::getTotalDistanceMeters).asConstraint("总路程最小"); .penalizeLong(HardSoftLongScore.ONE_SOFT, Technician::getTotalDistanceMeters)
.asConstraint(ConstraintNameEnum.totalDistance.name());
} }
protected Constraint preferredTotalDistance(ConstraintFactory factory) { protected Constraint preferredTotalDistance(ConstraintFactory factory) {
return factory.forEach(Technician.class) return factory.forEach(Technician.class)
.penalizeLong(HardSoftLongScore.ONE_SOFT, Technician::getPreferredTotalDistanceMeters) .penalizeLong(HardSoftLongScore.ONE_SOFT, Technician::getPreferredTotalDistanceMeters)
.asConstraint("技术员中心点偏好"); .asConstraint(ConstraintNameEnum.preferredTotalDistance.name());
} }
protected Constraint technicianBalanceSoft(ConstraintFactory factory) { protected Constraint technicianBalanceSoft(ConstraintFactory factory) {
return factory.forEachUniquePair(Technician.class).penalizeLong(HardSoftLongScore.ONE_SOFT, return factory.forEachUniquePair(Technician.class).penalizeLong(HardSoftLongScore.ONE_SOFT,
// 权重需要调节,差距一个相当于多一公里 FIXME 这里应该是时长均衡,不是订单量均衡 // 权重需要调节,差距一个相当于多一公里 FIXME 这里应该是时长均衡,不是订单量均衡
(a, b) -> Math.abs(a.getCustomerSize() - b.getCustomerSize()) * 1000).asConstraint("订单均分soft"); (a, b) -> Math.abs(a.getCustomerSize() - b.getCustomerSize()) * 1000)
.asConstraint(ConstraintNameEnum.technicianBalanceSoft.name());
} }
} }
package com.dituhui.pea.dispatch.eventListener;
import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
public class DispatchSolverEventListener implements org.optaplanner.core.api.solver.event.SolverEventListener<DispatchSolution> {
@Override
public void bestSolutionChanged(BestSolutionChangedEvent<DispatchSolution> event) {
// TODO Auto-generated method stub
}
}
...@@ -13,6 +13,11 @@ import lombok.Setter; ...@@ -13,6 +13,11 @@ import lombok.Setter;
public class Location { public class Location {
private final long id; private final long id;
// 坐标点
private float x;
private float y;
private String code; private String code;
// 类型 engineer order // 类型 engineer order
private String type; private String type;
...@@ -27,6 +32,12 @@ public class Location { ...@@ -27,6 +32,12 @@ public class Location {
public Location(long id) { public Location(long id) {
this.id = id; this.id = id;
} }
public Location(long id, float x, float y) {
this.id = id;
this.x = x;
this.y = y;
}
public Location(long id, String code, String type, double longitude, double latitude) { public Location(long id, String code, String type, double longitude, double latitude) {
this.id = id; this.id = id;
......
...@@ -19,194 +19,180 @@ import lombok.Data; ...@@ -19,194 +19,180 @@ import lombok.Data;
@PlanningEntity @PlanningEntity
public class Technician { public class Technician {
@PlanningId @PlanningId
private long id; private long id;
private String code; private String code;
@JsonIgnore @JsonIgnore
private Depot depot; private Depot depot;
// 上班时间窗 分钟480-1080 8-18点 // 上班时间窗 分钟480-1080 8-18点
private int startTime; private int startTime;
private int endTime; private int endTime;
// 技能 // 技能
private Set<String> skills; private Set<String> skills;
// 每日最大单量 // 偏好坐标
private int maxCount; private Location preferredlocation;
// technician code : customer code , distance
// 每日最大工作时长 @JsonIgnore
private int maxMinute; private Map<String, Long> preferredlocationDistanceMap = new HashMap<String, Long>();
// 每日最大单量
// 单位是米,这里要注意 private int maxCount;
private int maxDistanceMeter;
// 每日最大工作时长
private int maxMinute;
// 偏好坐标
// private Location preferredlocation; // 单位是米,这里要注意
// teck code : customer code , distance private int maxDistanceMeter;
@JsonIgnore
private Map<String, Long> preferredlocationDistanceMap = new HashMap<String, Long>(); @PlanningListVariable
private List<Customer> customerList = new ArrayList<>();
@PlanningListVariable
private List<Customer> customerList = new ArrayList<>(); public Technician() {
}
public Technician() {
} public Technician(long id, String code, Depot depot, int startTime, int endTime, Set<String> skills,
Map<String, Long> preferredlocationDistanceMap, Location preferredlocation) {
public Technician(long id, String code, Depot depot, int startTime, int endTime, Set<String> skills, this.id = id;
Map<String, Long> preferredlocationDistanceMap) { this.code = code;
this.id = id; this.depot = depot;
this.code = code; this.startTime = startTime;
this.depot = depot; this.endTime = endTime;
this.startTime = startTime; this.skills = skills;
this.endTime = endTime; this.preferredlocationDistanceMap = preferredlocationDistanceMap;
this.skills = skills; this.preferredlocation = preferredlocation;
this.preferredlocationDistanceMap = preferredlocationDistanceMap; }
}
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, Depot depot,
public Technician(long id, String code, int maxCount, int maxMinute, int maxDistanceMeter, int startTime, int endTime, Set<String> skills, Map<String, Long> preferredlocationDistanceMap) {
Depot depot, int startTime, int endTime, Set<String> skills, this.id = id;
Map<String, Long> preferredlocationDistanceMap) { this.code = code;
this.id = id; this.depot = depot;
this.code = code; this.startTime = startTime;
this.depot = depot; this.endTime = endTime;
this.startTime = startTime; this.skills = skills;
this.endTime = endTime; this.maxCount = maxCount;
this.skills = skills; this.maxMinute = maxMinute;
this.maxCount = maxCount; this.maxDistanceMeter = maxDistanceMeter;
this.maxMinute = maxMinute; this.preferredlocationDistanceMap = preferredlocationDistanceMap;
this.maxDistanceMeter = maxDistanceMeter; }
this.preferredlocationDistanceMap = preferredlocationDistanceMap;
} // ************************************************************************
// Complex methods
// ************************************************************************ // ************************************************************************
// Complex methods
// ************************************************************************ /**
* @return route of the vehicle
/** */
* @return route of the vehicle @JsonIgnore
*/ public List<Location> getRoute() {
@JsonIgnore if (customerList.isEmpty()) {
public List<Location> getRoute() { return Collections.emptyList();
if (customerList.isEmpty()) { }
return Collections.emptyList();
} List<Location> route = new ArrayList<Location>();
List<Location> route = new ArrayList<Location>(); route.add(depot.getLocation());
for (Customer customer : customerList) {
route.add(depot.getLocation()); route.add(customer.getLocation());
for (Customer customer : customerList) { }
route.add(customer.getLocation());
} return route;
}
return route;
} /**
* 总路线距离
/** *
* 总路线距离 * @return
* */
* @return public long getTotalDistanceMeters() {
*/ if (customerList.isEmpty()) {
public long getTotalDistanceMeters() { return 0;
if (customerList.isEmpty()) { }
return 0;
} long totalDistance = 0;
Location previousLocation = depot.getLocation();
long totalDistance = 0;
Location previousLocation = depot.getLocation(); for (Customer customer : customerList) {
totalDistance += previousLocation.getDistanceTo(customer.getLocation());
for (Customer customer : customerList) { previousLocation = customer.getLocation();
totalDistance += previousLocation.getDistanceTo(customer.getLocation()); }
previousLocation = customer.getLocation(); totalDistance += previousLocation.getDistanceTo(depot.getLocation());
}
totalDistance += previousLocation.getDistanceTo(depot.getLocation()); return totalDistance;
}
return totalDistance;
} /**
* 获取偏好总距离 所有customer与该技术员的PreferredLocation距离之和
*
/** * @return
* 获取偏好总距离 所有customer与该技术员的PreferredLocation距离之和 */
* public long getPreferredTotalDistanceMeters() {
* @return if (customerList.isEmpty()) {
*/ return 0;
public long getPreferredTotalDistanceMeters() { }
if (customerList.isEmpty()) {
return 0; long totalDistance = 0;
} for (Customer customer : customerList) {
totalDistance += preferredlocationDistanceMap.get(customer.getCode());
long totalDistance = 0; }
for (Customer customer : customerList) { return totalDistance;
totalDistance += preferredlocationDistanceMap.get(customer.getCode()); }
}
return totalDistance; public int getCustomerSize() {
} return customerList.size();
}
public int getCustomerSize() {
return customerList.size(); /**
} * 获取总上班时间,第一个订单到最后一个订单时间跨度
*
/** * @return
* 获取总上班时间,第一个订单到最后一个订单时间跨度 */
* public int getWorkTime() {
* @return int size = customerList.size();
*/ if (0 == size) {
public int getWorkTime() { return 0;
int size = customerList.size(); } else {
if (0 == size) { return customerList.get(size - 1).getArrivalTime() + customerList.get(size - 1).getServiceDuration()
return 0; - customerList.get(0).getArrivalTime();
} else { }
return customerList.get(size - 1).getArrivalTime() + customerList.get(size - 1).getServiceDuration() }
- customerList.get(0).getArrivalTime();
} /**
} * 获取下班时间,最后一个订单完成时间
*
* @return
/** */
* 获取下班时间,最后一个订单完成时间 public int getOffWorkTime() {
* int size = customerList.size();
* @return if (0 == size) {
*/ return 0;
public int getOffWorkTime() { } else {
int size = customerList.size(); Customer lastCustomer = customerList.get(size - 1);
if (0 == size) { return lastCustomer.getDepartureTime();
return 0; }
} else { }
Customer lastCustomer = customerList.get(size - 1);
return lastCustomer.getDepartureTime(); @Override
} public int hashCode() {
} return Long.valueOf(this.id).hashCode();
}
@Override
@Override public boolean equals(Object obj) {
public int hashCode() { if (obj == null)
return Long.valueOf(this.id).hashCode(); return false;
} if (!(obj instanceof Technician))
return false;
@Override if (obj == this)
public boolean equals(Object obj) { return true;
if (obj == null) return this.id == ((Technician) obj).getId();
return false; }
if (!(obj instanceof Technician))
return false; @Override
if (obj == this) public String toString() {
return true; return "Technician{" + "id=" + id + ", code='" + code + '\'' + ", depot=" + depot + ", startTime=" + startTime
return this.id == ((Technician) obj).getId(); + ", endTime=" + endTime + ", skills=" + skills + ", maxCount=" + maxCount + ", maxMinute=" + maxMinute
} + ", maxDistanceMeter=" + maxDistanceMeter + '}';
}
@Override
public String toString() {
return "Technician{" +
"id=" + id +
", code='" + code + '\'' +
", depot=" + depot +
", startTime=" + startTime +
", endTime=" + endTime +
", skills=" + skills +
", maxCount=" + maxCount +
", maxMinute=" + maxMinute +
", maxDistanceMeter=" + maxDistanceMeter +
'}';
}
} }
...@@ -26,7 +26,7 @@ spring: ...@@ -26,7 +26,7 @@ spring:
enabled: false enabled: false
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.10.0.116:3306/saas_aftersale_test?serverTimezone=Asia/Shanghai url: jdbc:mysql://10.10.0.54:3306/saas_aftersale_test?serverTimezone=Asia/Shanghai
username: root username: root
password: 123456 password: 123456
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource
......
depot,"120.614853,31.338807"
106330400,"120.982906,31.440902"
106632743,"120.726916,31.592422"
106632993,"120.868246,31.041383"
106704506,"120.625144,31.257837"
106729637,"120.959821,31.402083"
106732179,"121.077572,31.563578"
106737923,"120.622214,31.333417"
106758769,"120.731802,31.300809"
106765790,"120.847507,31.42479"
106790276,"120.606255,31.268432"
106798348,"120.959821,31.402083"
106845767,"120.687214,31.253326"
106848936,"120.679366,31.287809"
106854117,"120.664649,31.316367"
106855369,"120.616312,31.385716"
106865528,"120.623197,31.470323"
106867266,"120.597917,31.348755"
106879505,"120.765083,31.697774"
106882180,"120.664487,31.282887"
106893582,"121.107535,31.479541"
106228451,"120.721813,31.303003"
106660472,"120.611127,31.250817"
106684680,"120.626474,31.224566"
106703025,"120.530752,31.281024"
106762173,"120.606634,31.292016"
106802404,"120.654614,31.098804"
106820041,"120.719909,31.382979"
106820735,"120.58446,31.322863"
106824266,"120.697424,31.344814"
106828070,"120.596177,31.283586"
106833813,"120.523634,31.858481"
106835282,"121.039728,31.400563"
106853363,"121.104428,31.453363"
106856986,"120.745434,31.304664"
106861402,"120.785598,31.097726"
106863823,"120.966152,31.389332"
106870737,"120.612394,31.194301"
106870765,"120.451477,31.938889"
106871040,"120.686309,31.327041"
106872591,"120.591992,31.292224"
106872658,"120.768159,31.337805"
106875426,"120.64272,31.277918"
106890613,"120.526677,31.863775"
106893208,"120.748525,31.324368"
106279591,"120.982906,31.440902"
106704564,"120.625144,31.257837"
106729640,"120.959821,31.402083"
106732181,"121.077572,31.563578"
106738036,"120.622214,31.333417"
106758988,"120.731802,31.300809"
106765761,"120.847507,31.42479"
106818668,"120.493618,31.24607"
106823211,"120.651719,31.278747"
106824512,"120.754292,31.337413"
106836405,"120.665415,31.32013"
106841840,"120.615365,31.186788"
106851428,"120.98225,31.417492"
106852112,"120.654228,31.337012"
106853178,"120.632811,31.173964"
106854359,"120.84248,31.284855"
106855674,"120.743073,31.677162"
106856390,"120.655769,31.393115"
106859149,"120.724624,31.487646"
106859862,"120.549426,31.376686"
106860870,"120.748602,31.317585"
106862449,"120.777722,31.636159"
106865892,"120.664487,31.282887"
106866730,"120.606376,31.391923"
106868422,"120.702768,31.333509"
106870053,"120.732045,31.30597"
106873767,"120.964531,31.349257"
106873992,"120.574297,31.321478"
106876557,"120.721418,31.487407"
106876682,"121.088316,31.5656"
106881724,"120.601868,31.148427"
106883231,"121.120276,31.465135"
106883391,"120.861145,31.080919"
106888947,"120.628533,31.368943"
106889389,"120.680358,31.325299"
106893581,"121.107535,31.479541"
106776742,"120.70851,31.248646"
106820178,"120.678376,31.149701"
106826035,"120.49468,31.996066"
106835053,"120.600039,31.295101"
106839639,"120.883187,31.287842"
106840295,"120.526884,31.373801"
106848186,"121.135103,31.441649"
106869743,"120.938132,31.396139"
106871570,"120.634245,31.188141"
106871918,"120.64205,31.296388"
106872059,"120.589301,31.3104"
106874727,"120.748439,31.684466"
106879292,"120.715751,31.29945"
106881156,"120.566187,31.855965"
106884799,"120.605229,31.394392"
106886425,"120.548647,31.842596"
106890832,"120.604399,31.349966"
106576847,"121.106111,31.480717"
106704602,"120.625144,31.257837"
106729639,"120.959821,31.402083"
106732180,"121.077572,31.563578"
106738035,"120.622214,31.333417"
106853161,"120.632811,31.173964"
106855710,"120.743073,31.677162"
106856389,"120.655769,31.393115"
106859148,"120.724624,31.487646"
106860869,"120.748602,31.317585"
106862447,"120.777722,31.636159"
106866729,"120.606376,31.391923"
106868053,"120.606634,31.292016"
106888946,"120.628533,31.368943"
106854169,"120.634382,31.374593"
106870713,"120.633518,31.153463"
106891293,"120.636053,31.289328"
106549827,"121.144142,31.422735"
106765447,"120.616316,31.402105"
106781641,"120.606019,31.398212"
106793056,"120.747643,31.310646"
106803268,"120.607062,31.398126"
106818600,"121.144926,31.461553"
106822716,"120.705113,31.257879"
106829323,"120.673489,31.297058"
106840366,"120.649177,31.303238"
106847687,"120.530373,31.853617"
106866881,"120.629969,31.270552"
106870492,"120.650458,31.225728"
106871200,"120.59747,31.285217"
106872822,"120.616482,31.402948"
106878500,"120.606019,31.398212"
106880124,"120.615837,31.316617"
106880969,"120.677186,31.149534"
106892125,"120.587065,31.283529"
106763043,"120.687676,31.443469"
106801344,"120.69381,31.304455"
106806874,"120.650618,31.176803"
106824742,"120.77035,31.312694"
106858813,"120.769821,31.342065"
106870383,"120.624333,31.361689"
106632622,"120.726916,31.592422"
106744960,"121.077777,31.560303"
106809648,"120.956653,31.459789"
106862554,"120.685176,31.471252"
106649991,"120.632112,31.286827"
106753590,"120.597212,31.294007"
106845946,"120.747488,31.340815"
106859379,"120.756575,31.336275"
106632543,"120.726916,31.592422"
106632849,"120.726916,31.592422"
106665842,"120.64402,31.77468"
106730931,"120.639926,31.306425"
106744959,"121.077777,31.560303"
106809662,"120.956653,31.459789"
106813589,"120.844551,31.013903"
106862553,"120.685176,31.471252"
106871868,"120.686193,31.327165"
106879520,"120.614752,31.309742"
106694001,"120.632112,31.286827"
106828628,"120.77099,31.682885"
106834398,"120.761397,31.311754"
106886055,"120.598933,31.297181"
106665727,"120.64402,31.77468"
106665740,"120.64402,31.77468"
106704534,"120.625144,31.257837"
106809681,"120.956653,31.459789"
106862552,"120.685176,31.471252"
106884214,"120.620985,31.129614"
106884485,"120.485939,31.334295"
106885700,"120.727943,31.254621"
106887147,"120.790707,31.683097"
106754377,"120.639734,31.32923"
106775190,"120.612111,31.189908"
106849556,"120.605103,31.296943"
106867319,"120.696831,31.275636"
106888973,"120.898629,31.749332"
106889146,"120.60652,31.404533"
package com.dituhui.pea.dispatch.test;
import java.io.FileNotFoundException;
import java.io.UncheckedIOException;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.impl.solver.DefaultSolverFactory;
import com.dituhui.pea.dispatch.eventListener.DispatchSolverEventListener;
import com.dituhui.pea.dispatch.pojo.DispatchSolution;
import com.dituhui.pea.dispatch.utils.DataUtils;
import com.dituhui.pea.dispatch.utils.DispatchSolutionUtils;
public class Test {
public static void main(String[] args) throws UncheckedIOException, FileNotFoundException {
// 创建解决方案对象
DispatchSolution problem = DataUtils.getInitialProblem();
// 创建求解器
DefaultSolverFactory<DispatchSolution> solverFactory = DispatchSolutionUtils.getSolverFactory(3, 30);
Solver<DispatchSolution> solver = solverFactory.buildSolver();
// 得分监听器
solver.addEventListener(new DispatchSolverEventListener());
// 求解
DispatchSolution solution = solver.solve(problem);
// 打印和输出solution
DispatchSolutionUtils.printSolution(solution);
// DispatchSolutionUtils.explainSolutionConstraintDetail(solution);
// DispatchSolutionUtils.exportSolutionJson(solution, "dispatchSolution.json");
// DispatchSolutionUtils.exportMapHtml(solution, "dispatchMap" +
// System.currentTimeMillis());
// FIXME
DispatchSolution movedSolution = DispatchSolutionUtils.moveCustomer(solverFactory, solution,
solution.getTechnicianList().get(0), 0, 1);
DispatchSolutionUtils.printSolution(movedSolution);
}
}
...@@ -26,7 +26,7 @@ spring: ...@@ -26,7 +26,7 @@ spring:
# - optional:nacos:datasource-config.yaml # - optional:nacos:datasource-config.yaml
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.10.0.116:3306/saas_aftersale_test?serverTimezone=Asia/Shanghai url: jdbc:mysql://10.10.0.54:3306/saas_aftersale_test?serverTimezone=Asia/Shanghai
username: root username: root
password: 123456 password: 123456
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!