Ver código fonte

系统检查基础逻辑

欧阳劲驰 4 meses atrás
pai
commit
e547f28822
29 arquivos alterados com 2199 adições e 183 exclusões
  1. 32 12
      pom.xml
  2. 58 0
      src/main/java/com/shkpr/service/alambizplugin/apiparam/GisSurveyCheckParams.java
  3. 213 7
      src/main/java/com/shkpr/service/alambizplugin/bizservice/GisSurveyBizService.java
  4. 179 0
      src/main/java/com/shkpr/service/alambizplugin/components/GisSurveySystemChecker.java
  5. 79 0
      src/main/java/com/shkpr/service/alambizplugin/components/checker/DuplicatePointsFinder.java
  6. 204 0
      src/main/java/com/shkpr/service/alambizplugin/components/checker/IsolatedLinesFinder.java
  7. 92 0
      src/main/java/com/shkpr/service/alambizplugin/components/checker/IsolatedPointsFinder.java
  8. 115 0
      src/main/java/com/shkpr/service/alambizplugin/components/checker/OverlapLinesFinder.java
  9. 2 1
      src/main/java/com/shkpr/service/alambizplugin/configuration/MainSourceConfiguration.java
  10. 32 8
      src/main/java/com/shkpr/service/alambizplugin/configuration/ScheduleTaskConfiguration.java
  11. 2 0
      src/main/java/com/shkpr/service/alambizplugin/constants/ApiURI.java
  12. 35 0
      src/main/java/com/shkpr/service/alambizplugin/constants/GisSurveyCheckStatusEnum.java
  13. 31 0
      src/main/java/com/shkpr/service/alambizplugin/constants/GisSurveyCheckTypeEnum.java
  14. 186 31
      src/main/java/com/shkpr/service/alambizplugin/controller/ApiGisSurveyController.java
  15. 48 0
      src/main/java/com/shkpr/service/alambizplugin/dbdao/mapper/GisSurveyLayerApplyMapper.java
  16. 98 0
      src/main/java/com/shkpr/service/alambizplugin/dbdao/pgtype/GeomLineSegmentTypeHandlePg.java
  17. 92 0
      src/main/java/com/shkpr/service/alambizplugin/dbdao/pgtype/GeomPointTypeHandlePg.java
  18. 228 0
      src/main/java/com/shkpr/service/alambizplugin/dbdao/services/GisSurveyLayerApplyServiceImpl.java
  19. 46 0
      src/main/java/com/shkpr/service/alambizplugin/dbdao/services/intef/GisSurveyLayerApplyService.java
  20. 103 0
      src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyCheckResult.java
  21. 40 0
      src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyCheckTaskId.java
  22. 51 0
      src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyLayerApplyLine.java
  23. 46 0
      src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyLayerApplyPoint.java
  24. 5 52
      src/main/java/com/shkpr/service/alambizplugin/dto/ResponseRes.java
  25. 34 14
      src/main/java/com/shkpr/service/alambizplugin/globalmgr/ScheduleTaskMgr.java
  26. 58 54
      src/main/resources/application.properties
  27. 1 1
      src/main/resources/logback.xml
  28. 78 0
      src/main/resources/mapper/GisSurveyLayerApplyMapper.xml
  29. 11 3
      src/test/java/com/shkpr/service/alambizplugin/AlamBizPluginApplicationTests.java

+ 32 - 12
pom.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
@@ -285,17 +285,17 @@
             </exclusions>
         </dependency>
 
-        <dependency>
-            <groupId>it.geosolutions</groupId>
-            <artifactId>geoserver-manager</artifactId>
-            <version>1.7.0</version>
-            <exclusions>
-                <exclusion>
-                    <artifactId>commons-io</artifactId>
-                    <groupId>commons-io</groupId>
-                </exclusion>
-            </exclusions>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>it.geosolutions</groupId>-->
+<!--            <artifactId>geoserver-manager</artifactId>-->
+<!--            <version>1.7.0</version>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <artifactId>commons-io</artifactId>-->
+<!--                    <groupId>commons-io</groupId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
 
         <dependency>
             <groupId>org.jasypt</groupId>
@@ -309,6 +309,26 @@
             <version>3.14.9</version>
         </dependency>
 
+        <!--base-->
+        <dependency>
+            <groupId>com.global</groupId>
+            <artifactId>gbase</artifactId>
+            <version>1.0.5</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/libs/gbase.1.0.5.jar</systemPath>
+        </dependency>
+        <!--jts-->
+        <dependency>
+            <groupId>org.locationtech.jts</groupId>
+            <artifactId>jts-core</artifactId>
+            <version>1.19.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.locationtech.jts.io</groupId>
+            <artifactId>jts-io-common</artifactId>
+            <version>1.19.0</version>
+        </dependency>
+
     </dependencies>
 
     <repositories>

+ 58 - 0
src/main/java/com/shkpr/service/alambizplugin/apiparam/GisSurveyCheckParams.java

@@ -0,0 +1,58 @@
+package com.shkpr.service.alambizplugin.apiparam;
+
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckTypeEnum;
+import com.shkpr.service.alambizplugin.controllervalid.CommonParamValidDel;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * 系统检查入参
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@Setter
+public class GisSurveyCheckParams {
+    /**
+     * 检查类型:0:项目,1:任务
+     */
+    @NotNull(groups = {CommonParamValidDel.class})
+    private Integer checkType;
+    /**
+     * 项目id
+     */
+    @Size(max = 64, groups = {CommonParamValidDel.class})
+    private String projId;
+    /**
+     * 任务id
+     */
+    @Size(max = 64, groups = {CommonParamValidDel.class})
+    private String jobId;
+
+    /**
+     * 入参校验
+     *
+     * @return 校验状态
+     */
+    public boolean checkValid() {
+        //检查项目类型
+        if (checkType == null || Arrays.stream(GisSurveyCheckTypeEnum.values())
+                .noneMatch(e -> Objects.equals(e.getCode(), checkType))) {
+            return false;
+        }
+        //检查项目ID和任务ID
+        if (Objects.equals(checkType, GisSurveyCheckTypeEnum.PROJECT.getCode())) {
+            return StringUtils.isNotBlank(projId);
+        } else if (Objects.equals(checkType, GisSurveyCheckTypeEnum.JOB.getCode())) {
+            return StringUtils.isNotBlank(jobId);
+        }
+        return false;
+    }
+}

+ 213 - 7
src/main/java/com/shkpr/service/alambizplugin/bizservice/GisSurveyBizService.java

@@ -1,14 +1,220 @@
 package com.shkpr.service.alambizplugin.bizservice;
 
-import com.shkpr.service.alambizplugin.constants.ResponseCode;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.apiparam.GisSurveyCheckParams;
+import com.shkpr.service.alambizplugin.components.GisSurveySystemChecker;
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckTypeEnum;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dto.GisSurveyCheckResult;
+import com.shkpr.service.alambizplugin.dto.GisSurveyCheckTaskId;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
 
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+/**
+ * 系统检查管理service
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Component
 public class GisSurveyBizService {
-    private final static String mStrClassName = "GisSurveyBizService";
-    private final static String EMPTY_NULL = "NULL";
+    /**
+     * 任务缓存
+     */
+    private final static Map<GisSurveyCheckTaskId, ListenableFuture<GisSurveyCheckResult>> FUTURE_CACHE = new ConcurrentHashMap<>();
+    /**
+     * 开始时间缓存
+     */
+    private final static Map<GisSurveyCheckTaskId, LocalDateTime> TIME_CACHE = new ConcurrentHashMap<>();
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    private final GisSurveySystemChecker systemChecker;
+
+    public GisSurveyBizService(GisSurveySystemChecker systemChecker) {
+        mStrClassName = "GisSurveyBizService";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+        this.systemChecker = systemChecker;
+    }
+
+    /**
+     * 系统检查
+     *
+     * @param params 系统检查参数
+     * @return 检查状态
+     */
+    public GisSurveyCheckResult sysCheckFun(GisSurveyCheckParams params) {
+        //任务标识
+        GisSurveyCheckTaskId taskId = generateTaskId(params);
+        if (taskId == null) return GisSurveyCheckResult.notExists();
+        //获取已存在的任务
+        ListenableFuture<GisSurveyCheckResult> previousFuture = FUTURE_CACHE.get(taskId);
+        //进行中判断(未完成且未清除)
+        if (previousFuture != null && !previousFuture.isDone() && !previousFuture.isCancelled())
+            return GisSurveyCheckResult.inProgress(TIME_CACHE.get(taskId));
+        //已结束判断,删除缓存
+        if (previousFuture != null && (previousFuture.isDone() || previousFuture.isCancelled()))
+            FUTURE_CACHE.remove(taskId);
+        //启动检查任务
+        startTask(taskId, params);
+        //返回进行中
+        return GisSurveyCheckResult.inProgress(LocalDateTime.now());
+    }
+
+    /**
+     * 获取结果
+     *
+     * @param params 系统检查参数
+     * @return 检查结果(可能进行中)
+     */
+    public GisSurveyCheckResult getResult(GisSurveyCheckParams params) {
+        //任务标识
+        GisSurveyCheckTaskId taskId = generateTaskId(params);
+        if (taskId == null) return GisSurveyCheckResult.notExists();
+        //获取任务
+        ListenableFuture<GisSurveyCheckResult> checkFuture = FUTURE_CACHE.get(taskId);
+        if (checkFuture == null) return GisSurveyCheckResult.notExists();
+        //已结束,则直接返回失败(如完成cancelled为false,切无法clear)
+        if (checkFuture.isCancelled()) return GisSurveyCheckResult.fail();
+        //完成判断,如完成直接返回结果
+        if (checkFuture.isDone()) {
+            try {
+                return FUTURE_CACHE.get(taskId).get();
+            } catch (ExecutionException | InterruptedException e) {
+                //打印报错信息(不太可能走到这里)
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "监测到中断或执行异常 param:%s msg:%s"
+                                , params
+                                , e.getMessage()
+                        )
+                );
+                return GisSurveyCheckResult.fail();
+            }
+        }
+        //返回进行中
+        return GisSurveyCheckResult.inProgress(TIME_CACHE.get(taskId));
+    }
+
+    /**
+     * 删除结果
+     *
+     * @param params 系统检查参数
+     * @return 检查结果(可能进行中)
+     */
+    public GisSurveyCheckResult delResult(GisSurveyCheckParams params) {
+        //任务标识
+        GisSurveyCheckTaskId taskId = generateTaskId(params);
+        if (taskId == null) return GisSurveyCheckResult.fail();
+        //如无缓存,则直接返回不存在
+        if (!FUTURE_CACHE.containsKey(taskId)) return GisSurveyCheckResult.notExists();
+        //关闭检查任务
+        return stopTask(taskId) ? GisSurveyCheckResult.success() : GisSurveyCheckResult.fail();
+    }
+
+    /**
+     * 过期任务
+     * <p>用于检查和使任务过期</p>
+     */
+    public void expireResult(long ttl) {
+        //获取超时的id
+        List<GisSurveyCheckTaskId> taskIds = TIME_CACHE.entrySet().stream()
+                .filter(entry -> Duration.between(entry.getValue(), LocalDateTime.now()).toMillis() > ttl)
+                .map(Map.Entry::getKey)
+                .collect(Collectors.toList());
+        //停止超时的任务并删除任务缓存
+        for (GisSurveyCheckTaskId taskId : taskIds) {
+            //如任务不存在,则删除时间缓存
+            if (!FUTURE_CACHE.containsKey(taskId)) TIME_CACHE.remove(taskId);
+            //停用缓存
+            if (stopTask(taskId)) TIME_CACHE.remove(taskId);
+            else {
+                //打印报错信息
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "过期任务停止失败 ttl:%s taskId:%s"
+                                , ttl
+                                , taskId
+                        )
+                );
+            }
+        }
+    }
+
+    /**
+     * 生成任务标识
+     *
+     * @param params 系统检查参数
+     * @return 任务标识
+     */
+    private GisSurveyCheckTaskId generateTaskId(GisSurveyCheckParams params) {
+        //获取枚举
+        GisSurveyCheckTypeEnum checkTypeEnum = Arrays.stream(GisSurveyCheckTypeEnum.values())
+                .filter(enumeration -> Objects.equals(params.getCheckType(), enumeration.getCode()))
+                .findFirst().orElse(null);
+        if (checkTypeEnum == null) return null;
+        //项目维度
+        if (checkTypeEnum.equals(GisSurveyCheckTypeEnum.PROJECT)) {
+            return new GisSurveyCheckTaskId(GisSurveyCheckTypeEnum.PROJECT.getCode(), params.getProjId());
+        }
+        //任务维度
+        else if (checkTypeEnum.equals(GisSurveyCheckTypeEnum.JOB)) {
+            return new GisSurveyCheckTaskId(GisSurveyCheckTypeEnum.PROJECT.getCode(), params.getJobId());
+        }
+        return null;
+    }
+
+    /**
+     * 启动任务
+     *
+     * @param taskId 任务id
+     * @param params 检查参数
+     */
+    private void startTask(GisSurveyCheckTaskId taskId, GisSurveyCheckParams params) {
+        //异步执行系统检查任务
+        ListenableFuture<GisSurveyCheckResult> checkFuture = systemChecker.systemCheckTask(params);
+        //缓存任务句柄
+        FUTURE_CACHE.put(taskId, checkFuture);
+        //缓存时间
+        TIME_CACHE.put(taskId, LocalDateTime.now());
+    }
 
