Autor | SHA1 | Wiadomość | Data |
---|---|---|---|
|
97b5027656 | 导入任务 | 5 dni temu |
|
b15d3d8eeb | 关闭热部署 | 5 dni temu |
@@ -40,6 +40,7 @@ | |||
<de.odysseus.juel.version>2.1.3</de.odysseus.juel.version> | |||
<httpclient.version>4.5.13</httpclient.version> | |||
<net.coobird.version>0.4.8</net.coobird.version> | |||
<geotools.version>20.0</geotools.version> | |||
</properties> | |||
<!-- 依赖声明 --> | |||
@@ -230,6 +231,33 @@ | |||
<version>${net.coobird.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-test</artifactId> | |||
<scope>test</scope> | |||
<version>${spring-boot.version}</version> | |||
</dependency> | |||
<!-- 添加GeoTools依赖 --> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-shapefile</artifactId> | |||
<version>${geotools.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-swing</artifactId> | |||
<version>${geotools.version}</version> | |||
</dependency> | |||
<!-- wkt转geojson --> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-geojson</artifactId> | |||
<version>${geotools.version}</version> | |||
</dependency> | |||
<!-- 定时任务--> | |||
@@ -312,6 +340,17 @@ | |||
<enabled>true</enabled> | |||
</releases> | |||
</repository> | |||
<repository> | |||
<id>osgeo</id> | |||
<name>OSGeo Release Repository</name> | |||
<url>https://repo.osgeo.org/repository/release/</url> | |||
<snapshots> | |||
<enabled>false</enabled> | |||
</snapshots> | |||
<releases> | |||
<enabled>true</enabled> | |||
</releases> | |||
</repository> | |||
</repositories> | |||
<pluginRepositories> | |||
@@ -67,6 +67,11 @@ | |||
<artifactId>ruoyi-business</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-test</artifactId> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
@@ -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); | |||
} | |||
} |
@@ -66,7 +66,7 @@ spring: | |||
devtools: | |||
restart: | |||
# 热部署开关 | |||
enabled: true | |||
enabled: false | |||
# redis 配置 | |||
redis: | |||
# 地址 | |||
@@ -133,3 +133,9 @@ xss: | |||
excludes: /system/notice | |||
# 匹配链接 | |||
urlPatterns: /system/*,/monitor/*,/tool/* | |||
importTask: | |||
# 是否启动时执行等待中的任务 | |||
startOnBoot: false | |||
# 下次建议读取日志的间隔(毫秒) | |||
logNextPoll: 2000 |
@@ -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<Map<String, Object>> cjqies = geo.GetMapList(); | |||
//List<GeoCJQY> cjqies = geo.GetList(GeoCJQY.class); | |||
//List<GeoJTZY> cjqies = geo.GetList(GeoJTZY.class); | |||
//List<GeoZYHYCG> cjqies = geo.GetList(GeoZYHYCG.class); | |||
//List<GeoDK> cjqies = geo.GetList(GeoDK.class); | |||
System.err.println(JSON.toJSONString(cjqies)); | |||
} | |||
} | |||
} |
@@ -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() {} | |||
} |
@@ -74,4 +74,6 @@ public interface TGisCjqyMapper | |||
* @return 结果 | |||
*/ | |||
public int deleteTGisCjqyByFids(Long[] fids); | |||
public int deleteByCode(String importCode); | |||
} |
@@ -74,4 +74,7 @@ public interface TTaskImportMapper | |||
* @return 结果 | |||
*/ | |||
public int deleteTTaskImportByIds(Long[] ids); | |||
public int terminateRunningTask(); | |||
public TTaskImport getForUpdate(Long id); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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() {} | |||
} |
@@ -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 "<profile directory>" + 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 "<profile directory>" + fpath.substring(fprofile.length()); | |||
} | |||
private GeoSysDir() {} | |||
} |
@@ -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<GeoCJQY> 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<String> 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<TGisCjqy> LoadDatabase(String qydm) | |||
{ | |||
TGisCjqy cond = new TGisCjqy(); | |||
cond.setCjqydm(qydm); | |||
List<TGisCjqy> 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<TGisCjqy> insertList, List<TGisCjqy> 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<GeoCJQY> datas) | |||
{ | |||
importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); | |||
List<TGisCjqy> databases = LoadDatabase(dept.getImportCode()); | |||
Map<String, TGisCjqy> exists = databases.stream().collect(Collectors.toMap(TGisCjqy::getCjqydm, x->x)); | |||
List<TGisCjqy> insertList = new ArrayList<>(); | |||
List<TGisCjqy> 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<String, List<GeoCJQY>> 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; | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.ruoyi.geo.framework; | |||
public interface GeoDBTaskInterface | |||
{ | |||
public int LoadDataList(); | |||
public void CheckData(); | |||
public int SaveData(); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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<String> FindFiles(File dir, String suffix) | |||
{ | |||
return FindFiles_r(new ArrayList<>(), dir, suffix); | |||
} | |||
public static List<String> FindFiles_r(List<String> 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() {} | |||
} |
@@ -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<String, SysDept> importDeptMap; | |||
private Map<String, SysDept> sysDeptMap; | |||
private List<String> shpFiles; | |||
private List<String> 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<SysDept> 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; | |||
} | |||
} |
@@ -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<GeoJTZY> 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<String> 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<TResourceLand> LoadDatabase(String qydm) | |||
{ | |||
TResourceLand cond = new TResourceLand(); | |||
cond.setImportCode(qydm); | |||
List<TResourceLand> 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<TResourceLand> insertList, List<TResourceLand> 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<GeoJTZY> datas) | |||
{ | |||
importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); | |||
List<TResourceLand> databases = LoadDatabase(dept.getImportCode()); | |||
Map<String, TResourceLand> exists = databases.stream().collect(Collectors.toMap(TResourceLand::getDkbm, x->x)); | |||
List<TResourceLand> insertList = new ArrayList<>(); | |||
List<TResourceLand> 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<String, List<GeoJTZY>> 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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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<Runnable> 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<Runnable> 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<Runnable> task) | |||
{ | |||
if(IsRunning()) | |||
AddTask(task); | |||
else | |||
{ | |||
queue.addAll(task); | |||
Start(); | |||
} | |||
} | |||
public synchronized Runnable GetCurrentTask() | |||
{ | |||
return currentTask; | |||
} | |||
public synchronized Runnable FindTask(Predicate<Runnable> 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(); | |||
} | |||
} |
@@ -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<GeoZYHYCG> 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<String> 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<TResourceOperation> LoadDatabase(String qydm) | |||
{ | |||
TResourceOperation cond = new TResourceOperation(); | |||
cond.setImportCode(qydm); | |||
List<TResourceOperation> 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<TResourceOperation> insertList, List<TResourceOperation> 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<GeoZYHYCG> datas) | |||
{ | |||
importTask.logFile.WriteLine(TYPE_NAME + "开始处理数据: 区划代码={}, 条数={}", dept.getDeptName(), datas.size()); | |||
List<TResourceOperation> databases = LoadDatabase(dept.getImportCode()); | |||
Map<String, TResourceOperation> exists = databases.stream().collect(Collectors.toMap(TResourceOperation::getDkbm, x->x)); | |||
List<TResourceOperation> insertList = new ArrayList<>(); | |||
List<TResourceOperation> 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<String, List<GeoZYHYCG>> 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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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<TTaskImport> taskImports = taskImportMapper.selectTTaskImportList(cond); | |||
log.info("拉取到排队中的任务: " + taskImports.size()); | |||
List<Runnable> 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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -94,5 +94,6 @@ public interface TResourceLandMapper | |||
*/ | |||
public TBigDataScreen homepageDownStatistics(Long deptId); | |||
public int deleteByCode(String importCode); | |||
} |
@@ -77,4 +77,6 @@ public interface TResourceOperationMapper | |||
* @return 结果 | |||
*/ | |||
public int deleteTResourceOperationByIds(Long[] ids); | |||
public int deleteByCode(String importCode); | |||
} |
@@ -15,7 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
</resultMap> | |||
<sql id="selectTGisCjqyVo"> | |||
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 | |||
</sql> | |||
<select id="selectTGisCjqyList" parameterType="TGisCjqy" resultMap="TGisCjqyResult"> | |||
@@ -47,7 +47,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
<if test="cjqymc != null and cjqymc != ''">#{cjqymc},</if> | |||
<if test="bsm != null">#{bsm},</if> | |||
<if test="ysdm != null">#{ysdm},</if> | |||
<if test="theGeom != null">#{theGeom},</if> | |||
<if test="theGeom != null">ST_GEOMFROMTEXT(#{theGeom}),</if> | |||
<if test="importCode != null and importCode != ''">#{importCode},</if> | |||
</trim> | |||
</insert> | |||
@@ -69,7 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
#{item.cjqymc}, | |||
#{item.bsm}, | |||
#{item.ysdm}, | |||
#{item.theGeom}, | |||
ST_GEOMFROMTEXT(#{item.theGeom}), | |||
#{item.importCode}, | |||
</trim> | |||
</foreach> | |||
@@ -82,7 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
<if test="cjqymc != null and cjqymc != ''">cjqymc = #{cjqymc},</if> | |||
<if test="bsm != null">bsm = #{bsm},</if> | |||
<if test="ysdm != null">ysdm = #{ysdm},</if> | |||
<if test="theGeom != null">the_geom = #{theGeom},</if> | |||
<if test="theGeom != null">the_geom = ST_GEOMFROMTEXT(#{theGeom}),</if> | |||
<if test="importCode != null and importCode != ''">import_code = #{importCode},</if> | |||
</trim> | |||
where fid = #{fid} | |||
@@ -97,7 +97,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
<if test="item.cjqymc != null and item.cjqymc != ''">cjqymc = #{item.cjqymc},</if> | |||
<if test="item.bsm != null">bsm = #{item.bsm},</if> | |||
<if test="item.ysdm != null">ysdm = #{item.ysdm},</if> | |||
<if test="item.theGeom != null">the_geom = #{item.theGeom},</if> | |||
<if test="item.theGeom != null">the_geom = ST_GEOMFROMTEXT(#{item.theGeom}),</if> | |||
<if test="item.importCode != null and item.importCode != ''">import_code = #{item.importCode},</if> | |||
</set> | |||
where fid = #{item.fid} | |||
@@ -114,4 +114,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
#{fid} | |||
</foreach> | |||
</delete> | |||
<delete id="deleteByCode" parameterType="String"> | |||
delete from t_gis_cjqy where import_code = #{importCode} | |||
</delete> | |||
</mapper> |
@@ -161,4 +161,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
#{id} | |||
</foreach> | |||
</delete> | |||
<update id="terminateRunningTask"> | |||
update t_task_import | |||
set task_status = '5' | |||
where task_status = '2' | |||
</update> | |||
<select id="getForUpdate" parameterType="Long" resultMap="TTaskImportResult"> | |||
<include refid="selectTTaskImportVo"/> | |||
where id = #{id} FOR UPDATE | |||
</select> | |||
</mapper> |
@@ -49,7 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
</resultMap> | |||
<sql id="selectTResourceLandVo"> | |||
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 | |||
</sql> | |||
<sql id="selectTResourceLandMap"> | |||
@@ -156,7 +156,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
<if test="zjrxm != null">#{zjrxm},</if> | |||
<if test="txmj != null">#{txmj},</if> | |||
<if test="scmjm != null">#{scmjm},</if> | |||
<if test="theGeom != null">#{theGeom},</if> | |||
<if test="theGeom != null">ST_GEOMFROMTEXT(#{theGeom}),</if> | |||
<if test="qsdwdm != null">#{qsdwdm},</if> | |||
<if test="qsdwmc != null">#{qsdwmc},</if> | |||
<if test="sfzwd != null">#{sfzwd},</if> | |||
@@ -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" | |||
<if test="zjrxm != null">ZJRXM = #{zjrxm},</if> | |||
<if test="txmj != null">TXMJ = #{txmj},</if> | |||
<if test="scmjm != null">SCMJM = #{scmjm},</if> | |||
<if test="theGeom != null">the_geom = #{theGeom},</if> | |||
<if test="theGeom != null">the_geom = ST_GEOMFROMTEXT(#{theGeom}),</if> | |||
<if test="qsdwdm != null">QSDWDM = #{qsdwdm},</if> | |||
<if test="qsdwmc != null">QSDWMC = #{qsdwmc},</if> | |||
<if test="sfzwd != null">SFZWD = #{sfzwd},</if> | |||
@@ -298,7 +298,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
<if test="item.zjrxm != null">ZJRXM = #{item.zjrxm},</if> | |||
<if test="item.txmj != null">TXMJ = #{item.txmj},</if> | |||
<if test="item.scmjm != null">SCMJM = #{item.scmjm},</if> | |||
<if test="item.theGeom != null">the_geom = #{item.theGeom},</if> | |||
<if test="item.theGeom != null">the_geom = ST_GEOMFROMTEXT(#{item.theGeom}),</if> | |||
<if test="item.qsdwdm != null">QSDWDM = #{item.qsdwdm},</if> | |||
<if test="item.qsdwmc != null">QSDWMC = #{item.qsdwmc},</if> | |||
<if test="item.sfzwd != null">SFZWD = #{item.sfzwd},</if> | |||
@@ -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} | |||
</select> | |||
<delete id="deleteByCode" parameterType="String"> | |||
delete from t_resource_land where import_code = #{importCode} | |||
</delete> | |||
</mapper> |
@@ -281,4 +281,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |||
#{id} | |||
</foreach> | |||
</delete> | |||
<delete id="deleteByCode" parameterType="String"> | |||
delete from t_resource_operation where import_code = #{importCode} | |||
</delete> | |||
</mapper> |
@@ -155,6 +155,23 @@ | |||
<artifactId>hutool-all</artifactId> | |||
</dependency> | |||
<!-- 添加GeoTools依赖 --> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-shapefile</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-swing</artifactId> | |||
</dependency> | |||
<!-- wkt转geojson --> | |||
<dependency> | |||
<groupId>org.geotools</groupId> | |||
<artifactId>gt-geojson</artifactId> | |||
</dependency> | |||
</dependencies> | |||
@@ -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<Integer, String> 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<String> 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<Map<String, Object>> GetMapList() | |||
{ | |||
CheckSource(); | |||
try { | |||
List<Map<String, Object>> ret = new ArrayList<>(); | |||
try(SimpleFeatureIterator features = source.getFeatures().features()) { | |||
while(features.hasNext()) | |||
{ | |||
SimpleFeature feature = features.next(); | |||
Map<String, Object> 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 <T> List<T> GetList(Class<T> clazz) | |||
{ | |||
List<Map<String, Object>> maps = GetMapList(); | |||
return maps.stream().map((x) -> BeanUtil.mapToBean(x, clazz, false, CopyOptions.create())).collect(Collectors.toList()); | |||
} | |||
} |
@@ -487,4 +487,9 @@ public class DecimalUtils { | |||
return number.toString(); | |||
} | |||
} | |||
public static BigDecimal nullToZero(BigDecimal o) | |||
{ | |||
return null == o ? BigDecimal.ZERO : o; | |||
} | |||
} |
@@ -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> T Transaction(Supplier<T> 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() {} | |||
} |