doum
11 小时以前 d8b9d884967f502caac58d723e57931d0356fa11
最新版本541200007
已添加3个文件
已修改5个文件
610 ■■■■■ 文件已修改
server/system_service/src/main/java/com/doumee/core/utils/Constants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/ClusterCustomKMeans.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/Clustering.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/TspSolver.java 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/dao/admin/request/SketchCateModel.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/JkSketchService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchServiceImpl.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/system_service/src/main/java/com/doumee/core/utils/Constants.java
@@ -57,6 +57,7 @@
    public static final String SIGN_IN_QRCODE ="SIGN_IN_QRCODE" ;
    public static final String SIGN_IN_ATTENTION ="SIGN_IN_ATTENTION" ;
    public static final String SIGN_IN_QRCODE_PREFIX ="SIGN_IN_QRCODE_PREFIX" ;
    public static final String JK_PLAN_MAX_CLUSTER ="JK_PLAN_MAX_CLUSTER" ;
    /**
     * æ“ä½œç±»åž‹ï¼Œç”¨äºŽåšæŽ¥å£éªŒè¯åˆ†ç»„
server/visits/dmvisit_service/pom.xml
@@ -40,6 +40,11 @@
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.doumee</groupId>
            <artifactId>emaysms</artifactId>
            <version>1.0.0-SNAPSHOT</version>
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/ClusterCustomKMeans.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package com.doumee.core.tsp;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.doumee.core.utils.Constants;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.model.JkSketchCustomer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.ml.clustering.*;
import org.apache.commons.math3.ml.distance.EuclideanDistance;
/**
 *
 * @author xjx
 *
 */
@Slf4j
public class ClusterCustomKMeans{
    //欧式距离
        public  List<SketchCateModel> clusterPoints(List<JkSketchCustomer> customers, int k, int d){
            List<SketchCateModel> list = new ArrayList<>();
            if(customers.size() <= k){
                int index =0;
                for (JkSketchCustomer c : customers) {
                    SketchCateModel sketchCateModel = new SketchCateModel();
                    sketchCateModel.setId(index);
                    List<JkSketchCustomer> cList = new ArrayList<>();
                    cList.add(c);
                    sketchCateModel.setTotalNum(c.getTotalNum());
                    sketchCateModel.setCustomerList(cList);
                    sketchCateModel.setStartPoint(c);
                    sketchCateModel.setTotalCustomer(1);
                    list.add(sketchCateModel);
                    log.error("Cluster " + index + "members:"+1+"/200 "+" å®¢æˆ·ï¼š"+   c.getName());
                    index ++;
                }
                return list;
            }
            List<DoublePointNew> points = new ArrayList<>();
            for (int i = 0; i < customers.size(); i++) {
                DoublePointNew p = new DoublePointNew(new double[]{customers.get(i).getLatitude().doubleValue(), customers.get(i).getLongitude().doubleValue()});
                p.setCustomer(customers.get(i));
                points.add(p); // ç¤ºä¾‹ç‚¹1
            }
            KMeansPlusPlusClusterer<DoublePointNew> clusterer = new KMeansPlusPlusClusterer<>(k, d, new EuclideanDistance());
            List<CentroidCluster<DoublePointNew>> clusters = clusterer.cluster(points);
            int index  = 0;
            int maxMembersPerCluster = 200; // è®¾ç½®æ¯ä¸ªç°‡çš„æœ€å¤§æˆå‘˜æ•°
            List<CentroidCluster<DoublePointNew>> allList = new ArrayList<>();
            for (CentroidCluster<DoublePointNew> cluster : clusters) {
                if(cluster.getPoints() ==null || cluster.getPoints().size() ==0){
                    log.error("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200  æ— æ•°æ®");
                    continue;
                }
                log.error("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200 "+"中心点:"+ Arrays.toString(cluster.getCenter().getPoint())+"中心点:"+ cluster.getPoints().get(0).getCustomer().getName());
                if (cluster.getPoints().size() > maxMembersPerCluster) {
                    // è¿™é‡Œå¯ä»¥é‡æ–°è¿è¡Œèšç±»ç®—法或者手动调整簇的分配策略
                    log.error("Cluster " + clusters.indexOf(cluster) + " exceeds maximum members:" + cluster.getPoints().size() + "/200, re-clustering needed.");
                    // å¯ä»¥è€ƒè™‘再次调用KMeansPlusPlusClusterer或其它逻辑来处理超限情况。
                }
                allList.add(cluster);
            }
            for (CentroidCluster<DoublePointNew> cluster : allList) {
//                System.out.println("Cluster " + clusters.indexOf(cluster) + "members:"+cluster.getPoints().size()+"/200 ");
                if (cluster.getPoints().size() > maxMembersPerCluster) {
                    // è¿™é‡Œå¯ä»¥é‡æ–°è¿è¡Œèšç±»ç®—法或者手动调整簇的分配策略
//                    System.out.println("Cluster " + clusters.indexOf(cluster) + " exceeds maximum members:"+cluster.getPoints().size()+"/200, re-clustering needed.");
                    // å¯ä»¥è€ƒè™‘再次调用KMeansPlusPlusClusterer或其它逻辑来处理超限情况。
                }
                SketchCateModel sketchCateModel = new SketchCateModel();
                sketchCateModel.setId(index);
                List<JkSketchCustomer> cList = new ArrayList<>();
                for(DoublePointNew doublePointNew:cluster.getPoints()){
                    cList.add(doublePointNew.getCustomer());
                    sketchCateModel.setTotalNum(Constants.formatBigdecimal(sketchCateModel.getTotalNum()).add(Constants.formatBigdecimal(doublePointNew.getCustomer().getTotalNum())));
                }
                sketchCateModel.setCustomerList(cList);
                sketchCateModel.setStartPoint(cList.get(0));
                sketchCateModel.setTotalCustomer(cList.size());
                list.add(sketchCateModel);
            }
            return list;
        }
    public  class  DoublePointNew extends  DoublePoint{
        private JkSketchCustomer customer;
        public DoublePointNew(double[] point) {
            super(point);
        }
        public JkSketchCustomer getCustomer() {
            return customer;
        }
        public void setCustomer(JkSketchCustomer customer) {
            this.customer = customer;
        }
    }
    public static void main(String[] args) {
        List<JkSketchCustomer> points = new ArrayList<>();
        for (int i = 0; i <10; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(30.19d ));
            a.setLongitude(new BigDecimal(117.40 ));
            a.setName("客户"+i);
            points.add(a);
        }
        for (int i = 0; i <10; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(31.19d ));
            a.setLongitude(new BigDecimal(118.40 ));
            a.setName("客户"+i);
            points.add(a);
        }
         for (int i = 10; i <3000; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(31.19d-(0.1d)*Math.random()));
            a.setLongitude(new BigDecimal(118.40-(0.2d)*Math.random()));
            a.setName("客户"+i);
            points.add(a);
        }
        double threshold = 1000; // è®¾ç½®è·ç¦»é˜ˆå€¼ï¼Œè¶…过这个距离就不属于同一聚类。
        (new ClusterCustomKMeans()).clusterPoints(points, 100,1);
    }
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/Clustering.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.doumee.core.tsp;
/**
 * èšç±»åˆ†ç»„
 */
