package com.shkpr.service.alambizplugin.components; import com.fasterxml.jackson.core.type.TypeReference; import com.global.base.log.LogLevelFlag; import com.global.base.log.LogPrintMgr; import com.shkpr.service.alambizplugin.apiparam.GisSurveyThirdExportParams; import com.shkpr.service.alambizplugin.commtools.CRSUtil; import com.shkpr.service.alambizplugin.commtools.ThirdImportTemplateUtils; import com.shkpr.service.alambizplugin.constants.CommAsyncStatusEnum; import com.shkpr.service.alambizplugin.constants.CommCRSDefine; import com.shkpr.service.alambizplugin.constants.FileTypeEnum; import com.shkpr.service.alambizplugin.constants.GisMetadataDefine; import com.shkpr.service.alambizplugin.constants.GisSurveyExcelDefine; import com.shkpr.service.alambizplugin.constants.LogFlagBusiType; import com.shkpr.service.alambizplugin.dbdao.services.intef.GisMetadataLayerTemplateService; import com.shkpr.service.alambizplugin.dbdao.services.intef.GisSurveyJobInfoService; import com.shkpr.service.alambizplugin.dbdao.services.intef.GisSurveyLayerApplyService; import com.shkpr.service.alambizplugin.dto.CommAsyncResult; import com.shkpr.service.alambizplugin.dto.GisMetadataLayerTemplate; import com.shkpr.service.alambizplugin.dto.GisMetadataPropertyTemplate; import com.shkpr.service.alambizplugin.dto.GisSurveyLayerApply; import com.shkpr.service.alambizplugin.dto.GisSurveyPropertyValue; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.lang3.StringUtils; import org.geotools.data.DataUtilities; import org.geotools.feature.SchemaException; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opengis.feature.simple.SimpleFeatureType; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import org.springframework.util.concurrent.ListenableFuture; import java.io.IOException; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.LocalDateTime; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** * 第三方导出执行器 * * @author 欧阳劲驰 * @since 1.0.0 */ @Component public class GisSurveyThirdExporter { /** * 结果前缀 */ public final static String RESULT_PREFIX = "third-export-"; /** * log */ private final String mStrClassName; private final String mBizType; private final AsyncResultManager asyncResultManager; private final GisSurveyJobInfoService jobInfoService; private final GisMetadataLayerTemplateService layerTemplateService; private final GisSurveyLayerApplyService gisSurveyLayerApplyService; public GisSurveyThirdExporter(AsyncResultManager asyncResultManager, GisSurveyJobInfoService jobInfoService , GisMetadataLayerTemplateService layerTemplateService, GisSurveyLayerApplyService gisSurveyLayerApplyService) { mStrClassName = "GisSurveyThirdExporter"; mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue(); this.asyncResultManager = asyncResultManager; this.jobInfoService = jobInfoService; this.layerTemplateService = layerTemplateService; this.gisSurveyLayerApplyService = gisSurveyLayerApplyService; } /** * 第三方导出任务 * * @param params 导出参数 * @return 导出结果 */ @Async public ListenableFuture>> thirdExportTask(GisSurveyThirdExportParams params) { //参数 String jobId = params.getJobId(); FileTypeEnum fileType = params.getFileTypeEnum(); String targetCRSCode = params.getTargetCRSCode(); String operator = params.getOperator(); //构建返回 CommAsyncResult> result = CommAsyncResult.fail(params.getJobId()); result.setOperator(operator); try { LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName , String.format( "开始执行第三方导出;任务id: %s" , jobId ) ); //查询元素更新时间 LocalDateTime refreshTime = jobInfoService.findRefreshTime(jobId); //查询点线数据 List points = gisSurveyLayerApplyService.findAllByJobIdAndKind(jobId, GisMetadataDefine.TYPE_KINE.POINT); List lines = gisSurveyLayerApplyService.findAllByJobIdAndKind(jobId, GisMetadataDefine.TYPE_KINE.LINE); //处理坐标系 if (!Objects.equals(targetCRSCode, CommCRSDefine.CGCS2000)) transformGeom(targetCRSCode, points, lines); //获取图层key List pointLayer = getLayerKeys(points); List lineLayer = getLayerKeys(lines); //查询点图层模版 List pointLayerTemplates = layerTemplateService.findByKeyIn(pointLayer); //查询线图层模版 List lineLayerTemplates = layerTemplateService.findByKeyIn(lineLayer); //结果flag final String FLAG = RESULT_PREFIX + jobId; //创建临时文件夹 if (!asyncResultManager.createTempDirectory(FLAG)) return new AsyncResult<>(result); //根据文件类型导出 Path outputPath = null; if (fileType == FileTypeEnum.EXCEL) outputPath = exportExcel(points, lines, pointLayerTemplates, lineLayerTemplates, FLAG); if (fileType == FileTypeEnum.SHAPE_FILE) outputPath = exportShape(points, lines, pointLayerTemplates, lineLayerTemplates, FLAG); if (outputPath == null || !Files.exists(outputPath)) { //打印报错信息 LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName , String.format("第三方导出文件写入失败, 任务id:%s", jobId) ); return new AsyncResult<>(result); } //获取上次结果 CommAsyncResult> lastResult = asyncResultManager.getResult(FLAG, new TypeReference>>() { }); //设置数据 Map data = new HashMap<>(); if (lastResult != null && lastResult.getData() != null) data = lastResult.getData(); data.put(fileType.getName(), FLAG + "/" + outputPath.getFileName().toString()); //设置数据时间 Map refreshTimes = new HashMap<>(); if (lastResult != null && lastResult.getRefreshTimes() != null) refreshTimes = lastResult.getRefreshTimes(); refreshTimes.put(fileType.getName(), refreshTime); //导出完成 result.setStatus(CommAsyncStatusEnum.SUCCESS.getCode()); result.setCompleteTime(LocalDateTime.now()); result.setData(data); result.setRefreshTimes(refreshTimes); result.setSubitemKeys(Collections.singletonList(fileType.getName())); //写入结果 Path resultPath = asyncResultManager.writeJson(result, FLAG, AsyncResultManager.RESULT_FILE_NAME); if (resultPath == null || !Files.exists(resultPath)) { //打印报错信息 LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName , String.format("第三方导出文件写入失败, 任务id:%s", jobId) ); return new AsyncResult<>(CommAsyncResult.fail(jobId)); } //替换结果 if (!asyncResultManager.replaceResult(FLAG)) return new AsyncResult<>(CommAsyncResult.fail(jobId)); LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName , String.format( "结束第三方导出;任务id: %s, 用时(毫秒):%d" , jobId , Duration.between(result.getRequestTime(), result.getCompleteTime()).toMillis() ) ); return new AsyncResult<>(result); } catch (InterruptedException | IOException | ArchiveException | SchemaException e) { //打印报错信息 LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName , String.format("第三方导出异常 任务id:%s error:%s", jobId, e) ); return new AsyncResult<>(CommAsyncResult.fail(jobId)); } } /** * 转换geom * * @param targetCRSCode 目标坐标系 * @param points 点集合 * @param lines 线结合 */ private void transformGeom(String targetCRSCode, List points, List lines) { //处理点 points.parallelStream().forEach(point -> { //获取geom Geometry geometry = point.getGis(); if (geometry == null) return; //转换geom Geometry transform = CRSUtil.transform(geometry, CommCRSDefine.CGCS2000, targetCRSCode); if (transform == null) return; //回填元素 point.setGis(transform); //获取解析后的经纬度 Coordinate coordinate = transform.getCoordinate(); String lngStr = BigDecimal.valueOf(coordinate.getX()).toPlainString(); String latStr = BigDecimal.valueOf(coordinate.getY()).toPlainString(); //回填经纬度属性 List propertyValues = point.getPropertyValues(); propertyValues.stream() .filter(it -> Objects.equals(it.getProperty(), GisSurveyExcelDefine.TEMPLATE.LAT)) .forEach(it -> it.setValue(latStr)); propertyValues.stream() .filter(it -> Objects.equals(it.getProperty(), GisSurveyExcelDefine.TEMPLATE.LNG)) .forEach(it -> it.setValue(lngStr)); }); //处理线 lines.parallelStream().forEach(line -> { //获取geom Geometry geometry = line.getGis(); if (geometry == null) return; //转换geom Geometry transform = CRSUtil.transform(geometry, CommCRSDefine.CGCS2000, targetCRSCode); if (transform == null) return; //回填元素 line.setGis(transform); }); } /** * 导出excel * * @param points 点 * @param lines 线 * @param pointLayerTemplates 点图层模版 * @param lineLayerTemplates 线图层模版 * @param FLAG 结果flag * @return excel路径 * @throws IOException io异常 */ private Path exportExcel(List points, List lines, List pointLayerTemplates, List lineLayerTemplates, String FLAG) throws IOException { //构建excel头 Map> excelHeader = buildExcelHeader(pointLayerTemplates, lineLayerTemplates); //构建excel数据 Map>> excelData = buildExcelData(points, lines, pointLayerTemplates, lineLayerTemplates); //导出excel return asyncResultManager.writeExcel(excelHeader, excelData, FLAG, "third-export.xlsx"); } /** * 导出shape * * @param points 点 * @param lines 线 * @param pointLayerTemplates 点图层模版 * @param lineLayerTemplates 线图层模版 * @param FLAG 结果flag * @return shape路径 * @throws IOException io异常 * @throws ArchiveException 压缩异常 */ private Path exportShape(List points, List lines, List pointLayerTemplates, List lineLayerTemplates, String FLAG) throws IOException, ArchiveException, SchemaException { //构建shape数据 Map>> shapeData = buildShapeData(points, lines, pointLayerTemplates, lineLayerTemplates); //导出shape return asyncResultManager.writeShape(shapeData, FLAG, "third-export.zip"); } /** * 获取图层key * * @param datas 数据 * @return 图层key集合 */ private List getLayerKeys(List datas) { //去重图层 return datas.parallelStream() .map(GisSurveyLayerApply::getLayer) .filter(StringUtils::isNotBlank) .distinct() .collect(Collectors.toList()); } /** * 构建excel表头 * * @param pointLayerTemplates 点图层模版 * @param lineLayerTemplates 线图层模版 * @return excel数据 */ private Map> buildExcelHeader(List pointLayerTemplates , List lineLayerTemplates) { //点excel表头 Map pointExcelHeader = new LinkedHashMap<>(); //线excel表头 Map lineExcelHeader = new LinkedHashMap<>(); //点模版表头 Map pointTemplateHeader = pointLayerTemplates.stream() .flatMap(it -> it.getPropertyTemplates().stream()) .sorted(Comparator.comparing(GisMetadataPropertyTemplate::getOrdering)) .collect(Collectors.toMap(GisMetadataPropertyTemplate::getKey, GisMetadataPropertyTemplate::getName , (it1, it2) -> it1, LinkedHashMap::new)); //线模版表头 Map lineTemplateHeader = lineLayerTemplates.stream() .flatMap(it -> it.getPropertyTemplates().stream()) .collect(Collectors.toMap(GisMetadataPropertyTemplate::getKey, GisMetadataPropertyTemplate::getName , (it1, it2) -> it1, LinkedHashMap::new)); //加入图层表头 pointExcelHeader.put(GisSurveyExcelDefine.TEMPLATE.LAYER, GisSurveyExcelDefine.FILE.POINT_LAYER); lineExcelHeader.put(GisSurveyExcelDefine.TEMPLATE.LAYER, GisSurveyExcelDefine.FILE.LINE_LAYER); //加入模版表头 pointExcelHeader.putAll(pointTemplateHeader); lineExcelHeader.putAll(lineTemplateHeader); //excel表头 Map> excelHeader = new HashMap<>(); excelHeader.put(GisMetadataDefine.TYPE_KINE.POINT, pointExcelHeader); excelHeader.put(GisMetadataDefine.TYPE_KINE.LINE, lineExcelHeader); return excelHeader; } /** * 构建excel数据 * * @param points 点元素 * @param lines 线元素 * @param pointLayerTemplates 点图层模版 * @param lineLayerTemplates 线图层模版 * @return excel数据 */ private Map>> buildExcelData(List points, List lines , List pointLayerTemplates, List lineLayerTemplates) { LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始构建excel数据 ======>"); long begin = System.currentTimeMillis(); //excel数据 Map>> excelData = new HashMap<>(); excelData.put(GisMetadataDefine.TYPE_KINE.POINT, buildExcelSheetData(points, pointLayerTemplates)); excelData.put(GisMetadataDefine.TYPE_KINE.LINE, buildExcelSheetData(lines, lineLayerTemplates)); long end = System.currentTimeMillis(); LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName , String.format( "结束构建excel数据,用时(毫秒):%d" , (end - begin) ) ); return excelData; } /** * 构建excel页数据 * * @param layerApplies 采集元素集合 * @param layerTemplates 图层模版 * @return excel数据 */ private List> buildExcelSheetData(List layerApplies, List layerTemplates) { return layerApplies.parallelStream() .map(it -> { //处理模版数据 Map rowData = it.getPropertyValues().stream() .filter(it1 -> !StringUtils.isAnyBlank(it1.getProperty(), it1.getValue())) .collect(Collectors.toMap(GisSurveyPropertyValue::getProperty, GisSurveyPropertyValue::getValue , (i1, i2) -> i1)); //处理图层数据 if (StringUtils.isNotBlank(it.getLayer())) { //获取模版 GisMetadataLayerTemplate gisMetadataLayerTemplate = layerTemplates.stream() .filter(it1 -> Objects.equals(it1.getKey(), it.getLayer())) .findFirst().orElse(null); //存入图层 if (gisMetadataLayerTemplate != null) rowData.put(GisSurveyExcelDefine.TEMPLATE.LAYER, gisMetadataLayerTemplate.getName()); } return rowData; }).collect(Collectors.toList()); } /** * 构建shape数据 * * @param points 点元素 * @param lines 线元素 * @param pointLayerTemplates 点图层模版 * @param lineLayerTemplates 线图层模版 * @return excel数据 */ private Map>> buildShapeData(List points, List lines , List pointLayerTemplates, List lineLayerTemplates) throws SchemaException { LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName, "开始构建shape属性头和数据 ======>"); long begin = System.currentTimeMillis(); //点模版规格 String pointTemplateSpec = pointLayerTemplates.stream() .flatMap(it -> it.getPropertyTemplates().stream()) .sorted(Comparator.comparing(GisMetadataPropertyTemplate::getOrdering)) .map(it -> String.format("%s%s" , StringUtils.substring(it.getName(), 0, 5) , GisSurveyExcelDefine.FILE_HANDLE.COL_SPEC) ) .distinct() .collect(Collectors.joining(",")); //线模版规格 String lineTemplateSpec = lineLayerTemplates.stream() .flatMap(it -> it.getPropertyTemplates().stream()) .sorted(Comparator.comparing(GisMetadataPropertyTemplate::getOrdering)) .map(it -> String.format("%s%s" , StringUtils.substring(it.getName(), 0, 5) , GisSurveyExcelDefine.FILE_HANDLE.COL_SPEC) ) .distinct() .collect(Collectors.joining(",")); //创建点类型 final SimpleFeatureType POINT_TYPE = DataUtilities.createType( "point", String.format("%s%s", GisSurveyExcelDefine.FILE_HANDLE.POINT_SPEC, pointTemplateSpec) ); //创建线类型 final SimpleFeatureType LINE_TYPE = DataUtilities.createType( "line", String.format("%s%s", GisSurveyExcelDefine.FILE_HANDLE.LINE_SPEC, lineTemplateSpec) ); //shape数据 Map>> shapeData = new HashMap<>(); shapeData.put(POINT_TYPE, buildShapeItemData(points, pointLayerTemplates)); shapeData.put(LINE_TYPE, buildShapeItemData(lines, lineLayerTemplates)); long end = System.currentTimeMillis(); LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName , String.format( "结束构建shape属性头和数据,用时(毫秒):%d" , (end - begin) ) ); return shapeData; } /** * 构建shape项数据 * * @param layerApplies 采集元素集合 * @param layerTemplates 图层模版 * @return excel数据 */ private List> buildShapeItemData(List layerApplies, List layerTemplates) { return layerApplies.parallelStream() .map(it -> { Map rowData = new HashMap<>(); //获取图层模版 GisMetadataLayerTemplate layerTemplate = ThirdImportTemplateUtils.getLayerTemplate(it, layerTemplates); //处理图层数据 if (layerTemplate != null) { //存入图层 if (GisMetadataDefine.TYPE_KINE.POINT.equals(it.getKind())) rowData.put(GisSurveyExcelDefine.FILE.POINT_LAYER, layerTemplate.getName()); if (GisMetadataDefine.TYPE_KINE.LINE.equals(it.getKind())) rowData.put(GisSurveyExcelDefine.FILE.LINE_LAYER, layerTemplate.getName()); //遍历属性 it.getPropertyValues().forEach(propertyValue -> { //获取属性模版 GisMetadataPropertyTemplate propertyTemplate = ThirdImportTemplateUtils .getPropertyTemplate(layerTemplate, propertyValue.getProperty()); //填入属性数据 if (propertyTemplate != null) rowData.put(StringUtils.substring(propertyTemplate.getName(), 0, 5) , propertyValue.getValue()); }); } //存入geom rowData.put(GisSurveyExcelDefine.FILE.THE_GEOM, it.getGis()); return rowData; }).collect(Collectors.toList()); } }