-    public static ResponseCode sysCheckFun(){
-        ResponseCode code = ResponseCode.RESULT_BAD;
-        //do.......
-        return code;
+    /**
+     * 关闭检查任务
+     *
+     * @param taskId 任务id
+     * @return 关闭状态
+     */
+    private Boolean stopTask(GisSurveyCheckTaskId taskId) {
+        ListenableFuture<GisSurveyCheckResult> future = FUTURE_CACHE.get(taskId);
+        //完成判断,完成删除缓存
+        if (future.isCancelled() || future.isDone()) {
+            FUTURE_CACHE.remove(taskId);
+            TIME_CACHE.remove(taskId);
+            return true;
+        }
+        //尝试清除任务
+        boolean cancel = future.cancel(true);
+        //清除成功,删除缓存
+        if (cancel) {
+            FUTURE_CACHE.remove(taskId);
+            TIME_CACHE.remove(taskId);
+            return true;
+        }
+        return false;
     }
 }

+ 179 - 0
src/main/java/com/shkpr/service/alambizplugin/components/GisSurveySystemChecker.java

@@ -0,0 +1,179 @@
+package com.shkpr.service.alambizplugin.components;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.apiparam.GisSurveyCheckParams;
+import com.shkpr.service.alambizplugin.components.checker.DuplicatePointsFinder;
+import com.shkpr.service.alambizplugin.components.checker.IsolatedLinesFinder;
+import com.shkpr.service.alambizplugin.components.checker.IsolatedPointsFinder;
+import com.shkpr.service.alambizplugin.components.checker.OverlapLinesFinder;
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckStatusEnum;
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckTypeEnum;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dbdao.services.intef.GisSurveyLayerApplyService;
+import com.shkpr.service.alambizplugin.dto.GisSurveyCheckResult;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * 系统检查执行器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Component
+public class GisSurveySystemChecker {
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    private final GisSurveyLayerApplyService gisSurveyLayerApplyService;
+    private final IsolatedPointsFinder isolatedPointsFinder;
+    private final IsolatedLinesFinder isolatedLinesFinder;
+    private final DuplicatePointsFinder duplicatePointsFinder;
+    private final OverlapLinesFinder overlapLinesFinder;
+
+    public GisSurveySystemChecker(GisSurveyLayerApplyService gisSurveyLayerApplyService, IsolatedPointsFinder isolatedPointsFinder, IsolatedLinesFinder isolatedLinesFinder, DuplicatePointsFinder duplicatePointsFinder, OverlapLinesFinder overlapLinesFinder) {
+        mStrClassName = "GisSurveySystemChecker";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+        this.gisSurveyLayerApplyService = gisSurveyLayerApplyService;
+        this.isolatedPointsFinder = isolatedPointsFinder;
+        this.isolatedLinesFinder = isolatedLinesFinder;
+        this.duplicatePointsFinder = duplicatePointsFinder;
+        this.overlapLinesFinder = overlapLinesFinder;
+    }
+
+    /**
+     * 系统检查任务
+     *
+     * @param params 系统检查参数
+     * @return 系统检查返回
+     */
+    @Async
+    public ListenableFuture<GisSurveyCheckResult> systemCheckTask(GisSurveyCheckParams params) {
+        //项目id
+        String projId = params.getProjId();
+        //任务id
+        String jobId = params.getJobId();
+        //检查类型
+        Integer checkType = params.getCheckType();
+        //构建返回
+        GisSurveyCheckResult result = GisSurveyCheckResult.fail();
+        //孤立点任务
+        ListenableFuture<List<String>> isolatedPointsFuture = null;
+        //孤立线任务
+        ListenableFuture<List<List<String>>> IsolatedLinesFuture = null;
+        //重复点任务
+        ListenableFuture<List<List<String>>> duplicatePointsFuture = null;
+        //重叠线任务
+        ListenableFuture<List<List<String>>> overlapLinesFuture = null;
+        //点集合
+        List<GisSurveyLayerApplyPoint> points = null;
+        //线集合
+        List<GisSurveyLayerApplyLine> lines = null;
+        try {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "开始执行系统检查;检查类型 检查类型:%s 项目ID:%s 任务ID:%s"
+                            , checkType
+                            , projId
+                            , jobId
+                    )
+            );
+
+            //根据项目查询点线信息
+            if (Objects.equals(checkType, GisSurveyCheckTypeEnum.PROJECT.getCode())) {
+                points = gisSurveyLayerApplyService.findAddPointByProjId(projId);
+                lines = gisSurveyLayerApplyService.findAddLineByProjId(projId);
+            }
+            //根据任务查询点线信息
+            else if (Objects.equals(checkType, GisSurveyCheckTypeEnum.JOB.getCode())) {
+                points = gisSurveyLayerApplyService.findAddPointByJobId(jobId);
+                lines = gisSurveyLayerApplyService.findAddLineByJobId(jobId);
+            }
+
+            //孤立点检查
+            if (points != null && lines != null) {
+                isolatedPointsFuture = isolatedPointsFinder.findIsolatedPoints(points, lines);
+            }
+            //重复点检查
+            if (points != null) {
+                duplicatePointsFuture = duplicatePointsFinder.findDuplicatePoints(points, 6);
+            }
+            //孤立线和重叠线检查
+            if (lines != null) {
+                IsolatedLinesFuture = isolatedLinesFinder.findIsolatedLines(lines);
+                overlapLinesFuture = overlapLinesFinder.findDuplicateLines(lines);
+            }
+
+            //等待结果,并存入返回,优先监听点相关(执行速度快且只需要点集合,优先释放点集合内存)
+            if (isolatedPointsFuture != null) {
+                //监听孤立点
+                final List<String> isolatedPoints = isolatedPointsFuture.get();
+                result.setIsolatedPoints(isolatedPoints);
+                //释放点缓存(孤立点和重复点都完成时,后续不需要点数据,释放缓存节省内存)
+                if (duplicatePointsFuture != null && duplicatePointsFuture.isDone()) points.clear();
+            }
+            if (duplicatePointsFuture != null) {
+                //监听重复点
+                final List<List<String>> duplicatePointCodes = duplicatePointsFuture.get();
+                result.setDuplicatePoints(duplicatePointCodes);
+                //释放点缓存(孤立点和重复点都完成时,后续不需要点数据,释放缓存节省内存)
+                if (isolatedPointsFuture != null && isolatedPointsFuture.isDone()) points.clear();
+            }
+            if (IsolatedLinesFuture != null) {
+                //监听重复线
+                final List<List<String>> isolatedLines = IsolatedLinesFuture.get();
+                result.setIsolatedLines(isolatedLines);
+            }
+            if (overlapLinesFuture != null) {
+                //监听重叠线
+                final List<List<String>> overlapLines = overlapLinesFuture.get();
+                result.setOverlapLines(overlapLines);
+            }
+
+            //完成检查
+            result.setCompleteTime(LocalDateTime.now());
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束执行系统检查;检查类型: %d,项目ID:%s, 任务ID:,%s,用时(毫秒):%d",
+                            checkType,
+                            projId,
+                            jobId,
+                            Duration.between(result.getRequestTime(), result.getCompleteTime()).toMillis()
+                    )
+            );
+            return new AsyncResult<>(result);
+        } catch (InterruptedException | ExecutionException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format(
+                            "监测到中断或执行异常,开始清除子任务: %d,项目ID:%s, 任务ID:,%s,",
+                            checkType,
+                            projId,
+                            jobId
+                    )
+            );
+            //清除子任务
+            if (isolatedPointsFuture != null) isolatedPointsFuture.cancel(true);
+            if (IsolatedLinesFuture != null) IsolatedLinesFuture.cancel(true);
+            if (duplicatePointsFuture != null) duplicatePointsFuture.cancel(true);
+            if (overlapLinesFuture != null) overlapLinesFuture.cancel(true);
+            //失败信息
+            result.setCheckStatus(GisSurveyCheckStatusEnum.FAIL.getCode());
+            result.setCompleteTime(LocalDateTime.now());
+            return new AsyncResult<>(result);
+        }
+    }
+}

+ 79 - 0
src/main/java/com/shkpr/service/alambizplugin/components/checker/DuplicatePointsFinder.java

@@ -0,0 +1,79 @@
+package com.shkpr.service.alambizplugin.components.checker;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+import org.locationtech.jts.geom.Coordinate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 重复点寻找器
+ */
+@Component
+public class DuplicatePointsFinder {
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    public DuplicatePointsFinder() {
+        mStrClassName = "DuplicatePointsFinder";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+    }
+
+    /**
+     * 寻找重复点
+     * <p>根据 <strong>精度</strong> 强匹配</p>
+     *
+     * @param points 点集合
+     * @param scale  精度
+     * @return 重复点
+     */
+    @Async
+    public ListenableFuture<List<List<String>>> findDuplicatePoints(List<GisSurveyLayerApplyPoint> points, int scale) throws InterruptedException {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, String.format("开始执行寻找重复点 精度:%s ======>", scale));
+        long begin = System.currentTimeMillis();
+
+        //根据经纬度分组
+        Map<String, List<GisSurveyLayerApplyPoint>> keyToPoints = points.parallelStream()
+                .collect(Collectors.groupingBy(point -> {
+                    //抹去经纬度精度,并设置为分组条件
+                    Coordinate coordinate = point.getGis().getCoordinate();
+                    BigDecimal bdLon = new BigDecimal(Double.toString(coordinate.getX())).setScale(scale, RoundingMode.DOWN);
+                    BigDecimal bdLat = new BigDecimal(Double.toString(coordinate.getY())).setScale(scale, RoundingMode.DOWN);
+                    return String.format("%s%s%s", bdLon.toPlainString(), bdLat.toPlainString(), point.getElevation());
+                }, Collectors.toList()));
+        //响应中断
+        if (Thread.interrupted()) throw new InterruptedException();
+        //并行流处理
+        List<List<String>> collect = keyToPoints.values().parallelStream()
+                //过滤组内大于1
+                .filter(group -> group.size() > 1)
+                //取出code
+                .map(group -> group.stream()
+                        .map(GisSurveyLayerApplyPoint::getCode)
+                        .collect(Collectors.toList())
+                )
+                .collect(Collectors.toList());
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "结束执行寻找重复点,用时(毫秒):%d"
+                        , (end - begin)
+                )
+        );
+        return new AsyncResult<>(collect);
+    }
+}

+ 204 - 0
src/main/java/com/shkpr/service/alambizplugin/components/checker/IsolatedLinesFinder.java