import com.doumee.core.utils.Constants;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.model.JkSketchCustomer;
import com.doumee.service.business.impl.JkSketchServiceImpl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class Clustering {
    public static  List<SketchCateModel>  clusterPoints(List<JkSketchCustomer> points, double threshold) {
        List<SketchCateModel> clusters = new ArrayList<>();
        boolean[] visited = new boolean[points.size()];
        int index =0;
        for (int i = 0; i < points.size(); i++) {
            if (!visited[i]) {
                List<JkSketchCustomer> cluster = new ArrayList<>();
                dfs(points, visited, cluster, i, threshold);
                SketchCateModel sketchCateModel = new SketchCateModel();
                sketchCateModel.setCustomerList(cluster);
                sketchCateModel.setId(index);
                sketchCateModel.setStartPoint(cluster.get(0));
                for (JkSketchCustomer c : cluster){
                    sketchCateModel.setTotalNum(Constants.formatBigdecimal(sketchCateModel.getTotalNum()).add(Constants.formatBigdecimal(c.getTotalNum())));
                }
                sketchCateModel.setTotalCustomer(cluster.size());
                clusters.add(sketchCateModel);
            }
        }
        // æ‰“印每个聚类的点
        for (int i = 0; i < clusters.size(); i++) {
            System.out.println("Cluster " + (i + 1) + ": " + clusters.get(i).getStartPoint().getName()+ ": " + clusters.get(i).getCustomerList().size());
        }
        return  clusters;
    }
    public static double distanceTo(JkSketchCustomer self, JkSketchCustomer other) {
        List<DistanceMapParam> distanceMapParamList  =JkSketchServiceImpl.getListFromJsonStr(self.getDistanceJson());
        DistanceMapParam param = JkSketchServiceImpl.getParamByCustomerIds( other.getId(),distanceMapParamList);
        if(param!=null && param.getDistance()!=0){//如果之前已经获取过
          return (param.getDistance());
        }
        return DistanceCalculator.calculateDistance(Constants.formatBigdecimal(self.getLatitude()).doubleValue()
                ,Constants.formatBigdecimal(self.getLongitude()).doubleValue()
                ,Constants.formatBigdecimal(other.getLatitude()).doubleValue()
                ,Constants.formatBigdecimal(other.getLongitude()).doubleValue());
    }
    private static void dfs(List<JkSketchCustomer> points, boolean[] visited, List<JkSketchCustomer> cluster, int startIndex, double threshold) {
        visited[startIndex] = true;
        cluster.add(points.get(startIndex));
        JkSketchCustomer startPoint = points.get(startIndex);
        for (int i = 0; i < points.size(); i++) {
            if (!visited[i]) {
                double distance = distanceTo(startPoint,points.get(i));
                if (distance <= threshold) {
                    dfs(points, visited, cluster, i, threshold); // é€’归添加到聚类中
                }
            }
        }
    }
    /**
     * 117°40′~118°44′、北纬30°19′~31°34′
     * @param args
     */
    public static void main(String[] args) {
        List<JkSketchCustomer> points = new ArrayList<>();
        for (int i = 0; i <3000; i++) {
            JkSketchCustomer a = new JkSketchCustomer();
            a.setLatitude(new BigDecimal(30.19d+(30.54d-30.19d)*Math.random()));
            a.setLongitude(new BigDecimal(117.40+(117.74d-117.40d)*Math.random()));
            a.setName("客户"+i);
            points.add(a);
        }
        double threshold = 1000; // è®¾ç½®è·ç¦»é˜ˆå€¼ï¼Œè¶…过这个距离就不属于同一聚类。
        clusterPoints(points, threshold);
    }
}
server/visits/dmvisit_service/src/main/java/com/doumee/core/tsp/TspSolver.java
@@ -25,7 +25,7 @@
        data.initDataList();//构造数据
        long start =System.currentTimeMillis();
        System.out.println("=============start=========="+start);
       log.error("=============start=========="+start);
        //创建求解器manager对象,初始化求解器数据
        RoutingIndexManager manager =  new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);
