diff --git a/admin/src/main/java/com/multictrl/common/utils/kmz/KmzUtils.java b/admin/src/main/java/com/multictrl/common/utils/kmz/KmzUtils.java index 5165cbb..5740062 100644 --- a/admin/src/main/java/com/multictrl/common/utils/kmz/KmzUtils.java +++ b/admin/src/main/java/com/multictrl/common/utils/kmz/KmzUtils.java @@ -2,11 +2,15 @@ package com.multictrl.common.utils.kmz; import com.multictrl.common.exception.RenException; import com.multictrl.modules.business.dto.RouteDTO; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -96,4 +100,90 @@ public class KmzUtils { } } } + + /** + * 解析 KMZ 文件,提取指定内容为 byte[] + * + * @param inputStream KMZ 文件流 + * @return 包含文件名与 byte[] 的映射 + * @throws IOException IO 异常 + */ + public static Map extractKmzAsBytes(InputStream inputStream) throws IOException { + Map result = new HashMap<>(); + + try (ZipArchiveInputStream zipStream = new ZipArchiveInputStream(inputStream)) { + ZipArchiveEntry entry; + while ((entry = zipStream.getNextEntry()) != null) { + String entryName = entry.getName(); + + if (WPMZ_TEMPLATE_KML.equals(entryName)) { + result.put("template.kml", readStreamToBytes(zipStream)); + } else if (WPMZ_WAYLINES_WPML.equals(entryName)) { + result.put("waylines.wpml", readStreamToBytes(zipStream)); + } + } + } + + return result; + } + + /** + * 将 InputStream 读取为 byte[] + * + * @param inputStream 输入流 + * @return 字节数组 + * @throws IOException IO 异常 + */ + private static byte[] readStreamToBytes(InputStream inputStream) throws IOException { + try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + byte[] data = new byte[1024]; + int bytesRead; + + while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, bytesRead); + } + + return buffer.toByteArray(); + } + } + + /** + * 将 zip 输入流解压到目标目录,完整保留 zip 内的目录结构。 + * + * @param zipStream zip 输入流(调用方负责关闭外层流) + * @param destDir 目标目录 + * @throws IOException 解压失败 + */ + public static void unzipToDirectory(InputStream zipStream, File destDir) throws Exception { + if (!destDir.exists()) { + Files.createDirectories(destDir.toPath()); + } + + try (ZipInputStream zis = new ZipInputStream(zipStream)) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String entryName = entry.getName(); + File targetFile = new File(destDir, entryName); + Path targetPath = targetFile.getCanonicalFile().toPath(); + Path destPath = destDir.getCanonicalFile().toPath(); + if (!targetPath.startsWith(destPath)) { + throw new RenException("非法路径:" + entryName); + } + + if (entry.isDirectory()) { + Files.createDirectories(targetFile.toPath()); + } else { + Files.createDirectories(targetFile.getParentFile().toPath()); + try (FileOutputStream fos = new FileOutputStream(targetFile)) { + byte[] buffer = new byte[8192]; + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zis.closeEntry(); + } + } + } } diff --git a/admin/src/main/java/com/multictrl/common/utils/kmz/WpmlKmlParser.java b/admin/src/main/java/com/multictrl/common/utils/kmz/WpmlKmlParser.java new file mode 100644 index 0000000..ccce424 --- /dev/null +++ b/admin/src/main/java/com/multictrl/common/utils/kmz/WpmlKmlParser.java @@ -0,0 +1,279 @@ +package com.multictrl.common.utils.kmz; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.multictrl.common.constant.BusinessConstant; +import com.multictrl.common.constant.SetActionParams; +import com.multictrl.common.exception.RenException; +import com.multictrl.modules.business.dto.RouteDTO; +import com.multictrl.modules.business.dto.RouteWaypointDTO; +import com.multictrl.modules.business.dto.WaypointActionDTO; +import org.apache.commons.lang3.StringUtils; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +/** + * kml解析 + * + * @author Sdy + * @since 1.0.0 2026/6/18 + */ +public class WpmlKmlParser { + + public static RouteDTO parseKmlFile(byte[] bytes, Boolean accurateImport) throws DocumentException { + RouteDTO dto = new RouteDTO(); + SAXReader reader = new SAXReader(); + Document document = reader.read(new ByteArrayInputStream(bytes)); + Element root = document.getRootElement(); + + Element doc = root.element("Document"); + if (doc == null) { + throw new RenException("kml文件有误,无法解析"); + } + Element folder = doc.element("Folder"); + if (folder == null) { + throw new RenException("模板信息不存在,无法解析"); + } + //解析高度模式 + Element waylineCoordinateSysParam = folder.element("waylineCoordinateSysParam"); + if (waylineCoordinateSysParam == null) { + if (!accurateImport) { + throw new RenException("坐标系参数有误,无法解析"); + } + } else { + String heightMode = filedValid(waylineCoordinateSysParam.elementText("heightMode"), accurateImport); + dto.setHeightModel(heightMode); + } + + // 解析 + String templateType = folder.elementText("templateType"); + if (StringUtils.isEmpty(templateType) && accurateImport) { + return dto; + } + if (StringUtils.isEmpty(templateType) && !accurateImport) { + throw new RenException("模板类型不存在,解析失败"); + } + dto.setTemplateType(templateType); + String autoFlightSpeed = filedValid(folder.elementText("autoFlightSpeed"), accurateImport); + dto.setFlightSpeed(autoFlightSpeed == null ? null : Double.parseDouble(autoFlightSpeed)); + String globalHeight = filedValid(folder.elementText("globalHeight"), accurateImport); + dto.setFlightHeight(globalHeight == null ? null : Double.parseDouble(globalHeight)); + + List waypointList = new ArrayList<>(); + List placemarks = folder.elements("Placemark"); + if (CollectionUtil.isEmpty(placemarks)) { + if (accurateImport) { + dto.setRouteWaypointList(waypointList); + } else { + throw new RenException("航点信息不存在,解析失败"); + } + } else { + for (Element placemark : placemarks) { + RouteWaypointDTO waypoint = new RouteWaypointDTO(); + String useGlobalSpeed = placemark.elementText("useGlobalSpeed"); + String useGlobalHeight = placemark.elementText("useGlobalHeight"); + waypoint.setFollowRouteSpeed(filedValid(useGlobalSpeed, accurateImport) == null ? null : "1".equals(useGlobalSpeed)); + waypoint.setFollowRouteHeight(filedValid(useGlobalHeight, accurateImport) == null ? null : "1".equals(useGlobalHeight)); + waypointList.add(waypoint); + } + dto.setRouteWaypointList(waypointList); + } + return dto; + } + + public static void parseWpmlFile(byte[] bytes, RouteDTO dto, Boolean accurateImport) throws DocumentException, IOException { + SAXReader reader = new SAXReader(); + Document document = reader.read(new ByteArrayInputStream(bytes)); + Element root = document.getRootElement(); + + Element doc = root.element("Document"); + if (doc == null) { + throw new RenException("wpml文件有误,无法解析"); + } + Element missionConfig = doc.element("missionConfig"); + if (missionConfig == null) { + throw new RenException("任务信息不存在,无法解析"); + } + dto.setFlyToWaylineMode(filedValid(missionConfig.elementText("flyToWaylineMode"), accurateImport)); + dto.setFinishAction(filedValid(missionConfig.elementText("finishAction"), accurateImport)); + String exitOnRCLost = filedValid(missionConfig.elementText("exitOnRCLost"), accurateImport); + dto.setExitOnRcLost(exitOnRCLost); + if ("executeLostAction".equals(exitOnRCLost)) { + dto.setExecuteRcLostAction(filedValid(missionConfig.elementText("executeRCLostAction"), accurateImport)); + } + String takeOffSecurityHeight = filedValid(missionConfig.elementText("takeOffSecurityHeight"), accurateImport); + dto.setTakeoffSecurityHeight(StringUtils.isBlank(takeOffSecurityHeight) ? null : Double.parseDouble(missionConfig.elementText("takeOffSecurityHeight"))); + String globalTransitionalSpeed = filedValid(missionConfig.elementText("globalTransitionalSpeed"), accurateImport); + dto.setGlobalTransitionalSpeed(StringUtils.isBlank(globalTransitionalSpeed) ? null : Double.parseDouble(globalTransitionalSpeed)); + String globalRTHHeight = filedValid(missionConfig.elementText("globalRTHHeight"), accurateImport); + dto.setGlobalRthHeight(StringUtils.isBlank(globalRTHHeight) ? null : Double.parseDouble(globalRTHHeight)); + /*Element droneInfo = missionConfig.element("droneInfo"); + if (droneInfo == null) { + if (!accurateImport) { + throw new RenException("飞行器机型信息不存在,无法解析"); + } + } else { + String droneEnumValue = filedValid(droneInfo.elementText("droneEnumValue"), accurateImport); + String droneSubEnumValue = filedValid(droneInfo.elementText("droneSubEnumValue"), accurateImport); + }*/ + /*Element payloadInfo = missionConfig.element("payloadInfo"); + if (payloadInfo == null) { + if (!accurateImport) { + throw new RenException("负载机型信息不存在,无法解析"); + } + } else { + String payloadEnumValue = filedValid(payloadInfo.elementText("payloadEnumValue"), accurateImport); + String cameraName = CameraModel.getNameByType(payloadEnumValue); + dto.setCameraType(cameraName); + }*/ + + Element folder = doc.element("Folder"); + if (folder == null) { + if (!accurateImport) { + throw new RenException("航线信息不存在,无法解析"); + } + } else { + String autoFlightSpeed = filedValid(folder.elementText("autoFlightSpeed"), accurateImport); + dto.setFlightSpeed(StringUtils.isBlank(autoFlightSpeed) ? null : Double.parseDouble(autoFlightSpeed)); + + List placemarks = folder.elements("Placemark"); + if (CollectionUtil.isEmpty(placemarks)) { + if (!accurateImport) { + throw new RenException("航点信息不存在,无法解析"); + } + } else { + List waypointList = dto.getRouteWaypointList(); + if (waypointList == null || waypointList.isEmpty()) { + return; + } + + for (int i = 0; i < waypointList.size(); i++) { + RouteWaypointDTO waypoint = waypointList.get(i); + Element placemark = placemarks.get(i); + Element point = placemark.element("Point"); + if (point == null) { + if (!accurateImport) { + throw new RenException("航点经纬度不存在,无法解析"); + } + } else { + String coordinates = point.elementText("coordinates"); + String[] split = coordinates.split(","); + waypoint.setLongitude(Double.valueOf(split[0])); + waypoint.setLatitude(Double.valueOf(split[1])); + } + String index = filedValid(placemark.elementText("index"), accurateImport); + waypoint.setWaypointSort(StringUtils.isBlank(index) ? null : Integer.valueOf(index)); + String executeHeight = filedValid(placemark.elementText("executeHeight"), accurateImport); + waypoint.setFlightHeight(StringUtils.isBlank(executeHeight) ? null : Double.parseDouble(executeHeight)); + String waypointSpeed = filedValid(placemark.elementText("waypointSpeed"), accurateImport); + waypoint.setFlightSpeed(StringUtils.isBlank(waypointSpeed) ? null : Double.parseDouble(waypointSpeed)); + Element waypointHeadingParam = placemark.element("waypointHeadingParam"); + if (waypointHeadingParam == null) { + if (!accurateImport) { + throw new RenException("偏航角模式参数不存在,无法解析"); + } + } else { + waypoint.setWaypointHeadingMode(filedValid(waypointHeadingParam.elementText("waypointHeadingMode"), accurateImport)); + String waypointHeadingAngle = filedValid(waypointHeadingParam.elementText("waypointHeadingAngle"), accurateImport); + if (StrUtil.isNotBlank(waypointHeadingAngle)) { + waypoint.setWaypointHeadingAngle(waypointHeadingAngle); + } + String waypointPoiPoint = waypointHeadingParam.elementText("waypointPoiPoint"); + if (StrUtil.isNotBlank(waypointPoiPoint)) { + waypoint.setWaypointPoiPoint(waypointPoiPoint); + } + String waypointHeadingPathMode = filedValid(waypointHeadingParam.elementText("waypointHeadingPathMode"), accurateImport); + waypoint.setWaypointHeadingPathMode(StringUtils.isBlank(waypointHeadingPathMode) ? "" : waypointHeadingPathMode); + } + + Element waypointTurnParam = placemark.element("waypointTurnParam"); + if (waypointTurnParam == null) { + if (!accurateImport) { + throw new RenException("航点转弯模式不存在,无法解析"); + } + } else { + waypoint.setWaypointTruningMode(filedValid(waypointTurnParam.elementText("waypointTurnMode"), accurateImport)); + String waypointTurnDampingDist = filedValid(waypointTurnParam.elementText("waypointTurnDampingDist"), accurateImport); + if (StrUtil.isNotBlank(waypointTurnDampingDist)) { + waypoint.setWaypointTurnDampingDist(waypointTurnDampingDist); + } + } + + List actionList = waypoint.getWaypointActionList(); + if (CollectionUtil.isEmpty(actionList)) { + actionList = new ArrayList<>(); + } + List actionGroups = placemark.elements("actionGroup"); + for (Element actionGroup : actionGroups) { + Element actionTrigger = actionGroup.element("actionTrigger"); + String actionTriggerType = actionTrigger.elementText("actionTriggerType"); + if ("multipleTiming".equals(actionTriggerType) || "multipleDistance".equals(actionTriggerType)) { + String actionGroupId = actionGroup.elementText("actionGroupId"); + String actionTriggerParam = actionTrigger.elementText("actionTriggerParam"); + WaypointActionDTO action = new WaypointActionDTO(); + action.setActionType(actionTriggerType); + action.setActionValue(actionTriggerParam); + action.setActionSort(Integer.parseInt(actionGroupId)); + actionList.add(action); + + String actionGroupEndIndex = actionGroup.elementText("actionGroupEndIndex"); + RouteWaypointDTO uavWaypointDTO = waypointList.get(Integer.parseInt(actionGroupEndIndex)); + List actionList1 = uavWaypointDTO.getWaypointActionList(); + if (CollectionUtil.isEmpty(actionList1)) { + actionList1 = new ArrayList<>(); + } + WaypointActionDTO action2 = new WaypointActionDTO(); + action2.setActionType(BusinessConstant.FINISH_TAKE_PHOTO_FLAG); + action2.setActionValue(actionGroupEndIndex); + action.setActionSort(Integer.parseInt(actionGroupId)); + actionList1.add(action2); + uavWaypointDTO.setWaypointActionList(actionList1); + } else { + List actions = actionGroup.elements("action"); + for (Element act : actions) { + WaypointActionDTO action = new WaypointActionDTO(); + String actionActuatorFunc = act.elementText("actionActuatorFunc"); + Element actionActuatorFuncParam = act.element("actionActuatorFuncParam"); + if ("gimbalRotate".equals(actionActuatorFunc)) { + String gimbalYawRotateEnable = actionActuatorFuncParam.elementText("gimbalYawRotateEnable"); + if ("1".equals(gimbalYawRotateEnable)) { + action.setActionType(actionActuatorFunc + "_yaw"); + } else { + action.setActionType(actionActuatorFunc); + } + } else { + action.setActionType(actionActuatorFunc); + } + SetActionParams.getAction(action, actionActuatorFuncParam); + actionList.add(action); + } + } + } + waypoint.setWaypointActionList(actionList); + } + } + } + } + + //赋值器 + public static String filedValid(String value, Boolean accurateImport) { + if (StringUtils.isBlank(value)) { + if (accurateImport) { + return null; + } else { + throw new RenException("字段缺失,无法解析"); + } + } else { + return value; + } + } +} diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/RouteController.java b/admin/src/main/java/com/multictrl/modules/business/controller/RouteController.java index 64f7ceb..bad2239 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/RouteController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/RouteController.java @@ -11,6 +11,7 @@ import com.multictrl.common.validator.group.AddGroup; import com.multictrl.common.validator.group.UpdateGroup; import com.multictrl.modules.business.dto.RouteAiDTO; import com.multictrl.modules.business.dto.RouteDTO; +import com.multictrl.modules.business.dto.UploadRouteDTO; import com.multictrl.modules.business.service.RouteService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -116,4 +117,21 @@ public class RouteController { return new Result<>(); } + + @PostMapping("/upload") + @Operation(summary = "导入航线") + @LogOperation("导入航线") + @Parameters({ + @Parameter(name = "dockSn", description = "机库SN"), + @Parameter(name = "accurateImport", description = "是否精准导入"), + @Parameter(name = "globalRthHeight", description = "返航高度"), + @Parameter(name = "file", description = "航线文件") + }) + @RequiresPermissions("bus:route:upload") + public Result upload(UploadRouteDTO dto) { + ValidatorUtils.validateEntity(dto); + routeService.upload(dto); + + return new Result<>(); + } } diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/UploadRouteDTO.java b/admin/src/main/java/com/multictrl/modules/business/dto/UploadRouteDTO.java new file mode 100644 index 0000000..41cd11c --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/dto/UploadRouteDTO.java @@ -0,0 +1,49 @@ +package com.multictrl.modules.business.dto; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.multictrl.common.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Objects; + +/** + * 导入航线参数 + * + * @author Sdy + * @since 1.0.0 2026/6/18 + */ +@Data +@Schema(name = "导入航线参数") +public class UploadRouteDTO { + + @NotBlank(message = "机库SN不能为空") +// @Schema(description = "机库SN") + private String dockSn; + + @NotNull(message = "是否精准导入不能为空") +// @Schema(description = "是否精准导入 true false") + private Boolean accurateImport; + + // @Schema(description = "返航高度") + private Double globalRthHeight; + + @NotNull(message = "航线文件不能为空") +// @Schema(description = "航线文件") + private MultipartFile file; + + @JsonIgnore + @Schema(hidden = true) + @AssertTrue(message = "返航高度不能为空") + public boolean isGlobalRthHeightValid() { + if (accurateImport) { + return globalRthHeight != null; + } + return true; + } +} diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/WaypointActionDTO.java b/admin/src/main/java/com/multictrl/modules/business/dto/WaypointActionDTO.java index 1e39243..b47812a 100644 --- a/admin/src/main/java/com/multictrl/modules/business/dto/WaypointActionDTO.java +++ b/admin/src/main/java/com/multictrl/modules/business/dto/WaypointActionDTO.java @@ -28,6 +28,9 @@ public class WaypointActionDTO implements Serializable { @Schema(description = "动作值") private String actionValue; + @Schema(description = "动作顺序") + private Integer actionSort; + @Schema(description = "航点顺序", hidden = true) // @JsonIgnore private String waypointOrder; diff --git a/admin/src/main/java/com/multictrl/modules/business/service/RouteService.java b/admin/src/main/java/com/multictrl/modules/business/service/RouteService.java index 077e67d..00518dc 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/RouteService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/RouteService.java @@ -4,6 +4,7 @@ import com.multictrl.common.page.PageData; import com.multictrl.common.service.CrudService; import com.multictrl.modules.business.dto.RouteAiDTO; import com.multictrl.modules.business.dto.RouteDTO; +import com.multictrl.modules.business.dto.UploadRouteDTO; import com.multictrl.modules.business.entity.RouteEntity; import java.util.Map; @@ -36,4 +37,7 @@ public interface RouteService extends CrudService { //删除AI任务 void deleteRouteAi(Long routeId); + + //上传航线 + void upload(UploadRouteDTO dto); } \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/RouteServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/RouteServiceImpl.java index 81f3388..9d30174 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/RouteServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/RouteServiceImpl.java @@ -1,24 +1,26 @@ package com.multictrl.modules.business.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import cn.hutool.core.util.StrUtil; import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.exception.ErrorCode; +import com.multictrl.common.exception.ExceptionUtils; +import com.multictrl.common.exception.RenException; import com.multictrl.common.page.PageData; import com.multictrl.common.service.impl.CrudServiceImpl; import com.multictrl.common.utils.ConvertUtils; +import com.multictrl.common.utils.JsonUtils; import com.multictrl.common.utils.kmz.KmzUtils; +import com.multictrl.common.utils.kmz.WpmlKmlParser; import com.multictrl.common.validator.AssertUtils; import com.multictrl.common.validator.ValidatorUtils; import com.multictrl.modules.business.dao.RouteAiDao; import com.multictrl.modules.business.dao.RouteDao; import com.multictrl.modules.business.dao.RouteWaypointDao; import com.multictrl.modules.business.dao.WaypointActionDao; -import com.multictrl.modules.business.dto.RouteAiDTO; -import com.multictrl.modules.business.dto.RouteDTO; -import com.multictrl.modules.business.dto.RouteWaypointDTO; -import com.multictrl.modules.business.dto.WaypointActionDTO; +import com.multictrl.modules.business.dto.*; import com.multictrl.modules.business.entity.RouteAiEntity; import com.multictrl.modules.business.entity.RouteEntity; import com.multictrl.modules.business.entity.RouteWaypointEntity; @@ -27,8 +29,10 @@ import com.multictrl.modules.business.service.MinioService; import com.multictrl.modules.business.service.RouteService; import com.multictrl.modules.security.user.SecurityUser; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.ArrayList; @@ -41,8 +45,9 @@ import java.util.Map; * @author Sdy * @since 1.0.0 2026-04-20 */ -@RequiredArgsConstructor +@Slf4j @Service +@RequiredArgsConstructor public class RouteServiceImpl extends CrudServiceImpl implements RouteService { private final RouteWaypointDao routeWaypointDao; private final WaypointActionDao waypointActionDao; @@ -211,6 +216,60 @@ public class RouteServiceImpl extends CrudServiceImpl().eq("route_id", routeId)); } + @Override + public void upload(UploadRouteDTO dto) { + MultipartFile file = dto.getFile(); + String originalFilename = file.getOriginalFilename(); + String name = FileUtil.getPrefix(originalFilename); + String suffix = FileUtil.getSuffix(originalFilename); + if (!"kmz".equals(suffix)) { + throw new RenException(ErrorCode.FILE_FORMAT_ERROR, "kmz"); + } + //判断航线名称是否已存在 + checkRouteExist(null, name); + + RouteDTO routeDTO = null; + try { + Map kmzInfo = KmzUtils.extractKmzAsBytes(file.getInputStream()); + byte[] template = kmzInfo.get("template.kml"); + routeDTO = WpmlKmlParser.parseKmlFile(template, dto.getAccurateImport()); + byte[] waylines = kmzInfo.get("waylines.wpml"); + WpmlKmlParser.parseWpmlFile(waylines, routeDTO, dto.getAccurateImport()); + + log.debug("解析结果:{}", JsonUtils.toJsonString(dto)); + } catch (Exception e) { + log.error("解析结果失败:{}", ExceptionUtils.getErrorStackTrace(e)); +// throw new RenException(ErrorCode.FILE_PARSER_ERROR); + throw new RenException(e.getMessage()); + } + + routeDTO.setRouteName(name); + routeDTO.setGlobalRthHeight(dto.getGlobalRthHeight()); + String path = dto.getDockSn() + "/" + originalFilename; + try { + minioService.uploadFile(file.getInputStream(), BusinessConstant.ROUTE_KMZ_BUCKET, path); + } catch (IOException e) { + log.error("文件上传失败:{}", ExceptionUtils.getErrorStackTrace(e)); + throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR); + } + routeDTO.setKmzUrl(BusinessConstant.ROUTE_KMZ_BUCKET + "/" + path); + routeDTO.setIsAccurateImport(dto.getAccurateImport()); + routeDTO.setDockSn(dto.getDockSn()); + + //航点动作处理 + List routeWaypointList = routeDTO.getRouteWaypointList(); + handleWaypointAction(routeWaypointList); + + //保存信息 + RouteEntity routeEntity = ConvertUtils.sourceToTarget(routeDTO, currentModelClass()); + routeEntity.setDeptId(SecurityUser.getDeptId()); + routeEntity.setWaypointNum(routeWaypointList.size()); + baseDao.insert(routeEntity); + + //保存航点信息 + saveWaypoint(routeEntity.getId(), routeWaypointList); + } + //判断航线是否存在 private void checkRouteExist(Long id, String routeName) { RouteEntity uavRouteEntity = baseDao.selectOne(new QueryWrapper()