@@ -0,0 +1,204 @@
+package com.shkpr.service.alambizplugin.components.checker;
+
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 孤立线查找
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Component
+public class IsolatedLinesFinder {
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    public IsolatedLinesFinder() {
+        mStrClassName = "IsolatedLinesFinder";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+    }
+
+    /**
+     * 寻找孤立线
+     *
+     * @param lines 线集合
+     * @return 线分组
+     */
+    @Async
+    public ListenableFuture<List<List<String>>> findIsolatedLines(List<GisSurveyLayerApplyLine> lines) throws InterruptedException {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始执行寻找孤立线========>");
+        long begin = System.currentTimeMillis();
+
+        //创建并查集
+        UnionFind unionFind = new UnionFind();
+        //分组线
+        List<List<String>> groupLines = unionFind.groupLines(lines);
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "结束执行寻找孤立线,用时(毫秒):%d"
+                        , (end - begin)
+                )
+        );
+        return new AsyncResult<>(groupLines);
+    }
+
+    /**
+     * 并查集
+     */
+    public static class UnionFind {
+        //线的子父关系,key:子,value:父(可能为根)
+        private final Map<String, String> parent = new HashMap<>();
+        //点根线关系,{线的节点编码}->{根线编码}
+        private final Map<String, String> pointRoot = new HashMap<>();
+        //秩,通常树的根节点为1
+        private final Map<String, Integer> rank = new HashMap<>();
+
+        /**
+         * 线分组
+         *
+         * @param lines 线集合
+         * @return 分组集合
+         */
+        public List<List<String>> groupLines(List<GisSurveyLayerApplyLine> lines) throws InterruptedException {
+            //处理所有线
+            for (GisSurveyLayerApplyLine line : lines) {
+                //响应中断
+                if (Thread.interrupted()) throw new InterruptedException();
+                //初始化节点
+                initNode(line.getCode());
+                //当前code
+                String currentRoot = line.getCode();
+                //处理起点,合并当前线与以upNode为终点的根线
+                if (pointRoot.containsKey(line.getDownNode())) {
+                    String existingRoot = pointRoot.get(line.getUpNode());
+                    currentRoot = unionAndGetRoot(currentRoot, existingRoot);
+                }
+                //处理终点,合并当前线与以downNode为终点的根线
+                if (pointRoot.containsKey(line.getDownNode())) {
+                    String existingRoot = pointRoot.get(line.getDownNode());
+                    currentRoot = unionAndGetRoot(currentRoot, existingRoot);
+                }
+                //更新当前节点的根线映射
+                updateRootMappings(line.getUpNode(), currentRoot);
+                updateRootMappings(line.getDownNode(), currentRoot);
+            }
+            //清理关系表
+            pointRoot.clear();
+            //构建分组结果
+            Map<String, List<String>> groups = new HashMap<>();
+            for (GisSurveyLayerApplyLine line : lines) {
+                //响应中断
+                if (Thread.interrupted()) throw new InterruptedException();
+                //当前线code
+                String lineCode = line.getCode();
+                //根线code
+                String rootCode = findRoot(lineCode);
+                //当前线存入根线组
+                groups.computeIfAbsent(rootCode, k -> new ArrayList<>()).add(lineCode);
+            }
+            return new ArrayList<>(groups.values());
+        }
+
+        /**
+         * 、更新节点对应的根线映射
+         *
+         * @param node        节点
+         * @param currentRoot 当前节点的根线
+         */
+        private void updateRootMappings(String node, String currentRoot) throws InterruptedException {
+            //获取点根线关系,如存在该关系,则再寻找一次根线,保证多点根一致
+            String existingRoot = pointRoot.get(node);
+            if (existingRoot != null) currentRoot = unionAndGetRoot(currentRoot, existingRoot);
+            //更新点根线关系
+            pointRoot.put(node, currentRoot);
+        }
+
+        /**
+         * 初始化节线
+         *
+         * @param code 节点code
+         */
+        public void initNode(String code) {
+            parent.computeIfAbsent(code, k -> {
+                rank.put(k, 0);
+                return k;
+            });
+        }
+
+        /**
+         * 查找根节点
+         *
+         * @param code 节点code
+         * @return 根节点code
+         */
+        public String findRoot(String code) throws InterruptedException {
+            //当前code
+            String current = code;
+            //最大迭代深度(该算法时间为常数级,如查找深度到64则为程序异常)
+            int maxIterationsDepth = 64;
+            //迭代记录,防止无限递归
+            int iterations = 0;
+            while (iterations++ < maxIterationsDepth) {
+                //响应中断
+                if (Thread.interrupted()) throw new InterruptedException();
+                //提取父节点
+                String parentNode = parent.get(current);
+                // 找到根节点立即返回
+                if (parentNode.equals(current)) return current;
+                //当前节点指向祖父节点,路径减半(已知子父关系,故跳过)
+                String grandparent = parent.get(parentNode);
+                parent.put(current, grandparent);
+                current = grandparent;
+            }
+            //递归保险,最终强制验证是否为根节点,否则结束查找
+            if (parent.get(current).equals(current)) return current;
+            else throw new IllegalStateException("该code无法查询到根节点: " + code);
+        }
+
+        /**
+         * 合并并返回根线
+         *
+         * @param current 当前线
+         * @param target  目标线
+         * @return 根线
+         */
+        public String unionAndGetRoot(String current, String target) throws InterruptedException {
+            //获取两线的根线
+            String rootCurrent = findRoot(current);
+            String rootTarget = findRoot(target);
+            //排除根线相同
+            if (rootCurrent.equals(rootTarget)) return rootCurrent;
+            //按秩合并,如 当前线根节点秩 大于 目标线根节点秩,则 目标线根线 为 当前线根线 的下游线
+            if (rank.get(rootCurrent) > rank.get(rootTarget)) {
+                parent.put(rootTarget, rootCurrent);
+                return rootCurrent;
+            } else {
+                //否则 当前线根线 为 目标线根线 的下游线
+                parent.put(rootCurrent, rootTarget);
+                //如线秩相同,则对根线升秩
+                if (rank.get(rootCurrent).equals(rank.get(rootTarget))) {
+                    rank.put(rootTarget, rank.get(rootTarget) + 1);
+                }
+                return rootTarget;
+            }
+        }
+    }
+}

+ 92 - 0
src/main/java/com/shkpr/service/alambizplugin/components/checker/IsolatedPointsFinder.java

@@ -0,0 +1,92 @@
+package com.shkpr.service.alambizplugin.components.checker;
+
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 寻找孤立点
+ */
+@Component
+public class IsolatedPointsFinder {
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    public IsolatedPointsFinder() {
+        mStrClassName = "IsolatedPointsFinder";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+    }
+
+    /**
+     * 寻找孤立点
+     * <p>根据 {@code code} 上下游匹配,不涉及拓扑点</p><p>标识: {@code code} 唯一</p>
+     *
+     * @param points 点集合
+     * @param lines  线集合
+     * @return 孤立点集合
+     */
+    @Async
+    public ListenableFuture<List<String>> findIsolatedPoints(List<GisSurveyLayerApplyPoint> points, List<GisSurveyLayerApplyLine> lines) throws InterruptedException {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始执行寻找孤立点========>");
+        long begin = System.currentTimeMillis();
+
+        //线为空,点不为空,则全孤立点
+        if (lines.isEmpty() && !points.isEmpty())
+            return new AsyncResult<>(
+                    points.parallelStream()
+                            //转code
+                            .map(GisSurveyLayerApplyPoint::getCode)
+                            .collect(Collectors.toList())
+            );
+        //线为空,点也为空,则无孤立点
+        if (lines.isEmpty()) return new AsyncResult<>(Collections.emptyList());
+        //点为空,则无孤立点
+        if (points.isEmpty()) return new AsyncResult<>(Collections.emptyList());
+        //计算预期最大容量,避免频繁扩容
+        int expectedMaxSize = (int) (((long) lines.size() << 1) / .75f + 1);
+        //联通点code
+        Set<String> connectedPoints = new HashSet<>(expectedMaxSize);
+        //遍历所有线,标记参与的点
+        for (GisSurveyLayerApplyLine line : lines) {
+            //响应中断
+            if (Thread.interrupted()) throw new InterruptedException();
+            //上下游节点判空
+            if (StringUtils.isBlank(line.getUpNode()) || StringUtils.isBlank(line.getDownNode())) continue;
+            connectedPoints.add(line.getUpNode());
+            connectedPoints.add(line.getDownNode());
+        }
+        //使用并行流找出孤立点
+        List<String> collect = points.parallelStream()
+                .map(GisSurveyLayerApplyPoint::getCode)
+                .filter(code -> !connectedPoints.contains(code))
+                .collect(Collectors.toList());
+
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "结束执行寻找孤立点,用时(毫秒):%d"
+                        , (end - begin)
+                )
+        );
+        return new AsyncResult<>(collect);
+    }
+
+}

+ 115 - 0
src/main/java/com/shkpr/service/alambizplugin/components/checker/OverlapLinesFinder.java

@@ -0,0 +1,115 @@
+package com.shkpr.service.alambizplugin.components.checker;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import org.locationtech.jts.algorithm.Orientation;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.LineSegment;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 寻找重复线
+ *
+ * @author 欧阳劲驰
+ * @since 0.0.1
+ */
+@Component
+public class OverlapLinesFinder {
+    //双精度误差
+    private static final double EPSILON = Double.longBitsToDouble(0x3ca0000000000000L);
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    public OverlapLinesFinder() {
+        mStrClassName = "OverlapLinesFinder";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+    }
+
+    /**
+     * 寻找重复线
+     * <p>返回的为重复线组,未重复的线不会出现在返回结果</p>
+     * <p>一组线,仅为两条重叠的线,长度固定为2</p>
+     *
+     * @param lines 线集合
+     * @return 重复线分组
+     */
+    @Async
+    public ListenableFuture<List<List<String>>> findDuplicateLines(List<GisSurveyLayerApplyLine> lines) throws InterruptedException {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始执行寻找重复线========>");
+        long begin = System.currentTimeMillis();
+
+        //结果
+        List<List<String>> result = new ArrayList<>();
+        //双层循环遍历所有线
+        for (int i = 0; i < lines.size(); i++) {
+            GisSurveyLayerApplyLine line1 = lines.get(i);
+            //响应中断
+            if (Thread.interrupted()) throw new InterruptedException();
+            for (int j = i + 1; j < lines.size(); j++) {
+                GisSurveyLayerApplyLine line2 = lines.get(j);
+                //响应中断
+                if (Thread.interrupted()) throw new InterruptedException();
+                // 判断是否重复
+                if (calcDuplicateLines(line1, line2)) {
+                    // 创建两两一组的重复线对
+                    result.add(Arrays.asList(line1.getCode(), line2.getCode()));
+                }
+            }
+        }
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "结束执行寻找重复线,用时(毫秒):%d"
+                        , (end - begin)
+                )
+        );
+        return new AsyncResult<>(result);
+    }
+
+    /**
+     * 计算重叠线
+     *
+     * @param line1 线段1
+     * @param line2 线段2
+     * @return 重叠状态
+     */
+    public boolean calcDuplicateLines(GisSurveyLayerApplyLine line1, GisSurveyLayerApplyLine line2) {
+        //取出四个点
+        Coordinate a = line1.getGis().p0;
+        Coordinate b = line1.getGis().p1;
+        Coordinate c = line2.getGis().p0;
+        Coordinate d = line2.getGis().p1;
+        //点数量判断
+        if (a == null || b == null || c == null || d == null) return false;
+        //检查C和D是否在AB的直线上(叉积为0,则方向一致)
+        if (Orientation.index(a, b, c) != 0 || Orientation.index(a, b, d) != 0) return false;
+        LineSegment seg1 = new LineSegment(a, b);
+        LineSegment seg2 = new LineSegment(a, b);
+        //四点共点判断
+        if (seg1.equals(seg2)) return true;
+        //获取C和D对于线段1的投影因子
+        double tC = seg1.projectionFactor(c);
+        double tD = seg1.projectionFactor(d);
+        //获取最小和最大投影因子
+        double cdMin = Math.min(tC, tD);
+        double cdMax = Math.max(tC, tD);
+        //排除前后延伸联通线
+        if (cdMin == 1 && cdMax >= 1) return false;
+        if (cdMax == 0 && cdMin <= 0) return false;
+        //判断最大因子是否向前延伸或处于线段内,并且最小因子是否向后延伸或处于线段内
+        return (cdMax + EPSILON >= 0) && (cdMin - EPSILON <= 1);
+    }
+}