@@ -71,15 +71,15 @@
        // æ‰“印路线
        printSolution(data, routing, manager, solution);
        long end =System.currentTimeMillis();
        System.out.println("=============end=========="+end);
       log.error("=============end=========="+end);
        System.out.println("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
       log.error("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
    */}
    public static void startSearch(DataModel data) {
        // åˆå§‹åŒ–数据模型
        Loader.loadNativeLibraries();
        long start =System.currentTimeMillis();
        System.out.println("=============start=========="+start);
       log.error("开始规划=============start=========="+start);
        //创建求解器manager对象,初始化求解器数据
        RoutingIndexManager manager =  new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot);
@@ -87,7 +87,7 @@
        RoutingModel routing = new RoutingModel(manager);
        // æ³¨å†Œå›žè°ƒå‡½æ•°
       /* final int transitCallbackIndex =
       final int transitCallbackIndex =
                routing.registerTransitCallback((long fromIndex, long toIndex) -> {
                    int fromNode = manager.indexToNode(fromIndex);
                    int toNode = manager.indexToNode(toIndex);
@@ -96,15 +96,16 @@
        // å®šä¹‰å›žè°ƒå‡½æ•°è‡³æ¯æ¡è·¯çº¿
        routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
        routing.addDimension(transitCallbackIndex, 0, 30000000,
  /*      routing.addDimension(transitCallbackIndex, 0, 30000000,
                true,
                "Distance");
        RoutingDimension distanceDimension = routing.getMutableDimension("Distance");
        distanceDimension.setGlobalSpanCostCoefficient(100);*/
        distanceDimension.setGlobalSpanCostCoefficient(100); */
        // æ³¨å†Œå›žè°ƒå‡½æ•°
        final int transitCallbackIndex1 =
                routing.registerTransitCallback((long fromIndex, long toIndex) -> {
                    return 1;
                    int fromNode = manager.indexToNode(fromIndex);
                    return data.customerDemands[fromNode];
                });
        for (int d = 0; d < data.vehicleMaxNodes.length; d++) {
            // å¢žåŠ è·ç¦»ç»´åº¦çº¦æŸ
@@ -121,20 +122,12 @@
        });
        routing.addDimensionWithVehicleCapacity(demandCallbackIndex, 0, data.vehicleCapacities, true, "Capacity");
/*
        // æ·»åŠ å®¹é‡é™åˆ¶
        final int demandCallbackIndex1 = routing.registerUnaryTransitCallback((long fromIndex) -> {
            return 1;
        });
        routing.addDimensionWithVehicleCapacity(demandCallbackIndex1, 0, data.vehicleMaxNodes, true, "Capacity");
*/
        Solver solver = routing.solver();
        //设置搜索方法(
        RoutingSearchParameters searchParameters =
                main.defaultRoutingSearchParameters()
                        .toBuilder()
