From 97b502765662a9f5fc38abfc518ece749ebc9c09 Mon Sep 17 00:00:00 2001 From: Zhao Date: Fri, 12 Sep 2025 11:30:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 39 +++ ruoyi-admin/pom.xml | 5 + .../business/TTaskImportController.java | 38 ++- .../src/main/resources/application.yml | 6 + ruoyi-admin/src/test/java/GeoTest.java | 53 ++++ .../ruoyi/business/constants/TaskEnums.java | 25 ++ .../ruoyi/business/mapper/TGisCjqyMapper.java | 2 + .../business/mapper/TTaskImportMapper.java | 3 + .../ruoyi/geo/config/GeoImportTaskConfig.java | 22 ++ .../com/ruoyi/geo/constants/GeoShpType.java | 11 + .../com/ruoyi/geo/constants/GeoSysDir.java | 72 +++++ .../com/ruoyi/geo/framework/GeoCjqyTask.java | 149 +++++++++ .../geo/framework/GeoDBTaskInterface.java | 8 + .../com/ruoyi/geo/framework/GeoException.java | 14 + .../ruoyi/geo/framework/GeoFileSystem.java | 46 +++ .../ruoyi/geo/framework/GeoImportTask.java | 293 ++++++++++++++++++ .../com/ruoyi/geo/framework/GeoJtzyTask.java | 181 +++++++++++ .../com/ruoyi/geo/framework/GeoTaskLog.java | 174 +++++++++++ .../com/ruoyi/geo/framework/GeoTaskQueue.java | 261 ++++++++++++++++ .../ruoyi/geo/framework/GeoZyhycgTask.java | 179 +++++++++++ .../java/com/ruoyi/geo/object/GeoLogInfo.java | 13 + .../geo/service/GeoImportHandlerService.java | 247 +++++++++++++++ .../java/com/ruoyi/geo/structs/GeoCJQY.java | 69 +++++ .../java/com/ruoyi/geo/structs/GeoDK.java | 90 ++++++ .../java/com/ruoyi/geo/structs/GeoJTZY.java | 107 +++++++ .../java/com/ruoyi/geo/structs/GeoZYHYCG.java | 80 +++++ .../resource/mapper/TResourceLandMapper.java | 1 + .../mapper/TResourceOperationMapper.java | 2 + .../mapper/business/TGisCjqyMapper.xml | 14 +- .../mapper/business/TTaskImportMapper.xml | 11 + .../mapper/resource/TResourceLandMapper.xml | 14 +- .../resource/TResourceOperationMapper.xml | 4 + ruoyi-common/pom.xml | 17 + .../java/com/ruoyi/common/geo/GeoParser.java | 178 +++++++++++ .../com/ruoyi/common/utils/DecimalUtils.java | 5 + .../java/com/ruoyi/common/utils/sql/DB.java | 108 +++++++ 36 files changed, 2530 insertions(+), 11 deletions(-) create mode 100644 ruoyi-admin/src/test/java/GeoTest.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/business/constants/TaskEnums.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/config/GeoImportTaskConfig.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoShpType.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoSysDir.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoCjqyTask.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoDBTaskInterface.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoException.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoFileSystem.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoImportTask.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoJtzyTask.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskLog.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskQueue.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoZyhycgTask.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/object/GeoLogInfo.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/service/GeoImportHandlerService.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoCJQY.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoDK.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoJTZY.java create mode 100644 ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoZYHYCG.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/geo/GeoParser.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/DB.java diff --git a/pom.xml b/pom.xml index ed40f88..bdebf63 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ 2.1.3 4.5.13 0.4.8 + 20.0 @@ -230,6 +231,33 @@ ${net.coobird.version} + + org.springframework.boot + spring-boot-starter-test + test + ${spring-boot.version} + + + + + org.geotools + gt-shapefile + ${geotools.version} + + + + org.geotools + gt-swing + ${geotools.version} + + + + + org.geotools + gt-geojson + ${geotools.version} + + @@ -312,6 +340,17 @@ true + + osgeo + OSGeo Release Repository + https://repo.osgeo.org/repository/release/ + + false + + + true + + diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 6ec1b56..379d822 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -67,6 +67,11 @@ ruoyi-business + + org.springframework.boot + spring-boot-starter-test + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/business/TTaskImportController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/business/TTaskImportController.java index 5ec0e8c..db1a138 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/business/TTaskImportController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/business/TTaskImportController.java @@ -14,6 +14,7 @@ import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.pdf.PdfUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.translation.TranslateUtils; +import com.ruoyi.geo.service.GeoImportHandlerService; import com.ruoyi.system.service.ISysDeptService; import org.apache.commons.compress.utils.Lists; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +39,9 @@ public class TTaskImportController extends BaseController private ITTaskImportService tTaskImportService; @Autowired - private ISysDeptService deptService; + private ISysDeptService deptService; + @Autowired + private GeoImportHandlerService importHandlerService; /** * 查询导入任务列表 @@ -202,4 +205,37 @@ public class TTaskImportController extends BaseController PdfUtils.initPdf(response, pdf); } + + /** + * 开始导入任务 + */ + @PreAuthorize("@ss.hasPermi('business:import:do')") + @Log(title = "开始导入任务", businessType = BusinessType.UPDATE) + @PostMapping("/start/{taskId}") + public AjaxResult start(@PathVariable Long taskId) + { + return AjaxResult.success(importHandlerService.StartTask(taskId)); + } + + /** + * 获取导入任务日志 + */ + @PreAuthorize("@ss.hasPermi('business:import:do')") + @GetMapping("/log/{taskId}") + public AjaxResult log(@PathVariable Long taskId, Integer offset) + { + if(null == offset) + offset = 0; + return AjaxResult.success(importHandlerService.GetImportLog(taskId, offset)); + } + + /** + * 下载导入任务日志 + */ + @PreAuthorize("@ss.hasPermi('business:import:do')") + @GetMapping("/downloadLog/{taskId}") + public void downloadLog(@PathVariable Long taskId, HttpServletResponse response) + { + importHandlerService.DownloadLog(taskId, response); + } } diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 27f3d9e..920f76b 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -133,3 +133,9 @@ xss: excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* + +importTask: + # 是否启动时执行等待中的任务 + startOnBoot: false + # 下次建议读取日志的间隔(毫秒) + logNextPoll: 2000 \ No newline at end of file diff --git a/ruoyi-admin/src/test/java/GeoTest.java b/ruoyi-admin/src/test/java/GeoTest.java new file mode 100644 index 0000000..6b6cdf0 --- /dev/null +++ b/ruoyi-admin/src/test/java/GeoTest.java @@ -0,0 +1,53 @@ +import cn.hutool.core.thread.ThreadUtil; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.RuoYiApplication; +import com.ruoyi.common.geo.GeoParser; +import com.ruoyi.geo.service.GeoImportHandlerService; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +@Slf4j +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = RuoYiApplication.class) +public final class GeoTest +{ + @Resource + private GeoImportHandlerService service; + + @Test + public void test() + { + service.StartTask(4L); + //new Scanner(System.in).next(); + ThreadUtil.sleep(5000); + } + + public static void main(String[] args) + { + String PATH; + PATH = "D:/m/胜利村_shp_3857/cjqy.shp"; // cjqy + PATH = "D:/m/胜利村_shp_3857/dk.shp"; // dk + PATH = "D:/m/胜利村_shp_3857/zyhycg.shp"; // zyhycg + PATH = "D:/m/胜利村_shp_3857/jtzy.shp"; // jtzy + try(GeoParser geo = new GeoParser()) + { + geo.Open(PATH, "UTF-8"); + System.err.println(geo.GetTypeNames()); + geo.SetSource(0); + List> cjqies = geo.GetMapList(); + //List cjqies = geo.GetList(GeoCJQY.class); + //List cjqies = geo.GetList(GeoJTZY.class); + //List cjqies = geo.GetList(GeoZYHYCG.class); + //List cjqies = geo.GetList(GeoDK.class); + + System.err.println(JSON.toJSONString(cjqies)); + } + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/business/constants/TaskEnums.java b/ruoyi-business/src/main/java/com/ruoyi/business/constants/TaskEnums.java new file mode 100644 index 0000000..fe7b171 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/business/constants/TaskEnums.java @@ -0,0 +1,25 @@ +package com.ruoyi.business.constants; + +public final class TaskEnums +{ + public static final class TaskStatus + { + public static final String ST_READY = "0"; // 已准备 + public static final String ST_WAIT = "1"; // 排队中 + public static final String ST_RUNNING = "2"; // 运行中 + public static final String ST_FINISH = "3"; // 已完成 + public static final String ST_FAIL = "4"; // 失败 + public static final String ST_TERMINATED = "5"; // 被终止 + + private TaskStatus() {} + } + public static final class ImportType + { + public static final String IT_OVERRIDE = "1"; // 覆盖导入 + public static final String IT_APPEND = "2"; // 增量导入 + + private ImportType() {} + } + + private TaskEnums() {} +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TGisCjqyMapper.java b/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TGisCjqyMapper.java index 9f4852e..ddd7908 100644 --- a/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TGisCjqyMapper.java +++ b/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TGisCjqyMapper.java @@ -74,4 +74,6 @@ public interface TGisCjqyMapper * @return 结果 */ public int deleteTGisCjqyByFids(Long[] fids); + + public int deleteByCode(String importCode); } diff --git a/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TTaskImportMapper.java b/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TTaskImportMapper.java index 74d1869..305cec4 100644 --- a/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TTaskImportMapper.java +++ b/ruoyi-business/src/main/java/com/ruoyi/business/mapper/TTaskImportMapper.java @@ -74,4 +74,7 @@ public interface TTaskImportMapper * @return 结果 */ public int deleteTTaskImportByIds(Long[] ids); + + public int terminateRunningTask(); + public TTaskImport getForUpdate(Long id); } diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/config/GeoImportTaskConfig.java b/ruoyi-business/src/main/java/com/ruoyi/geo/config/GeoImportTaskConfig.java new file mode 100644 index 0000000..c3e0b35 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/config/GeoImportTaskConfig.java @@ -0,0 +1,22 @@ +package com.ruoyi.geo.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "import-task") +public class GeoImportTaskConfig +{ + public static boolean startOnBoot = false; // 是否启动时执行等待中的任务 + public static long logNextPoll = 2000; // 下次建议读取日志的间隔(毫秒) + + public void setStartOnBoot(boolean startOnBoot) + { + GeoImportTaskConfig.startOnBoot = startOnBoot; + } + + public void setLogNextPoll(long logNextPoll) + { + GeoImportTaskConfig.logNextPoll = logNextPoll; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoShpType.java b/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoShpType.java new file mode 100644 index 0000000..78679a0 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoShpType.java @@ -0,0 +1,11 @@ +package com.ruoyi.geo.constants; + +public final class GeoShpType +{ + public static final String CJQY = "cjqy"; + public static final String JTZY = "jtzy"; + public static final String ZYHYCG = "zyhycg"; + public static final String DK = "dk"; + + private GeoShpType() {} +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoSysDir.java b/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoSysDir.java new file mode 100644 index 0000000..1bb7920 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/constants/GeoSysDir.java @@ -0,0 +1,72 @@ +package com.ruoyi.geo.constants; + +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +public final class GeoSysDir +{ + // 导入日志路径 + public static final String LOG = "/log"; + // 导入zip临时解压路径 + public static final String ZIP_EXTRACT_TEMP = "/unzip"; + + public static String GetDir(String path) + { + if(StringUtils.isEmpty(path)) + path = ""; + else if(!path.startsWith("/")) + path = "/" + path; + String str = ""; + if(!path.startsWith(RuoYiConfig.getProfile())) + str += RuoYiConfig.getProfile(); + str += path; + return str; + } + + public static String CacheDir(String dir) + { + if(null == dir) + dir = ""; + return GetDir("/cache" + dir); + } + + public static String ImportDir(String dir) + { + if(null == dir) + dir = ""; + return GetDir("/import" + dir); + } + + public static String ExportDir(String dir) + { + if(null == dir) + dir = ""; + return GetDir("/export" + dir); + } + + public static String Desensitized(String path) + { + if(StringUtils.isEmpty(path)) + return path; + + String fpath = path.replaceAll("\\\\", "/"); + String fprofile = RuoYiConfig.getProfile().replaceAll("\\\\", "/"); + if(!fpath.startsWith(fprofile)) + return path; + return "" + path.substring(RuoYiConfig.getProfile().length()); + } + + public static String DesensitizedNormalized(String path) + { + if(StringUtils.isEmpty(path)) + return path; + + String fpath = path.replaceAll("\\\\", "/"); + String fprofile = RuoYiConfig.getProfile().replaceAll("\\\\", "/"); + if(!fpath.startsWith(fprofile)) + return path; + return "" + fpath.substring(fprofile.length()); + } + + private GeoSysDir() {} +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoCjqyTask.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoCjqyTask.java new file mode 100644 index 0000000..40211a9 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoCjqyTask.java @@ -0,0 +1,149 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.collection.ListUtil; +import com.ruoyi.business.domain.TGisCjqy; +import com.ruoyi.business.mapper.TGisCjqyMapper; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.geo.service.GeoImportHandlerService; +import com.ruoyi.geo.structs.GeoCJQY; +import static com.ruoyi.business.constants.TaskEnums.ImportType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class GeoCjqyTask implements GeoDBTaskInterface +{ + private final static String TYPE_NAME = "村级区域"; + + private final GeoImportTask importTask; + private List list; + + public GeoCjqyTask(GeoImportTask importTask) + { + this.importTask = importTask; + } + + public int LoadDataList() + { + list = importTask.parser.GetList(GeoCJQY.class); + importTask.logFile.WriteLine(TYPE_NAME + "shp读取数据条数: " + list.size()); + + return list.size(); + } + + public void CheckData() + { + boolean hasEmpty = list.stream().anyMatch((x) -> StringUtils.isEmpty(x.CJQYDM)); + if(hasEmpty) + { + importTask.logFile.ErrorLine("shp文件支持源中的行政区划代码缺失"); + return; + } + + List collect = list.stream().map((x) -> x.CJQYDM).distinct().collect(Collectors.toList()); + + for(String importCode : collect) + { + SysDept dept = importTask.importDeptMap.get(importCode); + if(null == dept) + { + importTask.logFile.ErrorLine("shp文件支持源中的行政区划代码在系统不存在: " + importCode); + return; + } + if(!dept.getOrgCode().startsWith(importTask.taskImport.getOrgCode())) + { + importTask.logFile.ErrorLine(TYPE_NAME + "shp文件支持源中的行政区划代码与系统不匹配: 文件({}) != 系统({})", dept.getOrgCode(), importTask.taskImport.getOrgCode()); + } + } + } + + private List LoadDatabase(String qydm) + { + TGisCjqy cond = new TGisCjqy(); + cond.setCjqydm(qydm); + List cjqies = GeoImportHandlerService.GisCjqyMapper().selectTGisCjqyList(cond); + importTask.logFile.WriteLine(TYPE_NAME + "读取系统已存在的条数: 区划代码={}, 条数={}", qydm, cjqies.size()); + return cjqies; + } + + private TGisCjqy ConvertData(GeoCJQY src, TGisCjqy dst, SysDept dept) + { + if(null == dst) + dst = new TGisCjqy(); + dst.setTheGeom(src.the_geom); + dst.setImportCode(dept.getImportCode()); + dst.setCjqydm(src.CJQYDM); + dst.setBsm(src.BSM.longValue()); + dst.setCjqymc(src.CJQYMC); + dst.setYsdm(src.YSDM); + return dst; + } + + private int CleanDatabase(String importCode) + { + if(ImportType.IT_OVERRIDE.equals(importTask.taskImport.getImportType())) + { + importTask.logFile.WriteLine(TYPE_NAME + "覆盖导入, 需清空现有数据, 然后再插入, 区划代码={}", importCode); + int ret = GeoImportHandlerService.GisCjqyMapper().deleteByCode(importCode); + importTask.logFile.WriteLine(TYPE_NAME + "清空现有数据: {}, 区划代码={}", ret, importCode); + return ret; + } + else + { + importTask.logFile.WriteLine(TYPE_NAME + "增量导入, 无需清空现有数据, 仅执行插入或更新, 区划代码={}", importCode); + return -1; + } + } + + private int WriteData(List insertList, List updateList, SysDept dept) + { + TGisCjqyMapper gisCjqyMapper = GeoImportHandlerService.GisCjqyMapper(); + int i = ListUtil.split(insertList, 50).stream().map(gisCjqyMapper::insertTGisCjqyBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "新增数据: 区划代码={}, 条数={}", dept.getDeptName(), i); + int u = ListUtil.split(updateList, 50).stream().map(gisCjqyMapper::updateTGisCjqyBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "更新数据: 区划代码={}, 条数={}", dept.getDeptName(), u); + return i + u; + } + + private int SaveDeptData(SysDept dept, List datas) + { + importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); + List databases = LoadDatabase(dept.getImportCode()); + Map exists = databases.stream().collect(Collectors.toMap(TGisCjqy::getCjqydm, x->x)); + + List insertList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + for(GeoCJQY data : datas) + { + TGisCjqy db = exists.get(data.CJQYDM); + boolean has = null == db; + db = ConvertData(data, db, dept); + if(has) + insertList.add(db); + else + updateList.add(db); + } + + CleanDatabase(dept.getImportCode()); + int ret = WriteData(insertList, updateList, dept); + importTask.logFile.WriteLine(TYPE_NAME + "总写入数据: 区划代码={}, 条数={}", dept.getDeptName(), ret); + return ret; + } + + public int SaveData() + { + int ret = 0; + Map> group = list.stream().collect(Collectors.groupingBy((x) -> x.CJQYDM)); + importTask.logFile.WriteLine(TYPE_NAME + "shp数据: 区划代码类型数={}", group.size()); + for(String dm : group.keySet()) + { + SysDept dept = importTask.importDeptMap.get(dm); + ret = SaveDeptData(dept, group.get(dm)); + } + return ret; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoDBTaskInterface.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoDBTaskInterface.java new file mode 100644 index 0000000..8c0b438 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoDBTaskInterface.java @@ -0,0 +1,8 @@ +package com.ruoyi.geo.framework; + +public interface GeoDBTaskInterface +{ + public int LoadDataList(); + public void CheckData(); + public int SaveData(); +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoException.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoException.java new file mode 100644 index 0000000..aa796ad --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoException.java @@ -0,0 +1,14 @@ +package com.ruoyi.geo.framework; + +public class GeoException extends RuntimeException +{ + public GeoException(String msg) + { + super(msg); + } + + public GeoException(Throwable e) + { + super(e); + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoFileSystem.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoFileSystem.java new file mode 100644 index 0000000..9b6616f --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoFileSystem.java @@ -0,0 +1,46 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.io.FileUtil; +import com.ruoyi.common.utils.StringUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public final class GeoFileSystem +{ + public static List FindFiles(File dir, String suffix) + { + return FindFiles_r(new ArrayList<>(), dir, suffix); + } + + public static List FindFiles_r(List list, File dir, String suffix) + { + File[] files; + if(StringUtils.isEmpty(suffix)) + files = dir.listFiles(); + else + { + String ext = suffix.toLowerCase(); + files = dir.listFiles((d, f) -> { + if(FileUtil.isDirectory(d.getAbsolutePath() + File.separatorChar + f)) + return true; + return f.toLowerCase().endsWith(ext); + }); + } + + if(null != files) + { + for(File file : files) + { + if(file.isDirectory()) + FindFiles_r(list, file, suffix); + else + list.add(file.getAbsolutePath()); + } + } + return list; + } + + private GeoFileSystem() {} +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoImportTask.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoImportTask.java new file mode 100644 index 0000000..c97ea47 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoImportTask.java @@ -0,0 +1,293 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ZipUtil; +import com.ruoyi.business.domain.TTaskImport; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.geo.GeoParser; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.common.utils.sql.DB; +import com.ruoyi.geo.constants.GeoShpType; +import com.ruoyi.geo.constants.GeoSysDir; +import com.ruoyi.geo.service.GeoImportHandlerService; +import com.ruoyi.system.mapper.SysDeptMapper; +import lombok.extern.slf4j.Slf4j; +import static com.ruoyi.business.constants.TaskEnums.TaskStatus; + +import java.io.File; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +public class GeoImportTask implements Runnable +{ + private final Long taskId; + TTaskImport taskImport; + GeoTaskLog logFile; + GeoParser parser; + Map importDeptMap; + private Map sysDeptMap; + private List shpFiles; + private List typeNames; + private String shpPath; + private String type; + String username; + Date now; + + private GeoDBTaskInterface task; + + public GeoImportTask(Long taskId) + { + this.taskId = taskId; + } + + public void Init() + { + now = new Date(); + username = "admin"; //SecurityUtils.getUsername(); + String path = LogPath(); + log.info("任务{}日志文件: {}", taskId, path); + logFile = new GeoTaskLog(path); + logFile.Open(); + logFile.WriteLine("任务初始化: {} - {}", taskId, DateUtil.format(now, "yyyy-MM-dd HH:mm:ss.SSS")); + } + + public void Shutdown() + { + CleanTempFiles(); + + logFile.WriteLine("任务结束: {} - {}", taskId, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS")); + if(null != logFile) + { + logFile.close(); + } + log.info("任务{}日志文件: {}", taskId, GeoSysDir.DesensitizedNormalized(LogPath())); + } + + private String UnzipCachePath() + { + return GeoSysDir.CacheDir(GeoSysDir.ZIP_EXTRACT_TEMP + "/" + taskId); + } + + public String LogPath() + { + return GeoSysDir.ImportDir(GeoSysDir.LOG) + "/" + taskId + ".log"; + } + + private void ExtractZip() + { + // 先清空源临时解压文件 + CleanTempFiles(); + + File file = new File(RuoYiConfig.getProfile() + "/" + taskImport.getFileUrl()); + logFile.WriteLine("正在打开zip文件: " + GeoSysDir.DesensitizedNormalized(file.getAbsolutePath())); + if(!file.isFile()) + { + logFile.ErrorLine("zip文件不存在: " + GeoSysDir.DesensitizedNormalized(file.getAbsolutePath())); + } + + String toDir = UnzipCachePath(); + File dir = ZipUtil.unzip(file, new File(toDir)); + shpFiles = GeoFileSystem.FindFiles(dir, ".shp"); + logFile.WriteLine("zip文件中发现shp文件: " + shpFiles.size()); + if(shpFiles.isEmpty()) + { + logFile.ErrorLine("zip文件中未找到shp文件: " + GeoSysDir.DesensitizedNormalized(file.getAbsolutePath())); + } + + int i = 0; + for(String shpFile : shpFiles) + { + logFile.WriteLine("{}: {}", ++i, GeoSysDir.DesensitizedNormalized(shpFile)); + } + } + + private void CleanTempFiles() + { + String toDir = UnzipCachePath(); + logFile.WriteLine("清理临时文件: " + GeoSysDir.DesensitizedNormalized(toDir)); + FileUtil.del(toDir); + } + + private void ParseShp() + { + if(null == parser) + parser = new GeoParser(); + + parser.close(); + + if(!parser.Open(shpPath, "UTF-8")) + { + logFile.ErrorLine("打开shp文件错误: " + shpPath); + } + + typeNames = parser.GetTypeNames(); + + if(CollectionUtil.isEmpty(typeNames)) + { + logFile.ErrorLine("shp文件支持源未读取到任何数据类型: " + shpPath); + } + } + + private boolean LoadDataList() + { + if(!parser.SetSource(type)) + { + logFile.ErrorLine("shp文件未找到支持源: " + type); + return false; + } + + int num = task.LoadDataList(); + if(num == 0) + { + logFile.WriteLine("shp文件支持源未读取到数据: " + type); + return false; + } + + task.CheckData(); + return true; + } + + private void LoadDept() + { + SysDept cond = new SysDept(); + SysDeptMapper deptMapper = SpringUtils.getBean(SysDeptMapper.class); + List sysDepts = deptMapper.selectDeptList(cond); + importDeptMap = sysDepts.stream().collect(Collectors.toMap(SysDept::getImportCode, x -> x)); + sysDeptMap = sysDepts.stream().collect(Collectors.toMap(SysDept::getOrgCode, x -> x)); + } + + private void LoadTask() + { + TTaskImport task = GeoImportHandlerService.TaskImportMapper().getForUpdate(taskId); + if(null == task) + { + logFile.ErrorLine("任务配置不存在: " + taskId); + return; + } + if(!TaskStatus.ST_WAIT.equals(task.getTaskStatus())) + { + logFile.ErrorLine("任务非等待状态: " + taskId); + return; + } + taskImport = task; + } + + private void SaveTask(String st) + { + if(null != taskImport) + { + taskImport.setTaskStatus(st); + GeoImportHandlerService.TaskImportMapper().updateTTaskImport(taskImport); + } + } + + private void HandleShpFiles() + { + for(String shpFile : shpFiles) + { + shpPath = shpFile; + HandleShpFile(); + } + } + + private void HandleShpFile() + { + // 1. 读取当前shp文件中的所有类型 + ParseShp(); + for(String typeName : typeNames) + { + type = typeName; + // 2. 分发任务 + DispatchTask(); + } + } + + private void DispatchTask() + { + // 1. 选择任务类型 + switch(type.toLowerCase()) + { + case GeoShpType.CJQY: + task = new GeoCjqyTask(this); + break; + case GeoShpType.JTZY: + task = new GeoJtzyTask(this); + break; + case GeoShpType.ZYHYCG: + task = new GeoZyhycgTask(this); + break; + default: + logFile.WriteLine("数据类型不支持: " + type); + return; + } + // 2. 处理导入任务 + DoTask(); + } + + private void DoTask() + { + // 1. 读取数据列表 + if(!LoadDataList()) + return; + + // 2. 保存数据列表 + task.SaveData(); + } + + private void Handle() + { + // 0. 初始化任务: 打开日志系统 + Init(); + + // 1. 加载任务配置, 并检查 + LoadTask(); + + // 2. 读取文件, 解压文件到工作目录 + ExtractZip(); + + // 3. 加载部门数据 + LoadDept(); + + // 4. 依次处理所有shp文件 + HandleShpFiles(); + } + + @Override + public void run() + { + Object handle = null; + try + { + handle = DB.BeginTransaction(); + + Handle(); + + // 更改任务状态为已完成 + SaveTask(TaskStatus.ST_FINISH); + } + catch(Exception e) + { + e.printStackTrace(); + logFile.WriteLine(ExceptionUtil.getExceptionMessage(e)); + SaveTask(TaskStatus.ST_FAIL); + } + finally + { + DB.EndTransaction(handle); + // -1: 终止任务, 关闭日志系统 + Shutdown(); + } + } + + public Long GetTaskId() + { + return taskId; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoJtzyTask.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoJtzyTask.java new file mode 100644 index 0000000..cd94ff8 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoJtzyTask.java @@ -0,0 +1,181 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.collection.ListUtil; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.geo.service.GeoImportHandlerService; +import com.ruoyi.geo.structs.GeoJTZY; +import com.ruoyi.resource.domain.TResourceLand; +import com.ruoyi.resource.mapper.TResourceLandMapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.ruoyi.business.constants.TaskEnums.ImportType; + +public class GeoJtzyTask implements GeoDBTaskInterface +{ + private final static String TYPE_NAME = "地块属性"; + + private final GeoImportTask importTask; + private List list; + + public GeoJtzyTask(GeoImportTask importTask) + { + this.importTask = importTask; + } + + public int LoadDataList() + { + list = importTask.parser.GetList(GeoJTZY.class); + importTask.logFile.WriteLine(TYPE_NAME + "shp读取数据条数: " + list.size()); + + return list.size(); + } + + public void CheckData() + { + boolean hasEmpty = list.stream().anyMatch((x) -> (StringUtils.isEmpty(x.DKBM) && StringUtils.isEmpty(x.QSDWDM)) || (x.DKBM.length() < 12 && x.QSDWDM.length() < 12)); + if(hasEmpty) + { + importTask.logFile.ErrorLine("shp文件支持源中的行政区划代码缺失或不完整"); + return; + } + + List collect = list.stream().map(GeoJTZY::Code).distinct().collect(Collectors.toList()); + + for(String importCode : collect) + { + SysDept dept = importTask.importDeptMap.get(importCode); + if(null == dept) + { + importTask.logFile.WriteLine("shp文件支持源中的行政区划代码在系统不存在: " + importCode); + continue; + } + if(!dept.getOrgCode().startsWith(importTask.taskImport.getOrgCode())) + { + importTask.logFile.ErrorLine(TYPE_NAME + "shp文件支持源中的行政区划代码与系统不匹配: 文件({}) != 系统({})", dept.getOrgCode(), importTask.taskImport.getOrgCode()); + } + } + } + + private List LoadDatabase(String qydm) + { + TResourceLand cond = new TResourceLand(); + cond.setImportCode(qydm); + List cjqies = GeoImportHandlerService.ResourceLandMapper().selectTResourceLandList(cond); + importTask.logFile.WriteLine(TYPE_NAME + "读取系统已存在的条数: 区划代码={}, 条数={}", qydm, cjqies.size()); + return cjqies; + } + + private TResourceLand ConvertData(GeoJTZY src, TResourceLand dst, SysDept dept) + { + boolean notExists = null == dst; + if(notExists) + dst = new TResourceLand(); + dst.setTheGeom(src.the_geom); + dst.setImportCode(dept.getImportCode()); + dst.setBsm(src.BSM); + dst.setYsdm(src.YSDM); + dst.setDkbm(src.DKBM); + dst.setDkmc(src.DKMC); + dst.setSyqxz(src.SYQXZ); + dst.setDklb(src.DKLB); + dst.setDldj(src.DLDJ); + dst.setTdyt(src.TDYT); + dst.setSfjbnt(src.SFJBNT); + dst.setDkdz(src.DKDZ); + dst.setDkxz(src.DKXZ); + dst.setDknz(src.DKNZ); + dst.setDkbz(src.DKBZ); + dst.setZjrxm(src.ZJRXM); + dst.setDkbzxx(src.DKBZXX); + dst.setTxmj(src.TXMJ); + dst.setQsdwdm(src.QSDWDM); + dst.setQsdwmc(src.QSDWMC); + dst.setSfzwd(src.SFZWD); + dst.setScmjm(src.SCMJM); + dst.setDeptName(dept.getDeptName()); + if(notExists) + { + dst.setSurveyStatus("1"); + dst.setCreateBy(importTask.username); + dst.setCreateTime(importTask.now); + } + else + { + dst.setUpdateBy(importTask.username); + dst.setUpdateTime(importTask.now); + } + return dst; + } + + private int CleanDatabase(String importCode) + { + if(ImportType.IT_OVERRIDE.equals(importTask.taskImport.getImportType())) + { + importTask.logFile.WriteLine(TYPE_NAME + "覆盖导入, 需清空现有数据, 然后再插入, 区划代码={}", importCode); + int ret = GeoImportHandlerService.ResourceLandMapper().deleteByCode(importCode); + importTask.logFile.WriteLine(TYPE_NAME + "清空现有数据: {}, 区划代码={}", ret, importCode); + return ret; + } + else + { + importTask.logFile.WriteLine(TYPE_NAME + "增量导入, 无需清空现有数据, 仅执行插入或更新, 区划代码={}", importCode); + return -1; + } + } + + private int WriteData(List insertList, List updateList, SysDept dept) + { + TResourceLandMapper gisCjqyMapper = GeoImportHandlerService.ResourceLandMapper(); + int i = ListUtil.split(insertList, 30).stream().map(gisCjqyMapper::insertTResourceLandBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "新增数据: 区划代码={}, 条数={}", dept.getDeptName(), i); + int u = ListUtil.split(updateList, 1).stream().map(gisCjqyMapper::updateTResourceLandBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "更新数据: 区划代码={}, 条数={}", dept.getDeptName(), u); + return i + u; + } + + private int SaveDeptData(SysDept dept, List datas) + { + importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); + List databases = LoadDatabase(dept.getImportCode()); + Map exists = databases.stream().collect(Collectors.toMap(TResourceLand::getDkbm, x->x)); + + List insertList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + for(GeoJTZY data : datas) + { + TResourceLand db = exists.get(data.DKBM); + boolean notExists = null == db; + db = ConvertData(data, db, dept); + if(notExists) + insertList.add(db); + else + updateList.add(db); + } + + CleanDatabase(dept.getImportCode()); + int ret = WriteData(insertList, updateList, dept); + importTask.logFile.WriteLine(TYPE_NAME + "总写入数据: 区划代码={}, 条数={}", dept.getDeptName(), ret); + return ret; + } + + public int SaveData() + { + int ret = 0; + Map> group = list.stream().collect(Collectors.groupingBy(GeoJTZY::Code)); + importTask.logFile.WriteLine(TYPE_NAME + "shp数据: 区划代码类型数={}", group.size()); + for(String dm : group.keySet()) + { + SysDept dept = importTask.importDeptMap.get(dm); + if(null == dept) + continue; + ret = SaveDeptData(dept, group.get(dm)); + } + return ret; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskLog.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskLog.java new file mode 100644 index 0000000..1e2b61d --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskLog.java @@ -0,0 +1,174 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +@Slf4j +public class GeoTaskLog implements Closeable +{ + private BufferedWriter writer; + private String path; + private boolean autoFlush; + + public GeoTaskLog(String path) + { + this(path, true); + } + + public GeoTaskLog(String path, boolean autoFlush) + { + this.path = path; + this.autoFlush = autoFlush; + } + + public boolean IsOpened() + { + return null != writer; + } + + public boolean Open() + { + Assert.isNull(writer, "日志文件已经打开"); + + File file = CreateFile(); + if(null == file) + return false; + + try + { + FileWriter fos = new FileWriter(file); + writer = new BufferedWriter(fos); + log.info("打开日志文件: " + path); + return true; + } + catch(Exception e) + { + e.printStackTrace(); + return false; + } + } + + @Override + public void close() + { + if(null != writer) + { + try + { + log.info("关闭日志文件: " + path); + writer.flush(); + writer.close(); + writer = null; + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + } + + private void AfterWrite() + { + if(autoFlush) + { + try + { + writer.flush(); + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + } + + private void CheckWriter() + { + Assert.notNull(writer, "文件未打开"); + } + + public void Error(String str, Object...args) + { + String text = StrUtil.format(str, args); + Write(text); + throw new GeoException(text); + } + + public void ErrorLine(String str, Object...args) + { + String text = StrUtil.format(str, args); + WriteLine(text); + throw new GeoException(text); + } + + public void Write(String str, Object...args) + { + String text = StrUtil.format(str, args); + log.info(text); + + //ThreadUtil.sleep(5000); + + CheckWriter(); + + try + { + writer.write(text); + AfterWrite(); + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + + public String GetString() + { + File file = new File(path); + if(!file.isFile()) + return null; + return FileUtil.readUtf8String(file); + } + + public void WriteLine(String str, Object...args) + { + Write(str, args); + try + { + writer.newLine(); + AfterWrite(); + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + + private File CreateFile() + { + File file = new File(path); + if(file.exists()) + { + log.error("日志文件已存在: " + path); + log.warn("覆盖日志文件: " + path); + return file; + } + + FileUtil.mkParentDirs(path); + + return file; + } + + public String Path() + { + return path; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskQueue.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskQueue.java new file mode 100644 index 0000000..8428acb --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoTaskQueue.java @@ -0,0 +1,261 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.thread.ThreadUtil; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +/** + * 队列式任务线程 + */ +@Slf4j +public class GeoTaskQueue implements Runnable +{ + private static final int ST_DILE = 0; + private static final int ST_READY = 1; + private static final int ST_RUNNING = 2; + private static final int ST_WORKING = 3; + private static final int ST_WAITING = 4; + private static final int ST_END = 5; + private static final int ST_TERMINATED = 6; + + private volatile Thread thread; + private final List queue = new LinkedList<>(); + private final AtomicInteger state = new AtomicInteger(ST_DILE); + private final Object lock = new Object(); + private volatile boolean reqQuit = false; + private final boolean quitOnNoTask; + private Runnable currentTask = null; + // 计数器 + private volatile int c_loop = 0; + private volatile int c_wait = 0; + private volatile int c_work = 0; + private volatile int c_error = 0; + private volatile int c_run = 0; + + public GeoTaskQueue(boolean quitOnNoTask) { + this.quitOnNoTask = quitOnNoTask; + } + + @Override + public void run() + { + Runnable task; + + log.info("[任务队列线程]: {}开始执行 -> {}", Thread.currentThread().getName(), Thread.currentThread().getId()); + state.set(ST_RUNNING); + for(;;) + { + c_loop++; + synchronized(this) { + if(reqQuit) + { + reqQuit = false; + boolean hasTask = HasTask(); + log.info("[退出任务队列线程]: 退出状态 -> " + (hasTask ? "中断" : "完成")); + state.set(hasTask ? ST_TERMINATED : ST_END); + break; + } + } + task = TakeTask(); + if(null == task) + { + if(quitOnNoTask) + { + log.info("[任务队列线程循环]: 当前无任务, 退出"); + break; + } + // 如果没有任务, 则暂停线程 + synchronized(lock) { + try + { + c_wait++; + log.info("[任务队列线程循环]: 当前无任务, 进入等待状态"); + state.set(ST_WAITING); + lock.wait(); + log.info("[任务队列线程循环]: 有新任务添加, 继续执行"); + } + catch(InterruptedException e) + { + e.printStackTrace(); + ThreadUtil.sleep(30000); // 强制等待30秒 + } + } + continue; + } + log.info("[任务队列线程循环]: 正在执行任务"); + state.set(ST_WORKING); + c_work++; + try + { + task.run(); + } + catch(Exception e) + { + c_error++; + e.printStackTrace(); + } + synchronized(this) { + currentTask = null; + } + log.info("[任务队列线程循环]: 当前任务执行完成"); + state.set(ST_RUNNING); + } + log.info("[任务队列线程循环]: 执行结束"); + state.set(IsRunning() ? ST_END : ST_TERMINATED); + + synchronized(this) { + thread = null; + } + } + + public synchronized int AddTask(Runnable task) + { + queue.add(task); + // 如果有任务, 则唤醒队列循环 + synchronized(lock) { + lock.notifyAll(); + } + return queue.size(); + } + + public synchronized int AddTask(List tasks) + { + queue.addAll(tasks); + // 如果有任务, 则唤醒队列循环 + synchronized(lock) { + lock.notifyAll(); + } + return queue.size(); + } + + private synchronized Runnable TakeTask() + { + if(queue.isEmpty()) + return null; + currentTask = queue.remove(0); + return currentTask; + } + + public synchronized boolean HasTask() + { + return !queue.isEmpty(); + } + + public boolean IsRunning() + { + int st = state.get(); + return st == ST_RUNNING || st == ST_WAITING || st == ST_WORKING; + } + + public synchronized void RequestQuit() + { + if(!IsRunning()) + { + log.warn("[请求结束任务队列线程]: 任务队列线程非运行状态 -> " + state.get()); + return; + } + log.info("[请求结束任务队列线程]: 等待当前任务完成后中止"); + if(reqQuit) + return; + reqQuit = true; + } + + public void Quit() + { + synchronized(this) { + if(null == thread) + return; + } + RequestQuit(); + try + { + log.info("[结束任务队列线程]: 等待当前任务完成"); + thread.join(); + synchronized(this) { + thread = null; + } + } + catch(InterruptedException e) + { + throw new RuntimeException(e); + } + } + + private void Reset() + { + log.info("[重置任务队列线程]: 重置状态"); + reqQuit = false; + c_loop = 0; + c_wait = 0; + c_work = 0; + c_error = 0; + state.set(ST_DILE); + currentTask = null; + } + + public synchronized void StartTask(Runnable task) + { + if(IsRunning()) + AddTask(task); + else + { + queue.add(task); + Start(); + } + } + + public synchronized void StartTask(List task) + { + if(IsRunning()) + AddTask(task); + else + { + queue.addAll(task); + Start(); + } + } + + public synchronized Runnable GetCurrentTask() + { + return currentTask; + } + + public synchronized Runnable FindTask(Predicate op) + { + if(queue.isEmpty()) + return null; + return queue.stream().filter(op).findFirst().orElse(null); + } + + public synchronized void Start() + { + if(null != thread) + { + log.error("[启动任务队列线程]: 上次任务队列线程仍在运行 -> " + thread.getName()); + return; + } + + int st = state.get(); + if(st != ST_DILE && st != ST_END && st != ST_TERMINATED) + { + log.error("[启动任务队列线程]: 任务队列线程非空闲/结束/中止状态 -> " + st); + return; + } + Reset(); + + if(state.get() != ST_DILE) + { + log.error("[开始任务队列线程]: 任务队列线程非空闲状态"); + return; + } + thread = new Thread(this);thread.interrupt(); + thread.setName("任务线程_" + c_run++); + state.set(ST_READY); + thread.start(); + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoZyhycgTask.java b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoZyhycgTask.java new file mode 100644 index 0000000..4741c2b --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/framework/GeoZyhycgTask.java @@ -0,0 +1,179 @@ +package com.ruoyi.geo.framework; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.utils.DecimalUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.geo.service.GeoImportHandlerService; +import com.ruoyi.geo.structs.GeoZYHYCG; +import com.ruoyi.resource.domain.TResourceOperation; +import com.ruoyi.resource.mapper.TResourceOperationMapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.ruoyi.business.constants.TaskEnums.ImportType; + +public class GeoZyhycgTask implements GeoDBTaskInterface +{ + private final static String TYPE_NAME = "地块经营"; + + private final GeoImportTask importTask; + private List list; + + public GeoZyhycgTask(GeoImportTask importTask) + { + this.importTask = importTask; + } + + public int LoadDataList() + { + list = importTask.parser.GetList(GeoZYHYCG.class); + importTask.logFile.WriteLine(TYPE_NAME + "shp读取数据条数: " + list.size()); + + return list.size(); + } + + public void CheckData() + { + boolean hasEmpty = list.stream().anyMatch((x) -> StringUtils.isEmpty(x.DKBM) || x.DKBM.length() < 12); + if(hasEmpty) + { + importTask.logFile.ErrorLine("shp文件支持源中的行政区划代码缺失或不完整"); + return; + } + + List collect = list.stream().map(GeoZYHYCG::Code).distinct().collect(Collectors.toList()); + + for(String importCode : collect) + { + SysDept dept = importTask.importDeptMap.get(importCode); + if(null == dept) + { + importTask.logFile.WriteLine("shp文件支持源中的行政区划代码在系统不存在: " + importCode); + continue; + } + if(!dept.getOrgCode().startsWith(importTask.taskImport.getOrgCode())) + { + importTask.logFile.ErrorLine(TYPE_NAME + "shp文件支持源中的行政区划代码与系统不匹配: 文件({}) != 系统({})", dept.getOrgCode(), importTask.taskImport.getOrgCode()); + } + } + } + + private List LoadDatabase(String qydm) + { + TResourceOperation cond = new TResourceOperation(); + cond.setImportCode(qydm); + List cjqies = GeoImportHandlerService.ResourceOperationMapper().selectTResourceOperationList(cond); + importTask.logFile.WriteLine(TYPE_NAME + "读取系统已存在的条数: 区划代码={}, 条数={}", qydm, cjqies.size()); + return cjqies; + } + + private TResourceOperation ConvertData(GeoZYHYCG src, TResourceOperation dst, SysDept dept) + { + boolean notExists = null == dst; + if(notExists) + dst = new TResourceOperation(); + dst.setImportCode(dept.getImportCode()); + dst.setDkbm(src.DKBM); + dst.setDkmc(src.DKMC); + dst.setDkdz(src.SYQXZ); + dst.setJymj(src.JYMJ); + dst.setJydxlx(src.JYDXLX); + dst.setJydxmc(src.JYDXMC); + dst.setJydxzjlx(src.JYDXZJLX); + dst.setJydxzjhm(src.JYDXZJHM); + dst.setSfqdht(src.SFQDHT); + dst.setJykssj(StrUtil.subPre(src.JYKSSJ, 10)); + dst.setJyjssj(StrUtil.subPre(src.JYJSSJ, 10)); + dst.setCbje(DecimalUtils.nullToZero(src.CBJE)); + dst.setDxje(DecimalUtils.nullToZero(src.DXJE)); + dst.setSqje(DecimalUtils.nullToZero(src.SQJE)); + dst.setNsy(DecimalUtils.nullToZero(src.NSY)); + dst.setBzxx(src.BZXX); + dst.setJyfs(src.JYFS); + dst.setDeptName(dept.getDeptName()); + if(notExists) + { + dst.setSurveyStatus("1"); + dst.setCreateBy(importTask.username); + dst.setCreateTime(importTask.now); + } + else + { + dst.setUpdateBy(importTask.username); + dst.setUpdateTime(importTask.now); + } + return dst; + } + + private int CleanDatabase(String importCode) + { + if(ImportType.IT_OVERRIDE.equals(importTask.taskImport.getImportType())) + { + importTask.logFile.WriteLine(TYPE_NAME + "覆盖导入, 需清空现有数据, 然后再插入, 区划代码={}", importCode); + int ret = GeoImportHandlerService.ResourceOperationMapper().deleteByCode(importCode); + importTask.logFile.WriteLine(TYPE_NAME + "清空现有数据: {}, 区划代码={}", ret, importCode); + return ret; + } + else + { + importTask.logFile.WriteLine(TYPE_NAME + "增量导入, 无需清空现有数据, 仅执行插入或更新, 区划代码={}", importCode); + return -1; + } + } + + private int WriteData(List insertList, List updateList, SysDept dept) + { + TResourceOperationMapper gisCjqyMapper = GeoImportHandlerService.ResourceOperationMapper(); + int i = ListUtil.split(insertList, 30).stream().map(gisCjqyMapper::insertTResourceOperationBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "新增数据: 区划代码={}, 条数={}", dept.getDeptName(), i); + int u = ListUtil.split(updateList, 1).stream().map(gisCjqyMapper::updateTResourceOperationBatch).reduce(0, Integer::sum); + importTask.logFile.WriteLine(TYPE_NAME + "更新数据: 区划代码={}, 条数={}", dept.getDeptName(), u); + return i + u; + } + + private int SaveDeptData(SysDept dept, List datas) + { + importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); + List databases = LoadDatabase(dept.getImportCode()); + Map exists = databases.stream().collect(Collectors.toMap(TResourceOperation::getDkbm, x->x)); + + List insertList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + for(GeoZYHYCG data : datas) + { + TResourceOperation db = exists.get(data.DKBM); + boolean notExists = null == db; + db = ConvertData(data, db, dept); + if(notExists) + insertList.add(db); + else + updateList.add(db); + } + + CleanDatabase(dept.getImportCode()); + int ret = WriteData(insertList, updateList, dept); + importTask.logFile.WriteLine(TYPE_NAME + "总写入数据: 区划代码={}, 条数={}", dept.getDeptName(), ret); + return ret; + } + + public int SaveData() + { + int ret = 0; + Map> group = list.stream().collect(Collectors.groupingBy(GeoZYHYCG::Code)); + importTask.logFile.WriteLine(TYPE_NAME + "shp数据: 区划代码类型数={}", group.size()); + for(String dm : group.keySet()) + { + SysDept dept = importTask.importDeptMap.get(dm); + if(null == dept) + continue; + ret = SaveDeptData(dept, group.get(dm)); + } + return ret; + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/object/GeoLogInfo.java b/ruoyi-business/src/main/java/com/ruoyi/geo/object/GeoLogInfo.java new file mode 100644 index 0000000..640eb41 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/object/GeoLogInfo.java @@ -0,0 +1,13 @@ +package com.ruoyi.geo.object; + +import lombok.Data; + +@Data +public class GeoLogInfo +{ + private Integer length; + private String text; + private Boolean eof; + private String taskStatus; + private Long nextPollDelay; +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/service/GeoImportHandlerService.java b/ruoyi-business/src/main/java/com/ruoyi/geo/service/GeoImportHandlerService.java new file mode 100644 index 0000000..be01e20 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/service/GeoImportHandlerService.java @@ -0,0 +1,247 @@ +package com.ruoyi.geo.service; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.extra.servlet.ServletUtil; +import com.ruoyi.business.domain.TTaskImport; +import com.ruoyi.business.mapper.TGisCjqyMapper; +import com.ruoyi.business.mapper.TTaskImportMapper; +import com.ruoyi.common.utils.sql.DB; +import com.ruoyi.geo.config.GeoImportTaskConfig; +import com.ruoyi.geo.constants.GeoSysDir; +import com.ruoyi.geo.framework.GeoImportTask; +import com.ruoyi.geo.framework.GeoTaskQueue; +import com.ruoyi.geo.object.GeoLogInfo; +import com.ruoyi.resource.mapper.TResourceLandMapper; +import com.ruoyi.resource.mapper.TResourceOperationMapper; +import com.ruoyi.system.mapper.SysDeptMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import static com.ruoyi.business.constants.TaskEnums.TaskStatus; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class GeoImportHandlerService +{ + @Resource + private SysDeptMapper deptMapper; + private static TTaskImportMapper taskImportMapper; + private static TGisCjqyMapper gisCjqyMapper; + private static TResourceLandMapper resourceLandMapper; + private static TResourceOperationMapper resourceOperationMapper; + + private GeoTaskQueue taskQueue; + + @Autowired + public void setTaskImportMapper(TTaskImportMapper taskImportMapper) + { + GeoImportHandlerService.taskImportMapper = taskImportMapper; + } + + @Autowired + public void setGisCjqyMapper(TGisCjqyMapper gisCjqyMapper) + { + GeoImportHandlerService.gisCjqyMapper = gisCjqyMapper; + } + + @Autowired + public void setResourceLandMapper(TResourceLandMapper resourceLandMapper) + { + GeoImportHandlerService.resourceLandMapper = resourceLandMapper; + } + + @Autowired + public void setResourceOperationMapper(TResourceOperationMapper resourceOperationMapper) + { + GeoImportHandlerService.resourceOperationMapper = resourceOperationMapper; + } + + public static TTaskImportMapper TaskImportMapper() + { + return taskImportMapper; + } + + public static TGisCjqyMapper GisCjqyMapper() + { + return gisCjqyMapper; + } + + public static TResourceLandMapper ResourceLandMapper() + { + return resourceLandMapper; + } + + public static TResourceOperationMapper ResourceOperationMapper() + { + return resourceOperationMapper; + } + + private synchronized GeoTaskQueue TaskQueue() + { + if(null == taskQueue) + taskQueue = new GeoTaskQueue(true); + return taskQueue; + } + + @PostConstruct + public synchronized void Init() + { + SyncTask(); + LoadTask(); + } + + @PreDestroy + public synchronized void Shutdown() + { + if(null != taskQueue) + taskQueue.Quit(); + } + + private void SyncTask() + { + log.info("更改运行中的任务状态为中止......"); + // 更改运行中的任务状态为中止 + int i = taskImportMapper.terminateRunningTask(); + log.info("已设置运行中的任务状态为中止: " + i); + } + + private void LoadTask() + { + if(!GeoImportTaskConfig.startOnBoot) + return; + + log.info("拉取排队中的任务......"); + TTaskImport cond = new TTaskImport(); + cond.setTaskStatus(TaskStatus.ST_WAIT); + List taskImports = taskImportMapper.selectTTaskImportList(cond); + log.info("拉取到排队中的任务: " + taskImports.size()); + List tasks = taskImports.stream().map((x) -> new GeoImportTask(x.getId())).collect(Collectors.toList()); + if(!tasks.isEmpty()) + TaskQueue().StartTask(tasks); + } + + public GeoLogInfo GetImportLog(Long taskId, int offset) + { + GeoLogInfo info = new GeoLogInfo(); + info.setNextPollDelay(GeoImportTaskConfig.logNextPoll); + TTaskImport taskImport = taskImportMapper.selectTTaskImportById(taskId); + Assert.notNull(taskImport, "导入任务不存在"); + info.setTaskStatus(taskImport.getTaskStatus()); + if(TaskStatus.ST_READY.equals(taskImport.getTaskStatus())/* || TaskStatus.ST_WAIT.equals(taskImport.getTaskStatus())*/) + { + info.setEof(true); + info.setLength(-1); + return info; + } + + String path = GeoSysDir.ImportDir(GeoSysDir.LOG) + "/" + taskId + ".log"; + if(!FileUtil.isFile(path)) + { + info.setEof(true); + info.setLength(-1); + return info; + } + + info.setEof(!TaskStatus.ST_RUNNING.equals(info.getTaskStatus()) && !TaskStatus.ST_WAIT.equals(info.getTaskStatus())); + String str = FileUtil.readUtf8String(path); + info.setLength(str.length()); + String text; + if(offset >= str.length() - 1) + text = ""; + else if(offset > 0) + text = str.substring(offset); + else + text = str; + info.setText(text); + + return info; + } + + public synchronized int StartTask(Long id) + { + if(null != taskQueue) + { + GeoImportTask task = (GeoImportTask)taskQueue.GetCurrentTask(); + Assert.isFalse(null != task && task.GetTaskId().equals(id), "任务正在执行"); + + boolean exists = null != taskQueue.FindTask((x) -> { + GeoImportTask t = (GeoImportTask) x; + return(t.GetTaskId().equals(id)); + }); + Assert.isFalse(exists, "任务已在队列中"); + } + + Object handler = DB.BeginTransaction(); + try + { + TTaskImport taskImport = taskImportMapper.getForUpdate(id); + Assert.notNull(taskImport, "任务不存在"); + Assert.isTrue(TaskStatus.ST_READY.equals(taskImport.getTaskStatus()), "任务非准备状态"); + taskImport.setTaskStatus(TaskStatus.ST_WAIT); + taskImportMapper.updateTTaskImport(taskImport); + DB.CommitTransaction(handler); + } + catch(Exception e) + { + e.printStackTrace(); + DB.RollbackTransaction(handler); + return 0; + } + int ret = null != taskQueue && taskQueue.HasTask() ? 1 : 2; + TaskQueue().StartTask(new GeoImportTask(id)); + return ret; + } + + public void DownloadLog(Long taskId, HttpServletResponse response) + { + String path = GeoSysDir.ImportDir(GeoSysDir.LOG) + "/" + taskId + ".log"; + String dlFileName; + try + { + dlFileName = URLEncoder.encode(FileNameUtil.getName(path), "UTF-8"); + } + catch(UnsupportedEncodingException e) + { + e.printStackTrace(); + dlFileName = ""; + } + + int bytes = 0; + + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"" + dlFileName + "\""); + response.setContentType("application/octet-stream; charset=UTF-8"); + + TTaskImport taskImport = taskImportMapper.selectTTaskImportById(taskId); + if(null == taskImport) + { + ServletUtil.write(response, "导入任务不存在", "application/octet-stream; charset=UTF-8"); + bytes = "导入任务不存在".getBytes(StandardCharsets.UTF_8).length; + } + + File file = new File(path); + if(!file.isFile()) + { + ServletUtil.write(response, "日志文件不存在", "application/octet-stream; charset=UTF-8"); + bytes = "日志文件不存在".getBytes(StandardCharsets.UTF_8).length; + } + + ServletUtil.write(response, file); + response.addHeader("Content-Length", "" + bytes); + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoCJQY.java b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoCJQY.java new file mode 100644 index 0000000..6c8cce8 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoCJQY.java @@ -0,0 +1,69 @@ +package com.ruoyi.geo.structs; + +import lombok.ToString; + +import java.math.BigDecimal; + +@ToString +public final class GeoCJQY +{ + /** + * 对象ID + */ + public Long OBJECTID; + + /** + * 县级区域代码 + */ + public String XJQYDM; + + /** + * 边界长度(米) + */ + public BigDecimal Shape_Leng; + + /** + * 县级区域名称 + */ + public String XJQYMC; + + /** + * 村级区域代码 + */ + public String CJQYDM; + + /** + * 行政区名称 + */ + public String XZQMC; + + /** + * 区域面积(平方米) + */ + public BigDecimal Shape_Area; + + /** + * 村级区域名称 + */ + public String CJQYMC; + + /** + * 要素代码 + */ + public String YSDM; + + /** + * 行政区代码 + */ + public String XZQDM; + + /** + * 标识码 + */ + public Integer BSM; + + /** + * 几何图形数据(WKT格式) + */ + public String the_geom; +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoDK.java b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoDK.java new file mode 100644 index 0000000..d6d0da7 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoDK.java @@ -0,0 +1,90 @@ +package com.ruoyi.geo.structs; + +import lombok.ToString; + +import java.math.BigDecimal; + +@ToString +public final class GeoDK +{ + /** 承包人姓名 */ + public String ZJRXM; + + /** 地块唯一标识符 */ + public Long OBJECTID_1; + + /** 土地面积(公顷) */ + public BigDecimal TXMJ; + + /** 是否在一类建设用地区域(1表示是) */ + public String SFZWD; + + /** 所属行政区划代码(天津市代码200000) */ + public String YSDM; + + /** 是否基本农田(1表示是) */ + public String SFJBNT; + + /** 地块唯一标识符 */ + public Long OBJECTID; + + /** 区划单位代码 */ + public String QSDWDM; + + /** 剩余使用权年限(10年) */ + public BigDecimal SYQXZ; + + /** 地块备注信息 */ + public String DKBZXX; + + /** 地块标识码 */ + public Integer BSM; + + /** 地块类型(99表示特殊用地) */ + public String DKLB; + + /** 地块几何坐标(WKT格式) */ + public String the_geom; + + /** 区划单位名称 */ + public String QSDWMC; + + /** 地块性质(承包人姓名) */ + public String DKXZ; + + /** 地块长度(米) */ + public BigDecimal Shape_Le_1; + + /** 土地用途(1表示耕地) */ + public String TDYT; + + /** 土地等级(01表示一级地) */ + public String DLDJ; + + /** 土地来源类型(011表示集体分配) */ + public String TDLYLX; + + /** 地块周长(米) */ + public BigDecimal Shape_Leng; + + /** 实测面积(公顷) */ + public BigDecimal SCMJM; + + /** 地块编码 */ + public String DKBM; + + /** 地块内作物 */ + public String DKNZ; + + /** 地块面积(平方米) */ + public BigDecimal Shape_Area; + + /** 地块名称(孔家) */ + public String DKMC; + + /** 地块地址(道路) */ + public String DKDZ; + + /** 地块备注 */ + public String DKBZ; +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoJTZY.java b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoJTZY.java new file mode 100644 index 0000000..f7c1931 --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoJTZY.java @@ -0,0 +1,107 @@ +package com.ruoyi.geo.structs; + +import com.ruoyi.common.utils.StringUtils; +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * 地块信息实体类 + */ +@ToString +public final class GeoJTZY +{ + /** 地块名称,空字符串表示未命名 */ + public String ZJRXM; + + /** 地块唯一标识符 */ + public Long OBJECTID_1; + + /** 地块面积(单位:公顷) */ + public BigDecimal TXMJ; + + /** 是否在二类建设用地区域(2表示是) */ + public String SFZWD; + + /** 关联对象ID */ + public Long OBJECTID_2; + + /** 所属行政区划代码(吉林省代码220000) */ + public String YSDM; + + /** 是否基本农田(2表示是) */ + public String SFJBNT; + + /** 地块唯一标识符 */ + public Long OBJECTID; + + /** 区划单位代码 */ + public String QSDWDM; + + /** 所有权性质 */ + public String SYQXZ; + + /** 地块备注信息 */ + public String DKBZXX; + + /** 地块标识码 */ + public Integer BSM; + + /** 地块类型 */ + public String DKLB; + + /** 地块几何坐标(WKT格式) */ + public String the_geom; + + /** 区划单位名称 */ + public String QSDWMC; + + /** 地块性质 */ + public String DKXZ; + + /** 地块长度(米) */ + public BigDecimal Shape_Le_1; + + /** 地块宽度(米) */ + public BigDecimal Shape_Le_2; + + /** 土地用途 */ + public String TDYT; + + /** 土地等级 */ + public String DLDJ; + + /** 土地来源类型 */ + public String TDLYLX; + + /** 地块周长(米) */ + public BigDecimal Shape_Leng; + + /** 实测面积(公顷) */ + public BigDecimal SCMJM; + + /** 地块编码 */ + public String DKBM; + + /** 地块内作物 */ + public String DKNZ; + + /** 地块面积(公顷) */ + public BigDecimal Shape_Area; + + /** 地块名称(200p平以下删除地块) */ + public String DKMC; + + /** 地块地址 */ + public String DKDZ; + + /** 地块备注 */ + public String DKBZ; + + public String Code() + { + if(StringUtils.isNotEmpty(QSDWDM) && QSDWDM.length() >= 12) + return QSDWDM.substring(0, 12); + return DKBM.substring(0, 12); + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoZYHYCG.java b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoZYHYCG.java new file mode 100644 index 0000000..574618a --- /dev/null +++ b/ruoyi-business/src/main/java/com/ruoyi/geo/structs/GeoZYHYCG.java @@ -0,0 +1,80 @@ +package com.ruoyi.geo.structs; + +import lombok.ToString; + +import java.math.BigDecimal; + +/** + * 集体用地信息实体类 + */ +@ToString +public final class GeoZYHYCG +{ + /** 用地类型(1表示集体建设用地) */ + public String JYDXLX; + + /** 补偿金额 */ + public BigDecimal CBJE; + + /** 土地面积(公顷) */ + public BigDecimal TXMJ; + + /** 备注信息(林地未收费) */ + public String BZXX; + + /** 净用地面积(公顷) */ + public BigDecimal JYMJ; + + /** 抵押金额 */ + public BigDecimal DXJE; + + /** 地块唯一标识符 */ + public Long OBJECTID; + + /** 是否签订合同(2表示已签订) */ + public String SFQDHT; + + /** 地块周长(米) */ + public BigDecimal Shape_Leng; + + /** 地块编码 */ + public String DKBM; + + /** 用地性质证明类型 */ + public String JYDXZJLX; + + /** 用地单位名称(村集体) */ + public String JYDXMC; + + /** 用地单位证件号码 */ + public String JYDXZJHM; + + /** 年收益 */ + public BigDecimal NSY; + + /** 使用权性质 */ + public String SYQXZ; + + /** 地块面积(公顷) */ + public BigDecimal Shape_Area; + + /** 地块名称(胜利村屯里) */ + public String DKMC; + + /** 地块几何坐标(WKT格式) */ + public String the_geom; + + /** 经营方式(120表示自主经营) */ + public String JYFS; + + /** 申请金额 */ + public BigDecimal SQJE; + + public String JYKSSJ; + public String JYJSSJ; + + public String Code() + { + return DKBM.substring(0, 12); + } +} diff --git a/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceLandMapper.java b/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceLandMapper.java index 013f4fa..2936b7d 100644 --- a/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceLandMapper.java +++ b/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceLandMapper.java @@ -94,5 +94,6 @@ public interface TResourceLandMapper */ public TBigDataScreen homepageDownStatistics(Long deptId); + public int deleteByCode(String importCode); } diff --git a/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceOperationMapper.java b/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceOperationMapper.java index 199c596..edcab5f 100644 --- a/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceOperationMapper.java +++ b/ruoyi-business/src/main/java/com/ruoyi/resource/mapper/TResourceOperationMapper.java @@ -77,4 +77,6 @@ public interface TResourceOperationMapper * @return 结果 */ public int deleteTResourceOperationByIds(Long[] ids); + + public int deleteByCode(String importCode); } diff --git a/ruoyi-business/src/main/resources/mapper/business/TGisCjqyMapper.xml b/ruoyi-business/src/main/resources/mapper/business/TGisCjqyMapper.xml index 537d2c9..9c80f08 100644 --- a/ruoyi-business/src/main/resources/mapper/business/TGisCjqyMapper.xml +++ b/ruoyi-business/src/main/resources/mapper/business/TGisCjqyMapper.xml @@ -15,7 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select cjqydm, cjqymc, bsm, ysdm, the_geom, import_code, fid from t_gis_cjqy + select cjqydm, cjqymc, bsm, ysdm, ST_AsText(the_geom) as the_geom, import_code, fid from t_gis_cjqy + + where id = #{id} FOR UPDATE + diff --git a/ruoyi-business/src/main/resources/mapper/resource/TResourceLandMapper.xml b/ruoyi-business/src/main/resources/mapper/resource/TResourceLandMapper.xml index fa8cecb..50dcaeb 100644 --- a/ruoyi-business/src/main/resources/mapper/resource/TResourceLandMapper.xml +++ b/ruoyi-business/src/main/resources/mapper/resource/TResourceLandMapper.xml @@ -49,7 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select fid, BSM, YSDM, DKBM, DKMC, SYQXZ, DKLB, TDLYLX, DLDJ, TDYT, SFJBNT, DKDZ, DKXZ, DKNZ, DKBZ, DKBZXX, ZJRXM, TXMJ, SCMJM, QSDWDM, QSDWMC, SFZWD, survey_status, import_code, dept_name, create_by, create_time, update_by, update_time from t_resource_land + select fid, BSM, YSDM, DKBM, DKMC, SYQXZ, DKLB, TDLYLX, DLDJ, TDYT, SFJBNT, DKDZ, DKXZ, DKNZ, DKBZ, DKBZXX, ZJRXM, TXMJ, SCMJM, QSDWDM, QSDWMC, SFZWD, survey_status, import_code, dept_name, ST_AsText(the_geom) as the_geom, create_by, create_time, update_by, update_time from t_resource_land @@ -156,7 +156,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{zjrxm}, #{txmj}, #{scmjm}, - #{theGeom}, + ST_GEOMFROMTEXT(#{theGeom}), #{qsdwdm}, #{qsdwmc}, #{sfzwd}, @@ -224,7 +224,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{item.zjrxm}, #{item.txmj}, #{item.scmjm}, - #{item.theGeom}, + ST_GEOMFROMTEXT(#{item.theGeom}), #{item.qsdwdm}, #{item.qsdwmc}, #{item.sfzwd}, @@ -260,7 +260,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ZJRXM = #{zjrxm}, TXMJ = #{txmj}, SCMJM = #{scmjm}, - the_geom = #{theGeom}, + the_geom = ST_GEOMFROMTEXT(#{theGeom}), QSDWDM = #{qsdwdm}, QSDWMC = #{qsdwmc}, SFZWD = #{sfzwd}, @@ -298,7 +298,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ZJRXM = #{item.zjrxm}, TXMJ = #{item.txmj}, SCMJM = #{item.scmjm}, - the_geom = #{item.theGeom}, + the_geom = ST_GEOMFROMTEXT(#{item.theGeom}), QSDWDM = #{item.qsdwdm}, QSDWMC = #{item.qsdwmc}, SFZWD = #{item.sfzwd}, @@ -367,4 +367,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" LEFT JOIN t_resource_land l ON l.import_code LIKE concat( d.import_code, '%' ) LEFT JOIN t_resource_operation o ON l.DKBM = o.DKBM WHERE d.dept_id = #{deptId} + + + delete from t_resource_land where import_code = #{importCode} + diff --git a/ruoyi-business/src/main/resources/mapper/resource/TResourceOperationMapper.xml b/ruoyi-business/src/main/resources/mapper/resource/TResourceOperationMapper.xml index 7c0e82c..de54964 100644 --- a/ruoyi-business/src/main/resources/mapper/resource/TResourceOperationMapper.xml +++ b/ruoyi-business/src/main/resources/mapper/resource/TResourceOperationMapper.xml @@ -281,4 +281,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{id} + + + delete from t_resource_operation where import_code = #{importCode} + diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 001ebde..e14f9b8 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -155,6 +155,23 @@ hutool-all + + + org.geotools + gt-shapefile + + + + org.geotools + gt-swing + + + + + org.geotools + gt-geojson + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/geo/GeoParser.java b/ruoyi-common/src/main/java/com/ruoyi/common/geo/GeoParser.java new file mode 100644 index 0000000..975846d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/geo/GeoParser.java @@ -0,0 +1,178 @@ +package com.ruoyi.common.geo; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.lang.Assert; +import org.geotools.data.shapefile.ShapefileDataStore; +import org.geotools.data.simple.SimpleFeatureIterator; +import org.geotools.data.simple.SimpleFeatureSource; +import org.locationtech.jts.geom.Geometry; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; +import org.opengis.feature.type.AttributeDescriptor; + +import java.io.Closeable; +import java.io.File; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public final class GeoParser implements Closeable +{ + private ShapefileDataStore dataStore; + private SimpleFeatureSource source; + private Map columnMap; + + public GeoParser() + { + } + + public void Init() + { + } + + @Override + public void close() + { + CloseSource(); + if(null != dataStore) + { + dataStore.dispose(); + dataStore = null; + } + } + + public boolean Open(String path, String charsetName) + { + return Open(new File(path), charsetName); + } + + public boolean Open(File file, String charsetName) + { + close(); + + try { + dataStore = new ShapefileDataStore(file.toURL()); + Charset charset = Charset.forName(charsetName); + dataStore.setCharset(charset); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + private void CheckDataStore() + { + Assert.notNull(dataStore, "请先打开"); + } + + private void CheckSource() + { + Assert.notNull(source, "请先设置数据源"); + } + + public List GetTypeNames() + { + CheckDataStore(); + + try { + return Arrays.stream(dataStore.getTypeNames()).collect(Collectors.toList()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public boolean SetSource(String typeName) + { + CloseSource(); + if(null == typeName) + return true; + + CheckDataStore(); + + try { + source = dataStore.getFeatureSource(typeName); + return InitSource(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean SetSource(int index) + { + CloseSource(); + if(index < 0) + return true; + CheckDataStore(); + return SetSource(GetTypeNames().get(index)); + } + + private void CloseSource() + { + columnMap = null; + source = null; + } + + private boolean InitSource() + { + if(null == source) + return false; + + try { + if(null == columnMap) + columnMap = new LinkedHashMap<>(); + else + columnMap.clear(); + SimpleFeatureType schema = source.getSchema(); + for(int i = 0; i < schema.getAttributeCount(); i++) + { + AttributeDescriptor descriptor = schema.getDescriptor(i); + columnMap.put(i, descriptor.getLocalName()); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public List> GetMapList() + { + CheckSource(); + + try { + List> ret = new ArrayList<>(); + try(SimpleFeatureIterator features = source.getFeatures().features()) { + while(features.hasNext()) + { + SimpleFeature feature = features.next(); + Map map = new HashMap<>(); + for (int i = 0; i < feature.getAttributeCount(); i++) { + Object attribute = feature.getAttribute(i); + if(attribute instanceof Geometry) + attribute = ((Geometry) attribute).toText(); + map.put(columnMap.get(i), attribute); + } + ret.add(map); + } + } + return ret; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public List GetList(Class clazz) + { + List> maps = GetMapList(); + return maps.stream().map((x) -> BeanUtil.mapToBean(x, clazz, false, CopyOptions.create())).collect(Collectors.toList()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DecimalUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DecimalUtils.java index b5070e1..4e4bb7f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DecimalUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DecimalUtils.java @@ -487,4 +487,9 @@ public class DecimalUtils { return number.toString(); } } + + public static BigDecimal nullToZero(BigDecimal o) + { + return null == o ? BigDecimal.ZERO : o; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/DB.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/DB.java new file mode 100644 index 0000000..264c321 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/DB.java @@ -0,0 +1,108 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.utils.spring.SpringUtils; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; + +import java.util.function.Supplier; + +public final class DB +{ + private static class TransactionHandle + { + private final PlatformTransactionManager manager; + private final TransactionStatus transactionStatus; + + private TransactionHandle(PlatformTransactionManager manager, TransactionStatus transactionStatus) + { + this.manager = manager; + this.transactionStatus = transactionStatus; + } + + private static TransactionHandle Cast(Object handle) + { + return (TransactionHandle) handle; + } + } + + public static Object BeginTransaction() + { + PlatformTransactionManager manager = SpringUtils.getBean(PlatformTransactionManager.class); + TransactionDefinition definition = SpringUtils.getBean(TransactionDefinition.class); + TransactionStatus transaction = manager.getTransaction(definition); + return new TransactionHandle(manager, transaction); + } + + public static void CommitTransaction(Object handle) + { + TransactionHandle transaction = (TransactionHandle) handle; + transaction.manager.commit(transaction.transactionStatus); + } + + public static void RollbackTransaction(Object handle) + { + TransactionHandle transaction = TransactionHandle.Cast(handle); + transaction.manager.rollback(transaction.transactionStatus); + } + + public static void EndTransaction(Object handle) + { + if(null == handle) + return; + + TransactionHandle transaction = TransactionHandle.Cast(handle); + try + { + transaction.manager.commit(transaction.transactionStatus); + } + catch(Exception e) + { + e.printStackTrace(); + transaction.manager.rollback(transaction.transactionStatus); + throw new RuntimeException(e); + } + } + + public static void Transaction(Runnable runnable) + { + PlatformTransactionManager manager = SpringUtils.getBean(PlatformTransactionManager.class); + TransactionDefinition definition = SpringUtils.getBean(TransactionDefinition.class); + TransactionStatus transaction = manager.getTransaction(definition); + try + { + runnable.run(); + manager.commit(transaction); + } + catch(Exception e) + { + e.printStackTrace(); + manager.rollback(transaction); + throw new RuntimeException(e); + } + } + + public static T Transaction(Supplier runnable, T...ifExceptRet) + { + PlatformTransactionManager manager = SpringUtils.getBean(PlatformTransactionManager.class); + TransactionDefinition definition = SpringUtils.getBean(TransactionDefinition.class); + TransactionStatus transaction = manager.getTransaction(definition); + try + { + T res = runnable.get(); + manager.commit(transaction); + return res; + } + catch(Exception e) + { + e.printStackTrace(); + manager.rollback(transaction); + if(null != ifExceptRet && ifExceptRet.length > 0) + return ifExceptRet[0]; + else + throw new RuntimeException(e); + } + } + + private DB() {} +}