+ 2 - 1
src/main/java/com/shkpr/service/alambizplugin/configuration/MainSourceConfiguration.java

@@ -11,6 +11,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
 import javax.sql.DataSource;
@@ -85,7 +86,7 @@ public class MainSourceConfiguration {
     public SqlSessionFactory mainSqlSessionFactoryBean(@Qualifier("mainDatasource") DataSource dataSource) throws Exception {
         SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
         sessionFactoryBean.setDataSource(dataSource);
-
+        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
         org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
         configuration.setCallSettersOnNulls(true);       //数据库中字段值为null时也要求返回
         configuration.setMapUnderscoreToCamelCase(true); //开启驼峰映射

+ 32 - 8
src/main/java/com/shkpr/service/alambizplugin/configuration/ScheduleTaskConfiguration.java

@@ -5,33 +5,38 @@ import com.global.base.log.LogPrintMgr;
 import com.shkpr.service.alambizplugin.commtools.TimeTool;
 import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.boot.task.TaskExecutorBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.TaskScheduler;
 import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.scheduling.config.ScheduledTaskRegistrar;
 
 import javax.annotation.PostConstruct;
 import java.lang.reflect.Method;
+import java.time.Duration;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.Executor;
 
 @Configuration
 @EnableScheduling
+@EnableAsync
 public class ScheduleTaskConfiguration implements SchedulingConfigurer, AsyncConfigurer {
-    private String mStrClassName;
+    private final String mStrClassName;
+
     public ScheduleTaskConfiguration() {
         mStrClassName = this.getClass().getSimpleName();
     }
 
     @Override
     public Executor getAsyncExecutor() {
-        Executor executor = taskScheduler();
-        return executor;
+        return taskExecutor();
     }
 
     @Override
@@ -40,7 +45,7 @@ public class ScheduleTaskConfiguration implements SchedulingConfigurer, AsyncCon
             @Override
             public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                 LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, LogFlagBusiType.BUSI_INIT.toStrValue(), mStrClassName
-                        ,String.format("AsyncUncaughtException(method="+method.getName()+", msg="+ex.getMessage()+")..."));
+                        , String.format("AsyncUncaughtException(method=" + method.getName() + ", msg=" + ex.getMessage() + ")..."));
             }
         };
     }
@@ -51,19 +56,38 @@ public class ScheduleTaskConfiguration implements SchedulingConfigurer, AsyncCon
         scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
     }
 
-    /** 定时任务多线程处理 */
+    /**
+     * 定时任务多线程处理
+     */
     @Bean(destroyMethod = "shutdown")