//                        .setTimeLimit(Duration.newBuilder().setSeconds(60*60).build())//最久1小时
                        .setTimeLimit(Duration.newBuilder().setSeconds(60*60*6).build())//最久1小时
                        .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC)
                        .build();
@@ -146,8 +139,7 @@
        // æ‰“印路线
        resultSolution(data, routing, manager, solution);
        long end =System.currentTimeMillis();
        System.out.println("=============end=========="+end);
        System.out.println("=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
       log.error("规划结束=============耗时=========="+(end -start)+"(ms)"+(end -start)/1000 +"s"+(end -start)/60/1000 +"m");
    }
    static void resultSolution( DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) {
        long maxRouteDistance = 0;
@@ -223,6 +215,7 @@
        //每一个点的商品的数量
        public   long[] demands;
        public   long[] customerDemands;
        //车辆最大容载
        public   long[] vehicleCapacities ;
        public   long[] vehicleMaxNodes ;
@@ -236,17 +229,19 @@
           this.solutions = solutions;
       }
       public  void initDataInfo(int vehicleNumber1, long[] demands1, long[] vehicleCapacities1, long[][] distanceMatrix1,long[] vehicleMaxNodes){
       public  void initDataInfo(int vehicleNumber1, long[] demands1,long[] demands2, long[] vehicleCapacities1, long[][] distanceMatrix1,long[] vehicleMaxNodes){
            this.demands = demands1;
            this.customerDemands = demands2;
            this.vehicleNumber = vehicleNumber1;
            this.vehicleCapacities=vehicleCapacities1;
            this.distanceMatrix=distanceMatrix1;
            this.vehicleMaxNodes =vehicleMaxNodes;
        }
        public   void initDataList(){
            lenght = 20;
            lenght = 10;
            vehicleNumber = 5;
            demands = new long[lenght];
            customerDemands = new long[lenght];
            vehicleCapacities =new long[vehicleNumber];
            vehicleMaxNodes =new long[vehicleNumber];
            distanceMatrix =  new long[lenght][lenght];
@@ -254,15 +249,16 @@
            for (int i = 0; i <vehicleNumber ; i++) {
                long tem = (long) (Math.random() * 1000 + 20000);
                vehicleCapacities[i] = tem;
                vehicleMaxNodes[i] =5;
                vehicleMaxNodes[i] =50;
                total0+=tem;
                System.out.print(tem+" ,");
            }
            System.out.println( "\ntotal Capacity:"+total0+"=====================");
           log.error( "\ntotal Capacity:"+total0+"=====================");
            long total = 0;
            for (int i = 0; i <lenght ; i++) {
                long tem =  (int)(Math.random()*100+100);
                demands[i] =tem;
                customerDemands[i] =1;
                total+=tem;
                System.out.print(tem+" ,");
                for (int j = 0; j <lenght ; j++) {
@@ -276,7 +272,7 @@
                }
            }
            System.out.println( "\ntotal Demands:"+total+"=====================");
           log.error( "\ntotal Demands:"+total+"=====================");
        }
       /* public final long[][] distanceMatrix = {
                {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662},
server/visits/dmvisit_service/src/main/java/com/doumee/dao/admin/request/SketchCateModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.doumee.dao.admin.request;
import com.baomidou.mybatisplus.annotation.TableField;
import com.doumee.core.annotation.excel.ExcelColumn;
import com.doumee.core.tsp.DistanceMapParam;
import com.doumee.dao.business.model.JkSketchCustomer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
 * å‘˜å·¥ä¿¡æ¯å¯¼å…¥è¡¨
 * @author æ±Ÿè¹„蹄
 * @date 2024/01/16 10:03
 */
@Data
@ApiModel("线路规划聚类分组集合")
public class SketchCateModel {
    private int id;
    private JkSketchCustomer startPoint;
    //中心点纬度
    private BigDecimal latitude;
    //中心点经度
    private BigDecimal longitude;
    @ApiModelProperty(value = "送货量", example = "1")
    private BigDecimal totalNum;
    @ApiModelProperty(value = "总客户数", example = "1")
    private int totalCustomer;
    //点集合
    private  List<JkSketchCustomer> customerList;
}
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/JkSketchService.java
@@ -108,6 +108,7 @@
    List<JkSketch> importBatch(MultipartFile file, String dateInfo, LoginUserInfo loginUser);
    void startUpdateLineAsyncOld(JkSketch model);
    void startUpdateLineAsync(JkSketch model);
    void distanceCustomer(Category model );
@@ -119,7 +120,7 @@
    void startEditSketchLineAsync(JkSketch model);
    void startInitOriginDistanceBatch(JkSketch list);
    void startEditSketchLineAsyncOld(JkSketch model);
    /**
     * è½¦è¾†çº¿è·¯  - ç”µä¿¡ä½¿ç”¨
server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/JkSketchServiceImpl.java
@@ -12,6 +12,7 @@
import com.doumee.core.wms.model.response.WmsBaseDataResponse;
import com.doumee.core.wms.model.response.WmsBaseResponse;
import com.doumee.dao.admin.request.JkOrdersImport;
import com.doumee.dao.admin.request.SketchCateModel;
import com.doumee.dao.business.*;
import com.doumee.dao.business.dto.TelecomLineInfoDTO;
import com.doumee.dao.business.model.*;
@@ -508,6 +509,69 @@
    @Async
    public void startUpdateLineAsync(JkSketch model) {
        try {
            int maxnum =100;
            try {
                maxnum = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_MAX_CLUSTER).getCode());
            }catch (Exception e){
            }
            List<JkSketchCustomer> allCustomerList = model.getCustomerList();
            //两公里内数据客户合并
//            List<SketchCateModel> customerList =Clustering.clusterPoints(allCustomerList,1000);
            List<SketchCateModel> customerList = new ClusterCustomKMeans().clusterPoints(allCustomerList,maxnum,500);
            List<JkLine> lineList = model.getLineList();
            TspSolver.DataModel dataModel = new TspSolver.DataModel();
            int vehicleNumber1 = lineList.size();//线路数量
            long[] vehicleCapacities1=new long[lineList.size()];//每辆车的最大订单量限制
            long[] vehicleMaxNodes=new long[lineList.size()];//每辆车的最大订单量限制
            long[] demands1 = new long[customerList.size()+1]; //各个点的订单量
            long[] demands2 = new long[customerList.size()+1]; //各个点的客戶量
            long[][] distanceMatrix1 = new long[customerList.size()+1][customerList.size()+1];
            distanceMatrix1[0][0] = 0;
            demands1[0] =0;//原点
            demands2[0] =0;//原点
            for (int i = 0; i < customerList.size(); i++) {
                List<DistanceMapParam>  disList =  customerList.get(i).getStartPoint().getDistanceMapParamList();
                distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                demands1[i+1] = Constants.formatBigdecimal( customerList.get(i).getTotalNum()).longValue();  //各个点的订单量
                demands2[i+1] = customerList.get(i).getTotalCustomer();  //各个点的订单量
                for (int j = 0; j < customerList.size(); j++) {
                    distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                }
            }
            for (int i = 0; i < lineList.size(); i++) {
                vehicleCapacities1[i] = lineList.get(i).getMaxOrder();//每辆车的最大订单量限制
                vehicleMaxNodes[i] =lineList.get(i).getMaxCustomer();//最大客户数
            }
            //构造优化数据模型
            dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            TspSolver.startSearch(dataModel);
            dealSearchSolution(model,  customerList,dataModel);
        }catch (Exception e){
            e.printStackTrace();
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化失败!")
                    .set(JkSketch::getStatus,Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,new Date()));
        }
    }
    private List<JkSketchCustomer> getCustomerCateList(List<JkSketchCustomer> allCustomerList) {
        List<JkSketchCustomer>  list = new ArrayList<>();
        return  list;
    }
    /**
     * å¼€å§‹å¼‚步执行线路优化任务
     * @param model
     */
    @Override
    @Async
    public void startUpdateLineAsyncOld(JkSketch model) {
        try {
            List<JkSketchCustomer> customerList = model.getCustomerList();
            List<JkLine> lineList = model.getLineList();
            TspSolver.DataModel dataModel = new TspSolver.DataModel();
@@ -515,6 +579,7 @@
            long[] vehicleCapacities1=new long[lineList.size()];//每辆车的最大订单量限制
            long[] vehicleMaxNodes=new long[lineList.size()];//每辆车的最大订单量限制
            long[] demands1 = new long[customerList.size()+1]; //各个点的订单量
            long[] demands2 = new long[customerList.size()+1]; //各个点的客户量
            long[][] distanceMatrix1 = new long[customerList.size()+1][customerList.size()+1];
            distanceMatrix1[0][0] = 0;
            demands1[0] =0;//原点
@@ -523,6 +588,7 @@
                distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                demands1[i+1] = Constants.formatBigdecimal( customerList.get(i).getTotalNum()).longValue();  //各个点的订单量
                demands2[i+1] =1;  //各个点的订单量
                for (int j = 0; j < customerList.size(); j++) {
                    distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                }
@@ -542,9 +608,9 @@
                vehicleMaxNodes[i] =lineList.get(i).getMaxCustomer();//最大客户数
            }
            //构造优化数据模型
            dataModel.initDataInfo(vehicleNumber1,demands1,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
            TspSolver.startSearch(dataModel);
            dealSearchSolution(model,dataModel);
            dealSearchSolutionOld(model,dataModel);
        }catch (Exception e){
            e.printStackTrace();
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
@@ -563,6 +629,126 @@
    @Override
    @Async
    public  void startEditSketchLineAsync(JkSketch model) {
        boolean success = true;
        int totalDistance = 0;
        List<JkSketchLine> lineList = model.getSketchLineList();
        try {
            MPJLambdaWrapper<JkSketchCustomer> queryWrapper = new MPJLambdaWrapper<>();
            queryWrapper.selectAll(JkSketchCustomer.class )
                    .selectAs(JkCustomer::getName,JkSketchCustomer::getName)
                    .selectAs(JkCustomer::getCode,JkSketchCustomer::getCode)
                    .selectAs(JkCustomer::getDistance,JkSketchCustomer::getDistanceJson)
                    .selectAs(JkCustomer::getLongitude,JkSketchCustomer::getLongitude)
                    .selectAs(JkCustomer::getLatitude,JkSketchCustomer::getLatitude)
                    .selectAs(JkCustomer::getStartDistance,JkSketchCustomer::getStartDistance)
                    .selectAs(JkCustomer::getEndDistance,JkSketchCustomer::getEndDistance)
                    .leftJoin(JkCustomer.class,JkCustomer::getId,JkSketchCustomer::getCustomerId )
                    .eq(JkSketchCustomer::getType, Constants.equalsInteger(model.getOptStatus(),Constants.ONE)?1:0)
                    .eq(JkSketchCustomer::getSketchId, model.getId())
                    .eq(JkSketchCustomer::getIsdeleted,Constants.ZERO)
                    .orderByAsc(JkSketchCustomer::getSortnum);
            List<JkSketchCustomer> customerList = jkSketchCustomerMapper.selectJoinList(JkSketchCustomer.class,queryWrapper);
            if(customerList == null ||customerList.size() ==0){
                throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"该线路客户信息为空,不满足优化条件!");
            }
            int maxnum =100;
            try {
                maxnum = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.JK_PLAN_MAX_CLUSTER).getCode());
            }catch (Exception e){
            }
            int totalNum = 0;
            for(JkSketchLine line : lineList){
                //完善线路客户优化参数
                List<JkSketchCustomer> customerListParam =  line.getCustomerList() ;
                if(customerListParam ==null || customerListParam.size()==0){
                    throw  new BusinessException(ResponseStatus.BAD_REQUEST);
                }
                initSketchCustomerListParam(line.getCustomerList(),customerList) ;
                totalNum += customerListParam.size();
            }
            if(totalNum != Constants.formatIntegerNum(model.getOrderNum())){
                throw  new BusinessException(ResponseStatus.BAD_REQUEST);
            }
            for(JkSketchLine line : lineList){
                //逐个路线优化
                List<JkSketchCustomer> allCustomerListParam =  line.getCustomerList() ;
//                List<SketchCateModel> customerListParam = Clustering.clusterPoints(allCustomerListParam,1000);
                List<SketchCateModel> customerListParam = new ClusterCustomKMeans().clusterPoints(allCustomerListParam,maxnum,500);
                TspSolver.DataModel dataModel = new TspSolver.DataModel();
                int vehicleNumber1 = 1;//线路数量
                long[] vehicleCapacities1=new long[]{line.getMaxOrder()};//每辆车的最大订单量限制
                long[] vehicleMaxNodes=new long[]{line.getMaxCustomer()};//每辆车的最大订单量限制
                long[] demands1 = new long[customerListParam.size()+1]; //各个点的订单量
                long[] demands2 = new long[customerListParam.size()+1]; //各个点的订单量
                long[][] distanceMatrix1 = new long[customerListParam.size()+1][customerListParam.size()+1];
                distanceMatrix1[0][0] = 0;
                demands1[0] =0;//原点
                for (int i = 0; i < customerListParam.size(); i++) {
                    List<DistanceMapParam>  disList =  customerListParam.get(i).getStartPoint().getDistanceMapParamList();
                    distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                    distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                    demands1[i+1] = Constants.formatBigdecimal( customerListParam.get(i).getTotalNum()).longValue();  //各个点的订单量
                    demands2[i+1] =   customerListParam.get(i).getTotalCustomer() ;  //各个点的订单量
                    for (int j = 0; j < customerListParam.size(); j++) {
                        distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                    }
                }
                //构造优化数据模型
                dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                TspSolver.startSearch(dataModel);
                if(dataModel.getSolutions()==null || dataModel.getSolutions().size()==0){
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
                }
                TspSolverSolutions so = dataModel.getSolutions().get(0);
                List<Integer> routes = so.getRouteIndex();
                totalDistance += so.getDistance();
                if(routes.size() <=2) {
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
                }
                int index = 0;
                line.setDistance(so.getDistance());
                //有效路径
                for (Integer cIndex : routes){
                    if(cIndex ==0){
                        continue; //起始点不处理
                    }
                    SketchCateModel cateModel = customerListParam.get(cIndex-1);
                    for(int t=0;t<cateModel.getCustomerList().size();t++){
                        JkSketchCustomer customer = cateModel.getCustomerList().get(t);
                        customer.setSortnum(index++);
                        if(t < cateModel.getCustomerList().size()-1){
                            long tdistance = (long)Clustering.distanceTo(customer, cateModel.getCustomerList().get(t+1));
                            line.setDistance(Constants.formatLongNum(line.getDistance()) +tdistance);
                            totalDistance  += tdistance;
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            success =false;
        }finally {
            if(success){
                List<JkSketchCustomer> allList = new ArrayList<>();
                for(JkSketchLine line : lineList){
                    allList.addAll(line.getCustomerList());
                }
                jkSketchLineMapper.updateById(lineList);
                jkSketchCustomerMapper.updateById(allList);
            }
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
//                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(success,JkSketch::getDistance,totalDistance)
                    .set(JkSketch::getPlanLineInfo,success?"最近一次线路调整成功":"最近一次线路调整失败!")
                    .set(JkSketch::getStatus,success?Constants.TWO:Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,new Date()));
        }
    }
    @Override
    @Async
    public  void startEditSketchLineAsyncOld(JkSketch model) {
        boolean success = true;
        int totalDistance = 0;
        List<JkSketchLine> lineList = model.getSketchLineList();
@@ -607,6 +793,7 @@
                long[] vehicleCapacities1=new long[]{line.getMaxOrder()};//每辆车的最大订单量限制
                long[] vehicleMaxNodes=new long[]{line.getMaxCustomer()};//每辆车的最大订单量限制
                long[] demands1 = new long[customerListParam.size()+1]; //各个点的订单量
                long[] demands2 = new long[customerListParam.size()+1]; //各个点的订单量
                long[][] distanceMatrix1 = new long[customerListParam.size()+1][customerListParam.size()+1];
                distanceMatrix1[0][0] = 0;
                demands1[0] =0;//原点
@@ -615,12 +802,13 @@
                    distanceMatrix1[0][i+1] =  disList.get(0).getDistance();
                    distanceMatrix1[i+1][0] = disList.get(disList.size() -1).getDistance();
                    demands1[i+1] = Constants.formatBigdecimal( customerListParam.get(i).getTotalNum()).longValue();  //各个点的订单量
                    demands2[i+1] = 1;  //各个点的订单量
                    for (int j = 0; j < customerListParam.size(); j++) {
                        distanceMatrix1[i+1][j+1] =disList.get(j+1).getDistance() ;
                    }
                }
                //构造优化数据模型
                dataModel.initDataInfo(vehicleNumber1,demands1,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                dataModel.initDataInfo(vehicleNumber1,demands1,demands2,vehicleCapacities1,distanceMatrix1,vehicleMaxNodes);
                TspSolver.startSearch(dataModel);
                if(dataModel.getSolutions()==null || dataModel.getSolutions().size()==0){
                    throw  new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"线路【"+line.getLineName()+"】调整失败 ï¼ŒæœªèŽ·å¾—æœ€ä¼˜äº¤é€šè§„åˆ’æ–¹æ¡ˆï¼");
@@ -828,7 +1016,114 @@
        return new ArrayList<>();
    }
    private void dealSearchSolution(JkSketch model, TspSolver.DataModel dataModel) {
    private void dealSearchSolution(JkSketch model, List<SketchCateModel> customerList,TspSolver.DataModel dataModel) {
        Date date = new Date();
        List<TspSolverSolutions> solutions = dataModel.getSolutions();
        List<JkSketchLine> sketchLineList = new ArrayList<>();
        List<JkSketchCustomer> sketchCustomerList  = new ArrayList<>();
        long totalDistance = 0l;
        if(solutions!=null && solutions.size()>0){
            for(TspSolverSolutions so : solutions){
                List<Integer> routes = so.getRouteIndex();
                totalDistance+= so.getDistance();
                if(routes.size() <=2) {
                    continue;//无客户的非有效路线
                }
                JkLine  line =model.getLineList().get(so.getLineIndex());
                JkSketchLine tModel =  new JkSketchLine();
                tModel.setSketchId(model.getId());
                tModel.setCreator(model.getEditor());
                tModel.setCreateDate(date);
                tModel.setLineId(line.getId());
                tModel.setTotalNum(new BigDecimal(0));
                tModel.setDateInfo(model.getDateInfo());
                tModel.setType(Constants.ONE);
                tModel.setSortnum(sketchLineList.size()+1);
                tModel.setEditDate(tModel.getCreateDate());
                tModel.setEditor(tModel.getCreator());
                tModel.setIsdeleted(Constants.ZERO);
                tModel.setDistance(so.getDistance());//
                sketchLineList.add(tModel);
                tModel.setCustomerList( new ArrayList<>());
                //有效路径
                for (Integer cIndex : routes){
                    if(cIndex ==0){
                        continue; //起始点不处理
                    }
//                    JkSketchCustomer customer = model.getCustomerList().get(cIndex-1);
//                    tModel.setTotalNum(tModel.getTotalNum().add(Constants.formatBigdecimal(customer.getTotalNum())));//送货量
                    SketchCateModel cateModel = customerList.get(cIndex-1);
                    tModel.setTotalNum(tModel.getTotalNum().add(Constants.formatBigdecimal(cateModel.getTotalNum())));//送货量
                    for(int t=0;t<cateModel.getCustomerList().size();t++){
                        JkSketchCustomer customer = cateModel.getCustomerList().get(t);
                        JkSketchCustomer cModel =  new JkSketchCustomer();
                        cModel.setCreator(tModel.getCreator());
                        cModel.setOrderId(customer.getOrderId());
                        cModel.setCreateDate(tModel.getCreateDate());
                        cModel.setType(Constants.ONE);
                        cModel.setTotalNum(customer.getTotalNum());
                        cModel.setDateInfo(model.getDateInfo());
                        cModel.setSortnum(tModel.getCustomerList().size()+1);
                        cModel.setEditDate(tModel.getCreateDate());
                        cModel.setEditor(tModel.getCreator());
                        cModel.setIsdeleted(Constants.ZERO);
                        cModel.setSketchId(model.getId());
                        cModel.setCustomerId(customer.getCustomerId());
                        if(t < cateModel.getCustomerList().size()-1){
                            long tdistance = (long)Clustering.distanceTo(customer, cateModel.getCustomerList().get(t+1));
                            tModel.setDistance(Constants.formatLongNum(tModel.getDistance()) +tdistance);
                            totalDistance  += tdistance;
                        }
                        tModel.getCustomerList().add(cModel);
                    }
                    tModel.setOrderNum( tModel.getCustomerList().size());
                }
            }
        }
        if(sketchLineList!=null && sketchLineList.size()>0){
            jkSketchLineMapper.update(null,new UpdateWrapper<JkSketchLine>().lambda()
                    .set(JkSketchLine::getIsdeleted,Constants.ONE)
                    .eq(JkSketchLine::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchLine::getType,Constants.ONE)
                    .eq(JkSketchLine::getDateInfo,model.getDateInfo()));
            jkSketchCustomerMapper.update(null,new UpdateWrapper<JkSketchCustomer>().lambda()
                    .set(JkSketchCustomer::getIsdeleted,Constants.ONE)
                    .eq(JkSketchCustomer::getIsdeleted,Constants.ZERO)
                    .eq(JkSketchCustomer::getType,Constants.ONE)
                    .eq(JkSketchCustomer::getDateInfo,model.getDateInfo()));
            if(sketchLineList.size()>0){
                jkSketchLineMapper.insert(sketchLineList);
            }
            for(JkSketchLine l : sketchLineList){
                if(l.getCustomerList()!=null ){
                    for(JkSketchCustomer c :l.getCustomerList()){
                        c.setSketchLineId(l.getId());
                    }
                    sketchCustomerList.addAll(l.getCustomerList());
                }
            }
            if(sketchCustomerList.size()>0){
                jkSketchCustomerMapper.insert(sketchCustomerList);
            }
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getLineNum,sketchLineList.size() )
                    .set(JkSketch::getOptStatus,Constants.ONE)//已生成优化线路
                    .set(JkSketch::getDistance,totalDistance)
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化成功,优化后总距离:"+(totalDistance/1000)+"公里!")
                    .set(JkSketch::getStatus,Constants.TWO)
                    .set(JkSketch::getPlanLineEndDate,date));
        }else{
            jkSketchMapper.update(null,new UpdateWrapper<JkSketch>().lambda()
                    .eq(JkSketch::getId,model.getId() )
                    .eq(JkSketch::getJobId,model.getJobId() )
                    .set(JkSketch::getPlanLineInfo,"最近一次线路优化失败,未找到最优路线!")
                    .set(JkSketch::getStatus,Constants.THREE)
                    .set(JkSketch::getPlanLineEndDate,date));
        }
    }
    private void dealSearchSolutionOld(JkSketch model, TspSolver.DataModel dataModel) {
        Date date = new Date();
        List<TspSolverSolutions> solutions = dataModel.getSolutions();
        List<JkSketchLine> sketchLineList = new ArrayList<>();