-    public ThreadPoolTaskScheduler taskScheduler(){
+    public ThreadPoolTaskScheduler taskScheduler() {
         ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
-        scheduler.setPoolSize(5);
+        scheduler.setPoolSize(2);
         scheduler.setThreadNamePrefix("Time-Task-");
         scheduler.setAwaitTerminationSeconds(5);
         scheduler.setWaitForTasksToCompleteOnShutdown(true);
         return scheduler;
     }
 
+    /**
+     * 异步任务多线程处理
+     *
+     * @return 线程任务执行
+     */
+    @Bean(destroyMethod = "shutdown", name = "asyncThreadPoolTaskExecutor")
+    public ThreadPoolTaskExecutor taskExecutor() {
+        TaskExecutorBuilder builder = new TaskExecutorBuilder();
+        builder = builder.queueCapacity(Integer.MAX_VALUE);
+        builder = builder.corePoolSize(8);
+        builder = builder.maxPoolSize(Integer.MAX_VALUE);
+        builder = builder.allowCoreThreadTimeOut(true);   // 允许核心线程超时回收:减少空闲资源占用[1](@ref)
+        builder = builder.keepAlive(Duration.ofSeconds(30)); // 非核心线程空闲存活时间:建议30-60秒[1,4](@ref)
+        builder = builder.threadNamePrefix("AsyncTask-");  // 线程名前缀:便于监控和日志追踪[1,4](@ref)
+        return builder.build();
+    }
+
     @PostConstruct
-    public void init(){
+    public void init() {
         new Timer().schedule(new TimerTask() {
             @Override
             public void run() {

+ 2 - 0
src/main/java/com/shkpr/service/alambizplugin/constants/ApiURI.java

@@ -25,6 +25,8 @@ public class ApiURI {
     public static final String URI_XXX_CHG_PWD = "pwd-chg";
     public static final String URI_XXX_IMAGES = "images";
     public static final String URI_XXX_SYS_CHECK = "sys-check";
+    public static final String URI_XXX_SYS_CHECK_GET = "sys-check-get";
+    public static final String URI_XXX_SYS_CHECK_DEL = "sys-check-del";
 
     public static final String URI_ACCESS_TOKEN_CHECK = "/kpr-plugin/apply/access-token-check";
     public static final String URI_FILE_BUSI_XXX = "/files/**";

+ 35 - 0
src/main/java/com/shkpr/service/alambizplugin/constants/GisSurveyCheckStatusEnum.java

@@ -0,0 +1,35 @@
+package com.shkpr.service.alambizplugin.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 系统检查状态枚举、
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.0
+ */
+@AllArgsConstructor
+@Getter
+public enum GisSurveyCheckStatusEnum {
+    /**
+     * 项目
+     */
+    IN_PROGRESS(0),
+    /**
+     * 任务
+     */
+    SUCCESS(1),
+    /**
+     * 失败
+     */
+    FAIL(2),
+    /**
+     * 已存在
+     */
+    NOT_EXISTS(3);
+    /**
+     * code
+     */
+    private final Integer code;
+}

+ 31 - 0
src/main/java/com/shkpr/service/alambizplugin/constants/GisSurveyCheckTypeEnum.java

@@ -0,0 +1,31 @@
+package com.shkpr.service.alambizplugin.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 系统检查类型枚举
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.0
+ */
+@AllArgsConstructor
+@Getter
+public enum GisSurveyCheckTypeEnum {
+    /**
+     * 项目
+     */
+    PROJECT(0, "project"),
+    /**
+     * 任务
+     */
+    JOB(1, "job");
+    /**
+     * code
+     */
+    private final Integer code;
+    /**
+     * 标识
+     */
+    private final String flag;
+}

+ 186 - 31
src/main/java/com/shkpr/service/alambizplugin/controller/ApiGisSurveyController.java

@@ -2,80 +2,235 @@ package com.shkpr.service.alambizplugin.controller;
 
 import com.global.base.log.LogLevelFlag;
 import com.global.base.log.LogPrintMgr;
-import com.shkpr.service.alambizplugin.apiparam.JPGetOne;
+import com.shkpr.service.alambizplugin.apiparam.GisSurveyCheckParams;
 import com.shkpr.service.alambizplugin.bizservice.GisSurveyBizService;
 import com.shkpr.service.alambizplugin.commtools.CommTool;
 import com.shkpr.service.alambizplugin.constants.ApiURI;
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckStatusEnum;
 import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
 import com.shkpr.service.alambizplugin.constants.ResponseCode;
 import com.shkpr.service.alambizplugin.controllerfilter.TokenAuthenticationService;
 import com.shkpr.service.alambizplugin.controllervalid.CommonParamValidSK;
+import com.shkpr.service.alambizplugin.dto.GisSurveyCheckResult;
 import com.shkpr.service.alambizplugin.dto.ResponseRes;
 import com.shkpr.service.alambizplugin.exception.SelfException;
 import org.springframework.validation.BindingResult;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @RequestMapping(ApiURI.URI_GIS_SURVEY_H)
 @RestController
 public class ApiGisSurveyController {
+    //message
     final String MSG_SUCCESS = "success.";
     final String MSG_FAILED = "failed.";
-    private String mStrClassName = "", mBizType = "";
-    private AtomicInteger mSeqSysCheckReq = null;
+    final String MSG_NOT_EXISTS = "notExists.";
+    //log
+    private final String mStrClassName;
+    private final String mBizType;
+    //计数器
+    private final AtomicInteger mSeqSysCheckReq;
+    private final AtomicInteger mSeqSysCheckGetReq;
+    private final AtomicInteger mSeqSysCheckDelReq;
 
-    public ApiGisSurveyController() {
+    private final GisSurveyBizService gisSurveyBizService;
+
+    public ApiGisSurveyController(GisSurveyBizService gisSurveyBizService) {
         mStrClassName = "ApiGisSurveyController";
         mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
         mSeqSysCheckReq = new AtomicInteger(0);
+        mSeqSysCheckGetReq = new AtomicInteger(0);
+        mSeqSysCheckDelReq = new AtomicInteger(0);
+        this.gisSurveyBizService = gisSurveyBizService;
     }
 
+    /**
+     * 执行系统检查
+     *
+     * @param request       request
+     * @param strClientType 客户端类型
+     * @param strUserAgent  用户信息
+     * @param oJsonParam    参数
+     * @param bindRes       bindingResult
+     * @return 执行状态
+     * @throws SelfException selfException
+     */
     @PostMapping(value = ApiURI.URI_XXX_SYS_CHECK)
-    public ResponseRes sysCheck(HttpServletRequest request
-            , @RequestHeader(value= ApiURI.HEADER_CLIENT_TYPE, required=false) String strClientType
-            , @RequestHeader(value= ApiURI.HEADER_USER_AGENT, required=false) String strUserAgent
-            , @RequestBody(required=false) @Validated(value={CommonParamValidSK.class}) JPGetOne oJsonParam
-            , BindingResult bindRes) throws Exception{
+    public ResponseRes<GisSurveyCheckResult> sysCheck(HttpServletRequest request
+            , @RequestHeader(value = ApiURI.HEADER_CLIENT_TYPE, required = false) String strClientType
+            , @RequestHeader(value = ApiURI.HEADER_USER_AGENT, required = false) String strUserAgent
+            , @RequestBody(required = false) @Validated(value = {CommonParamValidSK.class}) GisSurveyCheckParams oJsonParam
+            , BindingResult bindRes) throws SelfException {
+        //参数校验
         final String URI_PATH = request.getRequestURI();
         final String strPlatform = CommTool.getPlatformByAgent(strClientType, strUserAgent);
-        final String strUserId = (String)request.getAttribute(TokenAuthenticationService.HEADER_USERID);
-        if (oJsonParam == null || bindRes.hasErrors() || !oJsonParam.checkValid()){
+        final String strUserId = (String) request.getAttribute(TokenAuthenticationService.HEADER_USERID);
+        if (oJsonParam == null || bindRes.hasErrors() || !oJsonParam.checkValid()) {
             throw new SelfException(ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrCode()
                     , String.format(ApiURI.EXCEPTION_FORMAT
                     , strPlatform
                     , URI_PATH
                     , ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrMsg()));
         }
-
+        //begin
         long llReqBefore = System.currentTimeMillis();
         String strRunSeq = String.format("%d-%d", llReqBefore, mSeqSysCheckReq.incrementAndGet());
         LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
-                ,String.format("%s:%s seq:{%s} param:%s begin====>"
-                        ,strPlatform
-                        ,URI_PATH
-                        ,strRunSeq
-                        ,oJsonParam.toString()));
-
-        ResponseRes<String> resResult = new ResponseRes<String>();
+                , String.format("%s:%s seq:{%s} param:%s begin====>"
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , oJsonParam));
+        //构建result
+        ResponseRes<GisSurveyCheckResult> resResult = new ResponseRes<>();
         resResult.setResmsg(MSG_FAILED);
-        resResult.setResdata("");
+        resResult.setResdata(null);
+        //执行系统检查
+        GisSurveyCheckResult result = gisSurveyBizService.sysCheckFun(oJsonParam);
+        resResult.setRescode(ResponseCode.STATUS_SUCCESS.toStrCode());
+        resResult.setResdata(result);
+        resResult.setResmsg(MSG_SUCCESS);
+        //end
+        resResult.setTimestamp(System.currentTimeMillis());
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
+                , String.format("%s:%s seq:{%s} rescode:{%s} resmsg:{%s} time:{%d ms} end<===="
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , resResult.getRescode()
+                        , resResult.getResmsg()
+                        , resResult.getTimestamp() - llReqBefore));
+        return resResult;
+    }
 
-        ResponseCode code = GisSurveyBizService.sysCheckFun();
-        resResult.setRescode(code.toStrCode());
-        resResult.setResmsg((code==ResponseCode.RESULT_NORMAL)?MSG_SUCCESS:code.toStrMsg());
+    /**
+     * 获取系统检查
+     *
+     * @param request       request
+     * @param strClientType 客户端类型
+     * @param strUserAgent  用户信息
+     * @param oJsonParam    参数
+     * @param bindRes       bindingResult
+     * @return 系统检查回执
+     * @throws SelfException selfException
+     */
+    @PostMapping(value = ApiURI.URI_XXX_SYS_CHECK_GET)
+    public ResponseRes<GisSurveyCheckResult> getCheck(HttpServletRequest request
+            , @RequestHeader(value = ApiURI.HEADER_CLIENT_TYPE, required = false) String strClientType
+            , @RequestHeader(value = ApiURI.HEADER_USER_AGENT, required = false) String strUserAgent
+            , @RequestBody(required = false) @Validated(value = {CommonParamValidSK.class}) GisSurveyCheckParams oJsonParam
+            , BindingResult bindRes) throws Exception {
+        //入参校验
+        final String URI_PATH = request.getRequestURI();
+        final String strPlatform = CommTool.getPlatformByAgent(strClientType, strUserAgent);
+        final String strUserId = (String) request.getAttribute(TokenAuthenticationService.HEADER_USERID);
+        if (oJsonParam == null || bindRes.hasErrors() || !oJsonParam.checkValid()) {
+            throw new SelfException(ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrCode()
+                    , String.format(ApiURI.EXCEPTION_FORMAT
+                    , strPlatform
+                    , URI_PATH
+                    , ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrMsg()));
+        }
+        //begin
+        long llReqBefore = System.currentTimeMillis();
+        String strRunSeq = String.format("%d-%d", llReqBefore, mSeqSysCheckGetReq.incrementAndGet());
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
+                , String.format("%s:%s seq:{%s} param:%s begin====>"
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , oJsonParam));
+        //构建result
+        ResponseRes<GisSurveyCheckResult> resResult = new ResponseRes<>();
+        resResult.setResmsg(MSG_FAILED);
+        //尝试获取系统检查result
+        GisSurveyCheckResult result = gisSurveyBizService.getResult(oJsonParam);
+        resResult.setRescode(ResponseCode.STATUS_SUCCESS.toStrCode());
+        resResult.setResdata(result);
+        resResult.setResmsg(MSG_SUCCESS);
+        //end
+        resResult.setTimestamp(System.currentTimeMillis());
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
+                , String.format("%s:%s seq:{%s} rescode:{%s} resmsg:{%s} time:{%d ms} end<===="
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , resResult.getRescode()
+                        , resResult.getResmsg()
+                        , resResult.getTimestamp() - llReqBefore));
+        return resResult;
+    }
 
+    /**
+     * 删除系统检查
+     *
+     * @param request       request
+     * @param strClientType 客户端类型
+     * @param strUserAgent  用户信息
+     * @param oJsonParam    参数
+     * @param bindRes       bindingResult
+     * @return 删除状态
+     * @throws SelfException selfException
+     */
+    @PostMapping(value = ApiURI.URI_XXX_SYS_CHECK_DEL)
+    public ResponseRes<?> delCheck(HttpServletRequest request
+            , @RequestHeader(value = ApiURI.HEADER_CLIENT_TYPE, required = false) String strClientType
+            , @RequestHeader(value = ApiURI.HEADER_USER_AGENT, required = false) String strUserAgent
+            , @RequestBody(required = false) @Validated(value = {CommonParamValidSK.class}) GisSurveyCheckParams oJsonParam
+            , BindingResult bindRes) throws Exception {
+        //入参校验
+        final String URI_PATH = request.getRequestURI();
+        final String strPlatform = CommTool.getPlatformByAgent(strClientType, strUserAgent);
+        final String strUserId = (String) request.getAttribute(TokenAuthenticationService.HEADER_USERID);
+        if (oJsonParam == null || bindRes.hasErrors() || !oJsonParam.checkValid()) {
+            throw new SelfException(ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrCode()
+                    , String.format(ApiURI.EXCEPTION_FORMAT
+                    , strPlatform
+                    , URI_PATH
+                    , ResponseCode.STATUS_ERROR_JSON_FORMAT.toStrMsg()));
+        }
+        //begin
+        long llReqBefore = System.currentTimeMillis();
+        String strRunSeq = String.format("%d-%d", llReqBefore, mSeqSysCheckDelReq.incrementAndGet());
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
+                , String.format("%s:%s seq:{%s} param:%s begin====>"
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , oJsonParam));
+        //构建result
+        ResponseRes<Boolean> resResult = new ResponseRes<>();
+        resResult.setResmsg(MSG_FAILED);
+        //执行删除返回
+        GisSurveyCheckResult delResult = gisSurveyBizService.delResult(oJsonParam);
+        //成功
+        if (Objects.equals(delResult.getCheckStatus(), GisSurveyCheckStatusEnum.SUCCESS.getCode())) {
+            resResult.setRescode(ResponseCode.STATUS_SUCCESS.toStrCode());
+            resResult.setResmsg(MSG_SUCCESS);
+        }
+        //不存在
+        else if (Objects.equals(delResult.getCheckStatus(), GisSurveyCheckStatusEnum.NOT_EXISTS.getCode())) {
+            resResult.setRescode(ResponseCode.STATUS_SUCCESS.toStrCode());
+            resResult.setResmsg(MSG_NOT_EXISTS);
+        }
+        //end
         resResult.setTimestamp(System.currentTimeMillis());
         LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, strUserId
-                ,String.format("%s:%s seq:{%s} rescode:{%s} resmsg:{%s} time:{%d ms} end<===="
-                        ,strPlatform
-                        ,URI_PATH
-                        ,strRunSeq
-                        ,resResult.getRescode()
-                        ,resResult.getResmsg()
-                        ,resResult.getTimestamp()-llReqBefore));
+                , String.format("%s:%s seq:{%s} rescode:{%s} resmsg:{%s} time:{%d ms} end<===="
+                        , strPlatform
+                        , URI_PATH
+                        , strRunSeq
+                        , resResult.getRescode()
+                        , resResult.getResmsg()
+                        , resResult.getTimestamp() - llReqBefore));
         return resResult;
     }
 }

+ 48 - 0
src/main/java/com/shkpr/service/alambizplugin/dbdao/mapper/GisSurveyLayerApplyMapper.java

@@ -0,0 +1,48 @@
+package com.shkpr.service.alambizplugin.dbdao.mapper;
+
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.cursor.Cursor;
+
+/**
+ * 采集元素处理申请mapper
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Mapper
+public interface GisSurveyLayerApplyMapper {
+    /**
+     * 根据项目id查询新增点
+     *
+     * @param projId 项目id
+     * @return 点集合
+     */
+    Cursor<GisSurveyLayerApplyPoint> findAddPointByProjId(@Param("projId") String projId);
+
+    /**
+     * 根据项目id查询新增线
+     *
+     * @param projId 项目id
+     * @return 线集合
+     */
+    Cursor<GisSurveyLayerApplyLine> findAddLineByProjId(@Param("projId") String projId);
+
+    /**
+     * 根据任务id查询新增点
+     *
+     * @param jobId 任务id
+     * @return 点集合
+     */
+    Cursor<GisSurveyLayerApplyPoint> findAddPointByJobId(@Param("jobId") String jobId);
+
+    /**
+     * 根据任务id查询新增线
+     *
+     * @param jobId 任务id
+     * @return 线集合
+     */
+    Cursor<GisSurveyLayerApplyLine> findAddLineByJobId(@Param("jobId") String jobId);
+}

+ 98 - 0
src/main/java/com/shkpr/service/alambizplugin/dbdao/pgtype/GeomLineSegmentTypeHandlePg.java

@@ -0,0 +1,98 @@
+package com.shkpr.service.alambizplugin.dbdao.pgtype;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.LineSegment;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 地理线段类型处理器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@MappedTypes({LineSegment.class})
+@MappedJdbcTypes(JdbcType.VARCHAR)
+public class GeomLineSegmentTypeHandlePg extends BaseTypeHandler<LineSegment> {
+    //正则表达式匹配 [[x1,y1],[x2,y2]] 格式
+    private static final Pattern PATTERN = Pattern.compile("^\\[\\[\\s*(-?\\d+(\\.\\d+)?),\\s*(-?\\d+(\\.\\d+)?)\\s*],\\s*\\[\\s*(-?\\d+(\\.\\d+)?),\\s*(-?\\d+(\\.\\d+)?)\\s*]\\s*]$");
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, LineSegment segment, JdbcType jdbcType)
+            throws SQLException {
+        //将 LineSegment 序列化为 [[x1,y1],[x2,y2]] 字符串
+        if (segment != null) {
+            Coordinate p0 = segment.getCoordinate(0);
+            Coordinate p1 = segment.getCoordinate(1);
+            String value = String.format("[[%s,%s],[%s,%s]]", p0.x, p0.y, p1.x, p1.y);
+            ps.setString(i, value);
+        } else {
+            //处理空值
+            ps.setNull(i, JdbcType.VARCHAR.TYPE_CODE);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public LineSegment getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        return parseString(rs.getString(columnName));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public LineSegment getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        return parseString(rs.getString(columnIndex));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public LineSegment getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        return parseString(cs.getString(columnIndex));
+    }
+
+    /**
+     * 解析
+     *
+     * @param value 值
+     * @return 线段
+     * @throws SQLException sql异常
+     */
+    private LineSegment parseString(String value) throws SQLException {
+        //非空校验
+        if (value == null || value.isEmpty()) return null;
+        //正则匹配
+        Matcher matcher = PATTERN.matcher(value);
+        if (matcher.find()) {
+            try {
+                //获取值
+                double x0 = Double.parseDouble(matcher.group(1));
+                double y0 = Double.parseDouble(matcher.group(2));
+                double x1 = Double.parseDouble(matcher.group(3));
+                double y1 = Double.parseDouble(matcher.group(4));
+                //构建线段
+                return new LineSegment(x0, y0, x1, y1);
+            } catch (NumberFormatException e) {
+                throw new SQLException("坐标格式无效: " + value, e);
+            }
+        }
+        throw new SQLException("线段格式无效: " + value);
+    }
+}

+ 92 - 0
src/main/java/com/shkpr/service/alambizplugin/dbdao/pgtype/GeomPointTypeHandlePg.java

@@ -0,0 +1,92 @@
+package com.shkpr.service.alambizplugin.dbdao.pgtype;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.PrecisionModel;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+/**
+ * 地理点类型处理器
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@MappedTypes({Point.class})
+@MappedJdbcTypes(JdbcType.VARCHAR)
+public class GeomPointTypeHandlePg extends BaseTypeHandler<Point> {
+    GeometryFactory factory = new GeometryFactory(new PrecisionModel(), 4490);
+
+    /**
+     * 将Java类型Point写入数据库
+     */
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, Point point, JdbcType jdbcType) throws SQLException {
+        if (point != null) {
+            //将 Point 序列化化为 [x,y] 字符串
+            String pointStr = String.format("[%f,%f]", point.getX(), point.getY());
+            ps.setString(i, pointStr);
+        } else {
+            //处理空值
+            ps.setNull(i, Types.VARCHAR);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Point getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        String pointStr = rs.getString(columnName);
+        return rs.wasNull() ? null : parsePoint(pointStr);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Point getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        String pointStr = rs.getString(columnIndex);
+        return rs.wasNull() ? null : parsePoint(pointStr);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Point getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        String pointStr = cs.getString(columnIndex);
+        return cs.wasNull() ? null : parsePoint(pointStr);
+    }
+
+    /**
+     * 从数据库字符串解析为Point对象
+     */
+    private Point parsePoint(String pointStr) {
+        //非空校验
+        if (pointStr == null || pointStr.isEmpty()) return null;
+        //去除方括号和空白字符
+        String cleanStr = pointStr.replace("[", "").replace("]", "");
+        String[] coordinates = cleanStr.split(",");
+        //长度判断
+        if (coordinates.length != 2) throw new IllegalArgumentException("无效的点格式: " + pointStr);
+        try {
+            //获取值
+            double longitude = Double.parseDouble(coordinates[0]);
+            double latitude = Double.parseDouble(coordinates[1]);
+            //构建点
+            return factory.createPoint(new Coordinate(longitude, latitude));
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("I徐小的坐标格式: " + pointStr, e);
+        }
+    }
+}

+ 228 - 0
src/main/java/com/shkpr/service/alambizplugin/dbdao/services/GisSurveyLayerApplyServiceImpl.java

@@ -0,0 +1,228 @@
+package com.shkpr.service.alambizplugin.dbdao.services;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
+import com.shkpr.service.alambizplugin.dbdao.mapper.GisSurveyLayerApplyMapper;
+import com.shkpr.service.alambizplugin.dbdao.services.intef.GisSurveyLayerApplyService;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+import org.apache.ibatis.cursor.Cursor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 采集元素处理申请service实现
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Service
+public class GisSurveyLayerApplyServiceImpl implements GisSurveyLayerApplyService {
+    final
+    GisSurveyLayerApplyMapper gisSurveyLayerApplyMapper;
+    /**
+     * log
+     */
+    private final String mStrClassName;
+    private final String mBizType;
+
+    public GisSurveyLayerApplyServiceImpl(GisSurveyLayerApplyMapper gisSurveyLayerApplyMapper) {
+        mStrClassName = "GisSurveyLayerApplyServiceImpl";
+        mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
+        this.gisSurveyLayerApplyMapper = gisSurveyLayerApplyMapper;
+    }
+
+
+    /**
+     * 根据项目id查询新增点
+     *
+     * @param projId 项目id
+     * @return 点集合
+     */
+    @Transactional(transactionManager = "mainDbTransactionManager")
+    public List<GisSurveyLayerApplyPoint> findAddPointByProjId(String projId) throws InterruptedException {
+        //点集合
+        List<GisSurveyLayerApplyPoint> points = new ArrayList<>();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始根据项目id查询新增点,开启事务和游标 项目ID:%s"
+                        , projId
+                )
+        );
+        long begin = System.currentTimeMillis();
+        //获取游标
+        try (Cursor<GisSurveyLayerApplyPoint> cursor = gisSurveyLayerApplyMapper.findAddPointByProjId(projId)) {
+            //迭代游标
+            for (GisSurveyLayerApplyPoint point : cursor) {
+                //检查线程中断,并响应
+                if (Thread.interrupted()) throw new InterruptedException();
+                //检查游标完成
+                if (cursor.isConsumed()) return points;
+                points.add(point);
+            }
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束根据项目id查询新增点,关闭事务和游标 项目ID:%s 用时(毫秒):%d"
+                            , projId
+                            , (end - begin)
+                    )
+            );
+        } catch (IOException ioException) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format(
+                            "关闭游标失败 项目ID:%s msg:%s"
+                            , projId
+                            , ioException.getMessage()
+                    )
+            );
+        }
+        return points;
+    }
+
+    /**
+     * 根据项目id查询新增线
+     *
+     * @param projId 项目id
+     * @return 线集合
+     */
+    @Transactional(transactionManager = "mainDbTransactionManager")
+    public List<GisSurveyLayerApplyLine> findAddLineByProjId(String projId) throws InterruptedException {
+        //点集合
+        List<GisSurveyLayerApplyLine> lines = new ArrayList<>();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始根据项目id查询新增线,开启事务和游标 项目ID:%s"
+                        , projId
+                )
+        );
+        long begin = System.currentTimeMillis();
+        //获取游标
+        try (Cursor<GisSurveyLayerApplyLine> cursor = gisSurveyLayerApplyMapper.findAddLineByProjId(projId)) {
+            //迭代游标
+            for (GisSurveyLayerApplyLine point : cursor) {
+                //检查线程中断,并响应
+                if (Thread.interrupted()) throw new InterruptedException();
+                //检查游标完成
+                if (cursor.isConsumed()) return lines;
+                lines.add(point);
+            }
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束根据项目id查询新增线,关闭事务和游标 项目ID:%s 用时(毫秒):%d"
+                            , projId
+                            , (end - begin)
+                    )
+            );
+        } catch (IOException ioException) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format(
+                            "关闭游标失败 项目ID:%s msg:%s"
+                            , projId
+                            , ioException.getMessage()
+                    )
+            );
+        }
+        return lines;
+    }
+
+    /**
+     * 根据任务id查询新增点
+     *
+     * @param jobId 任务id
+     * @return 点集合
+     */
+    @Transactional(transactionManager = "mainDbTransactionManager")
+    public List<GisSurveyLayerApplyPoint> findAddPointByJobId(String jobId) throws InterruptedException {
+        //点集合
+        List<GisSurveyLayerApplyPoint> points = new ArrayList<>();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始根据任务id查询新增点,开启事务和游标 任务ID:%s"
+                        , jobId
+                )
+        );
+        long begin = System.currentTimeMillis();
+        //获取游标
+        try (Cursor<GisSurveyLayerApplyPoint> cursor = gisSurveyLayerApplyMapper.findAddPointByJobId(jobId)) {
+            //迭代游标
+            for (GisSurveyLayerApplyPoint gisSurveyLayerApplyPoint : cursor) {
+                //检查线程中断,并响应
+                if (Thread.interrupted()) throw new InterruptedException();
+                //检查游标完成
+                if (cursor.isConsumed()) return points;
+                points.add(gisSurveyLayerApplyPoint);
+            }
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束根据任务id查询新增点,关闭事务和游标 任务ID:%s 用时(毫秒):%d"
+                            , jobId
+                            , (end - begin)
+                    )
+            );
+        } catch (IOException ioException) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format(
+                            "关闭游标失败 任务ID:%s msg:%s"
+                            , jobId
+                            , ioException.getMessage()
+                    )
+            );
+        }
+        return points;
+    }
+
+    /**
+     * 根据任务id查询新增线
+     *
+     * @param jobId 任务id
+     * @return 线集合
+     */
+    @Transactional(transactionManager = "mainDbTransactionManager")
+    public List<GisSurveyLayerApplyLine> findAddLineByJobId(String jobId) throws InterruptedException {
+        //点集合
+        List<GisSurveyLayerApplyLine> lines = new ArrayList<>();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始根据任务id查询新增线,开启事务和游标 任务ID:%s"
+                        , jobId
+                )
+        );
+        long begin = System.currentTimeMillis();
+        //获取游标
+        try (Cursor<GisSurveyLayerApplyLine> cursor = gisSurveyLayerApplyMapper.findAddLineByJobId(jobId)) {
+            //迭代游标
+            for (GisSurveyLayerApplyLine line : cursor) {
+                //检查线程中断,并响应
+                if (Thread.interrupted()) throw new InterruptedException();
+                //检查游标完成
+                if (cursor.isConsumed()) return lines;
+                lines.add(line);
+            }
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束根据任务id查询新增线,关闭事务和游标 任务ID:%s 用时(毫秒):%d"
+                            , jobId
+                            , (end - begin)
+                    )
+            );
+        } catch (IOException ioException) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format(
+                            "关闭游标失败 任务ID:%s msg:%s"
+                            , jobId
+                            , ioException.getMessage()
+                    )
+            );
+        }
+        return lines;
+    }
+}

+ 46 - 0
src/main/java/com/shkpr/service/alambizplugin/dbdao/services/intef/GisSurveyLayerApplyService.java

@@ -0,0 +1,46 @@
+package com.shkpr.service.alambizplugin.dbdao.services.intef;
+
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine;
+import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint;
+
+import java.util.List;
+
+/**
+ * 采集元素处理申请service
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+public interface GisSurveyLayerApplyService {
+    /**
+     * 根据项目id查询新增点
+     *
+     * @param projId 项目id
+     * @return 点集合
+     */
+    List<GisSurveyLayerApplyPoint> findAddPointByProjId(String projId)throws InterruptedException;
+
+    /**
+     * 根据项目id查询新增线
+     *
+     * @param projId 项目id
+     * @return 线集合
+     */
+    List<GisSurveyLayerApplyLine> findAddLineByProjId(String projId)throws InterruptedException;
+
+    /**
+     * 根据任务id查询新增点
+     *
+     * @param jobId 任务id
+     * @return 点集合
+     */
+    List<GisSurveyLayerApplyPoint> findAddPointByJobId(String jobId)throws InterruptedException;
+
+    /**
+     * 根据任务id查询新增线
+     *
+     * @param jobId 任务id
+     * @return 线集合
+     */
+    List<GisSurveyLayerApplyLine> findAddLineByJobId(String jobId)throws InterruptedException;
+}

+ 103 - 0
src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyCheckResult.java

@@ -0,0 +1,103 @@
+package com.shkpr.service.alambizplugin.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.shkpr.service.alambizplugin.constants.GisSurveyCheckStatusEnum;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 系统检查返回dto
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class GisSurveyCheckResult {
+    /**
+     * 检查状态:0:进行中,1:成功,2:失败,3:不存在
+     */
+    private Integer checkStatus;
+    /**
+     * 检查类型:0:项目,1:任务
+     */
+    private Integer checkType;
+    /**
+     * 项目id
+     */
+    private String projId;
+    /**
+     * 任务id
+     */
+    private String jobId;
+    /**
+     * 请求检查时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime requestTime;
+    /**
+     * 完成检查时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh_CN", timezone = "Asia/Shanghai")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime completeTime;
+    /**
+     * 孤立点code
+     */
+    private List<String> isolatedPoints;
+    /**
+     * 孤立线code
+     */
+    private List<List<String>> isolatedLines;
+    /**
+     * 重复点集合
+     */
+    private List<List<String>> duplicatePoints;
+    /**
+     * 重叠线集合
+     */
+    private List<List<String>> overlapLines;
+
+    /**
+     * 进行中
+     */
+    public static GisSurveyCheckResult inProgress(LocalDateTime requestTime) {
+        GisSurveyCheckResult result = new GisSurveyCheckResult();
+        result.setCheckStatus(GisSurveyCheckStatusEnum.IN_PROGRESS.getCode());
+        result.setRequestTime(requestTime);
+        return result;
+    }
+
+    /**
+     * 成功
+     */
+    public static GisSurveyCheckResult success() {
+        GisSurveyCheckResult result = new GisSurveyCheckResult();
+        result.setCheckStatus(GisSurveyCheckStatusEnum.SUCCESS.getCode());
+        result.setRequestTime(LocalDateTime.now());
+        return result;
+    }
+
+    /**
+     * 失败
+     */
+    public static GisSurveyCheckResult fail() {
+        GisSurveyCheckResult result = new GisSurveyCheckResult();
+        result.setCheckStatus(GisSurveyCheckStatusEnum.FAIL.getCode());
+        result.setRequestTime(LocalDateTime.now());
+        return result;
+    }
+
+    /**
+     * 不存在
+     */
+    public static GisSurveyCheckResult notExists() {
+        GisSurveyCheckResult result = new GisSurveyCheckResult();
+        result.setCheckStatus(GisSurveyCheckStatusEnum.NOT_EXISTS.getCode());
+        return result;
+    }
+
+}

+ 40 - 0
src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyCheckTaskId.java

@@ -0,0 +1,40 @@
+package com.shkpr.service.alambizplugin.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+import java.util.Objects;
+
+/**
+ * 系统任务id
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Getter
+@AllArgsConstructor
+@ToString
+public class GisSurveyCheckTaskId {
+    /**
+     * 任务类型:0:项目,1:任务
+     */
+    private final Integer checkType;
+    /**
+     * 标识(项目code/任务code)
+     */
+    private final String code;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        GisSurveyCheckTaskId that = (GisSurveyCheckTaskId) o;
+        return Objects.equals(checkType, that.checkType) && Objects.equals(code, that.code);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(checkType, code);
+    }
+}

+ 51 - 0
src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyLayerApplyLine.java

@@ -0,0 +1,51 @@
+package com.shkpr.service.alambizplugin.dto;
+
+import lombok.Data;
+import org.locationtech.jts.geom.LineSegment;
+
+/**
+ * 采集元素处理申请线
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class GisSurveyLayerApplyLine {
+    /**
+     * 元素唯一编码
+     */
+    private String code;
+    /**
+     * 所属任务的唯一ID
+     */
+    private String jobId;
+    /**
+     * 元素类型标识符
+     */
+    private String layer;
+    /**
+     * 元素分类:point--点;line--线;face--面
+     */
+    private String kind;
+    /**
+     * 坐标值
+     */
+    private LineSegment gis;
+    /**
+     * 操作标记:add/update/delete
+     */
+    private String apply;
+    /**
+     * 数据来源:0--APP;1--Web;255--第三方导入
+     */
+    private Short source;
+    /**
+     * 线的起点唯一编码
+     */
+    private String upNode;
+
+    /**
+     * 线的终点唯一编码
+     */
+    private String downNode;
+}

+ 46 - 0
src/main/java/com/shkpr/service/alambizplugin/dto/GisSurveyLayerApplyPoint.java

@@ -0,0 +1,46 @@
+package com.shkpr.service.alambizplugin.dto;
+
+import lombok.Data;
+import org.locationtech.jts.geom.Point;
+
+/**
+ * 采集元素处理申请点
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.0
+ */
+@Data
+public class GisSurveyLayerApplyPoint {
+    /**
+     * 元素唯一编码
+     */
+    private String code;
+    /**
+     * 所属任务的唯一ID
+     */
+    private String jobId;
+    /**
+     * 元素类型标识符
+     */
+    private String layer;
+    /**
+     * 元素分类:point--点;line--线;face--面
+     */
+    private String kind;
+    /**
+     * 坐标值
+     */
+    private Point gis;
+    /**
+     * 操作标记:add/update/delete
+     */
+    private String apply;
+    /**
+     * 数据来源:0--APP;1--Web;255--第三方导入
+     */
+    private Short source;
+    /**
+     * 点的高程
+     */
+    private Double elevation;
+}

+ 5 - 52
src/main/java/com/shkpr/service/alambizplugin/dto/ResponseRes.java

@@ -1,5 +1,10 @@
 package com.shkpr.service.alambizplugin.dto;
 
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
 public class ResponseRes<T> {
     private long timestamp;
     private String rescode;
@@ -7,59 +12,7 @@ public class ResponseRes<T> {
     private T resdata;//data;
 
     public ResponseRes() {
-    }
-
-    public ResponseRes(String rescode) {
-        this.rescode = rescode;
-    }
-
-    public ResponseRes(long timestamp, String rescode, String resmsg, T resdata) {
-        this.timestamp = timestamp;
-        this.rescode = rescode;
-        this.resmsg = resmsg;
-        this.resdata = resdata;
-    }
-
-    public long getTimestamp() {
-        return timestamp;
-    }
-
-    public void setTimestamp(long timestamp) {
-        this.timestamp = timestamp;
-    }
-
-    public String getRescode() {
-        return rescode;
-    }
-
-    public void setRescode(String rescode) {
-        this.rescode = rescode;
-    }
-
-    public String getResmsg() {
-        return resmsg;
-    }
-
-    public void setResmsg(String resmsg) {
-        this.resmsg = resmsg;
-    }
-
-    public T getResdata() {
-        return resdata;
-    }
-
-    public void setResdata(T resdata) {
-        this.resdata = resdata;
-    }
 
-    @Override
-    public String toString() {
-        return "ResponseRes{" +
-                "timestamp=" + timestamp +
-                ", rescode='" + rescode + '\'' +
-                ", resmsg='" + resmsg + '\'' +
-                ", resdata=" + resdata +
-                '}';
     }
 }
 

+ 34 - 14
src/main/java/com/shkpr/service/alambizplugin/globalmgr/ScheduleTaskMgr.java

@@ -3,13 +3,15 @@ package com.shkpr.service.alambizplugin.globalmgr;
 import com.global.base.log.LogLevelFlag;
 import com.global.base.log.LogPrintMgr;
 import com.global.base.thread.ThreadPoolProxy;
+import com.shkpr.service.alambizplugin.bizservice.GisSurveyBizService;
 import com.shkpr.service.alambizplugin.commtools.TimeTool;
 import com.shkpr.service.alambizplugin.components.GisDynamicDataSource;
 import com.shkpr.service.alambizplugin.components.locks.AlarmOrderLockMgr;
 import com.shkpr.service.alambizplugin.components.locks.SurveyBizLockMgr;
 import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
 import com.zaxxer.hikari.HikariDataSource;
-import org.springframework.scheduling.annotation.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.PostConstruct;
@@ -19,22 +21,40 @@ import java.util.Map;
 import java.util.Set;
 
 @Component
-@EnableScheduling   // 1.开启定时任务
-@EnableAsync        // 2.开启多线程
 public class ScheduleTaskMgr {
-    private String mStrClassName;
-    public ScheduleTaskMgr() {
+    final
+    GisSurveyBizService gisSurveyBizService;
+
+    /**
+     * 系统检查任务超时时间
+     */
+    @Value("${system-check.ttl}")
+    private Long ttl;
+    private final String mStrClassName;
+
+    public ScheduleTaskMgr(GisSurveyBizService gisSurveyBizService) {
         mStrClassName = this.getClass().getSimpleName();
+        this.gisSurveyBizService = gisSurveyBizService;
     }
 
     @PostConstruct
-    public void afterPropertiesSet() throws Exception{
+    public void afterPropertiesSet() throws Exception {
         LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, LogFlagBusiType.BUSI_INIT.toStrValue(), mStrClassName
-                ,String.format("init()..."));
+                , "init()...");
     }
 
     @PreDestroy
-    public void destroy() throws Exception{
+    public void destroy() throws Exception {
+    }
+
+    /**
+     * 系统检查超时任务
+     */
+    @Scheduled(fixedRateString = "${system-check.ttl-check-interval}")
+    public void sysCheckTasksTtl() {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue(), mStrClassName
+                , String.format("开始执行系统检查超时任务 ttl:%s ======>", ttl));
+        if (ttl != null) gisSurveyBizService.expireResult(ttl);
     }
 
     @Scheduled(cron = "${cron.refresh.timer.clock}")
@@ -51,21 +71,21 @@ public class ScheduleTaskMgr {
 
     //@Scheduled(cron = "0 0 5 * * ?") //每天凌晨05:00:00执行
     //@Scheduled(initialDelay = 600000, fixedRate = 600000) //延迟10分钟后第一次调用该方法,此后每隔10分钟再执行一次
-    public void clearLocalCache(){
+    public void clearLocalCache() {
     }
 
     @Scheduled(cron = "0 0/10 * * * ?")
-    public void keepDBConnectAlive(){
+    public void keepDBConnectAlive() {
         Set<Map.Entry<Object, Object>> entrySet = GisDynamicDataSource.listTargetDataSource().entrySet();
-        for (Map.Entry<Object, Object> entry : entrySet){
-            HikariDataSource dataSource = (HikariDataSource)entry.getValue();
+        for (Map.Entry<Object, Object> entry : entrySet) {
+            HikariDataSource dataSource = (HikariDataSource) entry.getValue();
             if (dataSource != null) {
                 try {
                     Class.forName(dataSource.getDriverClassName());
                     //Connection connection = DriverManager.getConnection(dataSource.getJdbcUrl(), dataSource.getUsername(), dataSource.getPassword());
                     //connection.close();
-                    int step = dataSource.getHikariPoolMXBean().getIdleConnections() - dataSource.getMinimumIdle()*2;
-                    while ((step--) > 0){
+                    int step = dataSource.getHikariPoolMXBean().getIdleConnections() - dataSource.getMinimumIdle() * 2;
+                    while ((step--) > 0) {
                         Connection connection = dataSource.getConnection();
                         dataSource.evictConnection(connection);
                     }

+ 58 - 54
src/main/resources/application.properties

@@ -1,39 +1,39 @@
-#项目的全局配置==============>
+#椤圭洰鐨勫叏灞€閰嶇疆==============>
 spring.application.name=v1-alam-biz-plugin
-# 项目contextPath
+# 椤圭洰contextPath
 server.servlet.context-path= /
-# 错误页,指定发生错误时,跳转的URL。请查看BasicErrorController源码便知
+# 閿欒�椤碉紝鎸囧畾鍙戠敓閿欒�鏃讹紝璺宠浆鐨刄RL銆傝�鏌ョ湅BasicErrorController婧愮爜渚跨煡
 server.error.path= /
-# 服务端口
+# 鏈嶅姟绔�彛
 server.port=9010
 #global.lan.test.log=true
 
-#spring boot 2.0在开启https时,若运行环境安装openssl后将以https-openssl模式运行,若未安装openssl时将以https-jsse模式运行
+#spring boot 2.0鍦ㄥ紑鍚痟ttps鏃讹紝鑻ヨ繍琛岀幆澧冨畨瑁卭penssl鍚庡皢浠�ttps-openssl妯″紡杩愯�锛岃嫢鏈�畨瑁卭penssl鏃跺皢浠�ttps-jsse妯″紡杩愯�
 #server.ssl.enabled=true
 #server.ssl.key-store=classpath:alam_server.pfx
 #server.ssl.key-store-password=Tri12345678
 #server.ssl.keyStoreType=PKCS12
 #server.ssl.key-alias=alias
 
-# session最大超时时间(秒钟),默认为30
+# session最大超时时间(秒钟),默认为30
 server.servlet.session.timeout=60s
-# 最大链接超时时间
+# 最大链接超时时间
 server.connection-timeout=60s
 
-#Tomcat配置================>
-# tomcat的URI编码
+#Tomcat閰嶇疆================>
+# tomcat鐨刄RI缂栫爜
 server.tomcat.uri-encoding=UTF-8
-# 存放Tomcat的日志、Dump等文件的临时文件夹,默认为系统的tmp文件夹(如:C:\Users\Shanhy\AppData\Local\Temp)
+# 存放Tomcat的日志、Dump等文件的临时文件夹,默认为系统的tmp文件夹(如:C:\Users\Shanhy\AppData\Local\Temp)
 server.tomcat.basedir=./trilog/tomcattmp
-# Tomcat的Access日志开关(默认值false):
+# Tomcat的Access日志开关(默认值false):
 server.tomcat.access-log-enabled=false
-# Tomcat的Access日志格式[common 或 combined],默认值common,注:若设置为空则日志文件内容为空
+# Tomcat的Access日志格式[common 或 combined],默认值common,注:若设置为空则日志文件内容为空
 server.tomcat.access-log-pattern=common
 
-## Freemarker 配置
-## 文件配置路径
+## Freemarker 閰嶇疆
+## 鏂囦欢閰嶇疆璺�緞
 spring.freemarker.template-loader-path=classpath:/templates/
-## 对于静态资源建议开启缓存
+## 对于静态资源建议开启缓存
 spring.freemarker.cache=true
 spring.freemarker.charset=UTF-8
 spring.freemarker.check-template-location=true
@@ -42,92 +42,92 @@ spring.freemarker.expose-request-attributes=true
 spring.freemarker.expose-session-attributes=true
 spring.freemarker.request-context-attribute=request
 spring.freemarker.suffix=.ftl
-#设定静态文件路径,js,css等
+#设定静态文件路径,js,css等
 spring.mvc.static-path-pattern=/static/**
 
-# tomcat是否开启Apr
+# tomcat鏄�惁寮€鍚疉pr
 global.server.apr=false
-# 本服务所在的部署机器的索引编号
+# 本服务所在的部署机器的索引编号
 global.machine.index=0
-# 本服务在部署机器上的服务索引编号
+# 鏈�湇鍔″湪閮ㄧ讲鏈哄櫒涓婄殑鏈嶅姟绱㈠紩缂栧彿
 global.service.index=5
-# 本服务的ID
+# 鏈�湇鍔$殑ID
 global.server.id=tri.gispg.v100
-# tomcat同一时刻可接收的第三方最大并发连接数(Java NIO模式下默认是10000)
+# tomcat鍚屼竴鏃跺埢鍙�帴鏀剁殑绗�笁鏂规渶澶у苟鍙戣繛鎺ユ暟(Java NIO妯″紡涓嬮粯璁ゆ槸10000)
 global.max.tomcat.concurrent.request=10000
-# tomcat可启动的最大线程数(默认值为200),调优取值[200,1000],注:在shell脚本中根据当前配置动态计算
+# tomcat可启动的最大线程数(默认值为200),调优取值[200,1000],注:在shell脚本中根据当前配置动态计算
 global.max.tomcat.threads=400
-# tomcat启动的线程数达到最大时,接受排队的请求个数(默认值为100),调优取值[100,400],注:在shell脚本中根据当前配置动态计算
+# tomcat启动的线程数达到最大时,接受排队的请求个数(默认值为100),调优取值[100,400],注:在shell脚本中根据当前配置动态计算
 global.max.tomcat.accept.queue=200
 
-#===================Redis配置文件路径====================
+#===================Redis閰嶇疆鏂囦欢璺�緞====================
 #global.redis.config.path=./redis.properties
 
-#===================数据库配置===========================
+#===================数据库配置===========================
 global.sql.config.path=./sql.properties
 
-#开启mybatis内部调试日志输出
+#寮€鍚痬ybatis鍐呴儴璋冭瘯鏃ュ織杈撳嚭
 #logging.level.com.sqlmybatis.test.mapper2th=debug
 
-#===========日志打印配置===================
-#是否开启基于TraceId的日志记录
+#===========鏃ュ織鎵撳嵃閰嶇疆===================
+#是否开启基于TraceId的日志记录
 global.switch.trace.log=true
-#所有业务Info级别的日志开关标识
+#所有业务Info级别的日志开关标识
 global.log.switch.info=true
-#用户业务Info级别的日志开关标识
+#用户业务Info级别的日志开关标识
 global.log.switch.info.user=true
-#验证业务Info级别的日志开关标识
+#验证业务Info级别的日志开关标识
 global.log.switch.info.auth=true
 
-#Database中用户业务Info级别的日志开关标识
+#Database中用户业务Info级别的日志开关标识
 global.log.switch.info.db.user=true
 
-#============全局配置参数===================
-#是否启用本地缓存策略
+#============鍏ㄥ眬閰嶇疆鍙傛暟===================
+#鏄�惁鍚�敤鏈�湴缂撳瓨绛栫暐
 global.local.cache.switch=true
 
-#============本地存储配置参数===================
-#Cache可存储的最大app菜单项数量
+#============鏈�湴瀛樺偍閰嶇疆鍙傛暟===================
+#Cache可存储的最大app菜单项数量
 local.cache.max.size.apis.total=40
-#Cache存储app菜单项的有效时长(单位:分钟)
+#Cache存储app菜单项的有效时长(单位:分钟)
 local.cache.expired.apis.total=1440
-#Cache可存储的最大app菜单项套餐数量
+#Cache可存储的最大app菜单项套餐数量
 local.cache.max.size.case.total=40
-#Cache存储app菜单项套餐的有效时长(单位:分钟)
+#Cache存储app菜单项套餐的有效时长(单位:分钟)
 local.cache.expired.case.total=1440
 
-#=============内部运维账号配置===================
+#=============鍐呴儴杩愮淮璐﹀彿閰嶇疆===================
 global.internal.operator.account=TriCooperation
 global.internal.operator.password=$2b$10$Scz7AoeOTUB69piABNLOEepFU7OesRDRStP43g9r7QFw0Go7KTo4u
 
-#本机的内部通信可信任口令
+#本机的内部通信可信任口令
 global.internal.call.password=Tri@Tech
-#基础服务的内部通信可信任口令
+#基础服务的内部通信可信任口令
 cloud.base.service.call.password=Tri@Tech
-#基础服务访问地址
+#鍩虹�鏈嶅姟璁块棶鍦板潃
 cloud.base.service.address=https://140.246.183.164:9000
-#主GeoServer服务访问地址
+#涓籊eoServer鏈嶅姟璁块棶鍦板潃
 cloud.geo.server.master=http://140.246.183.164:8090/
-#从GeoServer服务访问地址列表(多地址以逗号分隔)
+#浠嶨eoServer鏈嶅姟璁块棶鍦板潃鍒楄〃(澶氬湴鍧€浠ラ€楀彿鍒嗛殧)
 cloud.geo.server.slave.list=
 #cloud.geo.server.address=https://183.64.62.101:9059/
-#GeoServer服务Gis坐标系标准
+#GeoServer服务Gis坐标系标准
 cloud.geo.server.srs=EPSG:4490
-#GeoServer服务{账号:密码}的base64编码
+#GeoServer鏈嶅姟{璐﹀彿:瀵嗙爜}鐨刡ase64缂栫爜
 #cloud.geo.server.basic.tk=YWRtaW46a3ByLmdlb3NlcnZlcg==
 cloud.geo.server.basic.tk=YWRtaW46a3ByLmdlb3NlcnZlckAyMzA3
-#天地图的访问许可证(即:tk)
+#天地图的访问许可证(即:tk)
 tdt.license.key=
 
 #=================ScheduleTaskTime=========================
-#每天凌晨00:00:00到00:05:00,每隔一分钟触发一次
+#每天凌晨00:00:00到00:05:00,每隔一分钟触发一次
 cron.refresh.timer.clock=0 0-5 0 * * ?
-#每天凌晨02:00:00执行
+#姣忓ぉ鍑屾櫒02:00:00鎵ц�
 cron.refresh.wfs.feature.data=0 0 2 * * ?
-#每天凌晨00:05:00执行
+#姣忓ぉ鍑屾櫒00:05:00鎵ц�
 cron.refresh.tile.map.data=0 5 0 * * ?
 
-#=============fdfs配置========================
+#=============fdfs閰嶇疆========================
 fdfs.connect-timeout=30000
 fdfs.so-timeout=60000
 fdfs.tracker-list=116.63.130.83:22122
@@ -135,10 +135,14 @@ fdfs.pool.test-while-idle=true
 fdfs.pool.max-total=200
 fdfs.pool.max-total-per-key=50
 fdfs.pool.max-wait-millis=5000
-#fdfs的目标代理地址(主要用于获取或显示用)
+#fdfs鐨勭洰鏍囦唬鐞嗗湴鍧€(涓昏�鐢ㄤ簬鑾峰彇鎴栨樉绀虹敤)
 des.proxy.fdfs=http://116.63.130.83:9999
 
-
+#=============系统检查========================
+#瓒呮椂鏃堕棿(姣��)
+system-check.ttl= 1800000
+#检查周期(毫秒)
+system-check.ttl-check-interval= 60000
 
 
 

+ 1 - 1
src/main/resources/logback.xml

@@ -118,7 +118,7 @@
     </appender>
 
     <!--指定最基础的日志输出级别,低于Info级别的信息都不会输出-->
-    <root level="Info">
+    <root level="info">
         <appender-ref ref="consoleOutput" />
         <appender-ref ref="fileInfoOutput" />
         <appender-ref ref="fileWarnOutput" />

+ 78 - 0
src/main/resources/mapper/GisSurveyLayerApplyMapper.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.alambizplugin.dbdao.mapper.GisSurveyLayerApplyMapper">
+    <resultMap id="Point" type="com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyPoint">
+        <!--@Table k3_gis_survey_layer_apply-->
+        <id column="code" jdbcType="VARCHAR" property="code"/>
+        <id column="job_id" jdbcType="VARCHAR" property="jobId"/>
+        <result column="layer" jdbcType="VARCHAR" property="layer"/>
+        <result column="kind" jdbcType="VARCHAR" property="kind"/>
+        <result column="gis" jdbcType="VARCHAR" property="gis"
+                typeHandler="com.shkpr.service.alambizplugin.dbdao.pgtype.GeomPointTypeHandlePg"/>
+        <result column="apply" jdbcType="VARCHAR" property="apply"/>
+        <result column="source" jdbcType="SMALLINT" property="source"/>
+        <result column="elevation" jdbcType="DOUBLE" property="elevation"/>
+    </resultMap>
+
+    <resultMap id="Line" type="com.shkpr.service.alambizplugin.dto.GisSurveyLayerApplyLine">
+        <!--@Table k3_gis_survey_layer_apply-->
+        <id column="job_id" jdbcType="VARCHAR" property="jobId"/>
+        <id column="code" jdbcType="VARCHAR" property="code"/>
+        <result column="layer" jdbcType="VARCHAR" property="layer"/>
+        <result column="kind" jdbcType="VARCHAR" property="kind"/>
+        <result column="gis" jdbcType="VARCHAR" property="gis"
+                typeHandler="com.shkpr.service.alambizplugin.dbdao.pgtype.GeomLineSegmentTypeHandlePg"/>
+        <result column="apply" jdbcType="VARCHAR" property="apply"/>
+        <result column="source" jdbcType="SMALLINT" property="source"/>
+        <result column="up_node" jdbcType="VARCHAR" property="upNode"/>
+        <result column="down_node" jdbcType="VARCHAR" property="downNode"/>
+    </resultMap>
+
+    <select id="findAddPointByProjId" fetchSize="3000" resultMap="Point">
+        select la.code, la.job_id, la.layer, la.kind, la.gis, la.apply, la.source, la.elevation
+        from k3_gis_survey_layer_apply la
+        join k3_gis_survey_job_info jo on la.job_id = jo.uid
+        join k3_gis_survey_project_info pit on jo.proj_id = pit.uid
+        where pit.uid = #{projId,jdbcType=VARCHAR}
+        and la.apply = 'add' and kind = 'point'
+    </select>
+
+    <select id="findAddLineByProjId" fetchSize="3000" resultMap="Line">
+        select la.code, la.job_id, la.layer, la.kind, la.apply, la.source, la.up_node, la.down_node,
+        case when un.code is not null and dn.code is not null then concat('[', un.gis, ',', dn.gis, ']') else la.gis end
+        as gis
+        from k3_gis_survey_layer_apply as la
+        join k3_gis_survey_job_info jo on la.job_id = jo.uid
+        join k3_gis_survey_project_info pit on jo.proj_id = pit.uid
+        left join k3_gis_survey_layer_apply as un
+        on la.up_node = un.code and un.kind = 'point' and la.job_id = un.job_id
+        left join k3_gis_survey_layer_apply as dn
+        on la.down_node = dn.code and dn.kind = 'point' and la.job_id = dn.job_id
+        where pit.uid = #{projId,jdbcType=VARCHAR}
+        and la.kind = 'line';
+    </select>
+
+    <select id="findAddPointByJobId" fetchSize="3000" resultMap="Point">
+        select la.code, la.job_id, la.layer, la.kind, la.gis, la.apply, la.source, la.elevation
+        from k3_gis_survey_layer_apply la
+        join k3_gis_survey_job_info jo on la.job_id = jo.uid
+        join k3_gis_survey_project_info pit on jo.proj_id = pit.uid
+        where jo.uid = #{jobId,jdbcType=VARCHAR}
+        and la.apply = 'add' and kind = 'point'
+    </select>
+
+    <select id="findAddLineByJobId" fetchSize="3000" resultMap="Line">
+        select la.code, la.job_id, la.layer, la.kind, la.apply, la.source, la.up_node, la.down_node,
+        case when un.code is not null and dn.code is not null then concat('[', un.gis, ',', dn.gis, ']') else la.gis end
+        as gis
+        from k3_gis_survey_layer_apply as la
+        join k3_gis_survey_job_info jo on la.job_id = jo.uid
+        join k3_gis_survey_project_info pit on jo.proj_id = pit.uid
+        left join k3_gis_survey_layer_apply as un
+        on la.up_node = un.code and un.kind = 'point' and la.job_id = un.job_id
+        left join k3_gis_survey_layer_apply as dn
+        on la.down_node = dn.code and dn.kind = 'point' and la.job_id = dn.job_id
+        where jo.uid = #{jobId,jdbcType=VARCHAR}
+        and la.kind = 'line';
+    </select>
+</mapper>

+ 11 - 3
src/test/java/com/shkpr/service/alambizplugin/AlamBizPluginApplicationTests.java

@@ -1,12 +1,20 @@
 package com.shkpr.service.alambizplugin;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
 
+/**
+ * 测试启动类
+ */
 @SpringBootTest
-class AlamBizPluginApplicationTests {
+@RunWith(SpringRunner.class)
+public class AlamBizPluginApplicationTests {
 
-	void contextLoads() {
-	}
+    @Test
+    public void test() {
+
+    }
 
 }