From 89df7e6c0e6dd9519e9aecca54b74347f50a9c7e Mon Sep 17 00:00:00 2001 From: ZZX9599 <536509593@qq.com> Date: Tue, 16 Sep 2025 11:41:45 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 + src/main/java/com/yj/earth/ServerApp.java | 1 + .../com/yj/earth/annotation/CheckAuth.java | 12 + .../java/com/yj/earth/aspect/AuthAspect.java | 99 ++++ .../java/com/yj/earth/auth/AuthGenerator.java | 1 - .../java/com/yj/earth/auth/AuthValidator.java | 3 +- .../controller/FileInfoController.java | 151 ++++-- .../controller/GraphHopperController.java | 22 +- .../business/controller/SourceController.java | 114 ++++- .../TacticalCalculationController.java | 436 ++++++++++++++++++ .../common/service/ServerInitService.java | 107 ++++- .../common/service/SourceDataGenerator.java | 4 +- .../com/yj/earth/common/util/CodeUtil.java | 2 +- .../earth/common/util/ServerUniqueIdUtil.java | 3 +- .../yj/earth/datasource/DatabaseManager.java | 2 + src/main/java/com/yj/earth/design/Model.java | 33 ++ .../java/com/yj/earth/design/ModelType.java | 20 + .../dto/source/UploadLocationImageDto.java | 16 + .../java/com/yj/earth/params/DiffuseScan.java | 2 +- src/main/java/com/yj/earth/params/Layer.java | 3 + .../java/com/yj/earth/params/RadarScan.java | 2 +- src/main/java/com/yj/earth/params/Roam.java | 35 ++ 22 files changed, 1011 insertions(+), 64 deletions(-) create mode 100644 src/main/java/com/yj/earth/annotation/CheckAuth.java create mode 100644 src/main/java/com/yj/earth/aspect/AuthAspect.java create mode 100644 src/main/java/com/yj/earth/business/controller/TacticalCalculationController.java create mode 100644 src/main/java/com/yj/earth/design/Model.java create mode 100644 src/main/java/com/yj/earth/design/ModelType.java create mode 100644 src/main/java/com/yj/earth/dto/source/UploadLocationImageDto.java create mode 100644 src/main/java/com/yj/earth/params/Roam.java diff --git a/pom.xml b/pom.xml index 4d09feb..b921ae9 100644 --- a/pom.xml +++ b/pom.xml @@ -149,6 +149,13 @@ aspectjweaver + + + com.drewnoakes + metadata-extractor + 2.18.0 + + org.apache.httpcomponents diff --git a/src/main/java/com/yj/earth/ServerApp.java b/src/main/java/com/yj/earth/ServerApp.java index 241333d..5a83fb4 100644 --- a/src/main/java/com/yj/earth/ServerApp.java +++ b/src/main/java/com/yj/earth/ServerApp.java @@ -49,6 +49,7 @@ public class ServerApp implements CommandLineRunner { serverInitService.init(); // 检查默认数据 serverInitService.checkDefaultData(); + // 打印项目文档地址 log.info("项目文档地址: {}", "http://" + serverConfig.getHost() + ":" + serverConfig.getPort() + "/doc.html"); } } diff --git a/src/main/java/com/yj/earth/annotation/CheckAuth.java b/src/main/java/com/yj/earth/annotation/CheckAuth.java new file mode 100644 index 0000000..cdba1a2 --- /dev/null +++ b/src/main/java/com/yj/earth/annotation/CheckAuth.java @@ -0,0 +1,12 @@ +package com.yj.earth.annotation; + +import java.lang.annotation.*; + +/** + * 授权验证注解 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface CheckAuth { +} diff --git a/src/main/java/com/yj/earth/aspect/AuthAspect.java b/src/main/java/com/yj/earth/aspect/AuthAspect.java new file mode 100644 index 0000000..d459f6f --- /dev/null +++ b/src/main/java/com/yj/earth/aspect/AuthAspect.java @@ -0,0 +1,99 @@ +package com.yj.earth.aspect; + +import com.yj.earth.auth.AuthInfo; +import com.yj.earth.auth.AuthValidator; +import com.yj.earth.common.util.ApiResponse; +import com.yj.earth.common.util.ServerUniqueIdUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 授权验证切面 + * 实现授权验证注解的功能 + */ +@Aspect +@Component +public class AuthAspect { + + // 授权文件路径 + private static final String LICENSE_FILE_PATH = "license/yjearth.lic"; + + @Pointcut("@annotation(com.yj.earth.annotation.CheckAuth)") + public void authPointCut() { + } + + @Around("authPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + // 读取授权文件内容 + String authString = readLicenseFile(); + + // 检查授权文件是否存在 + if (authString == null || authString.trim().isEmpty()) { + return ApiResponse.failure("未导入授权"); + } + + // 获取当前服务器硬件信息 + String currentServerHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId(); + + // 验证授权是否有效 + boolean isValid = AuthValidator.validateAuth(authString, currentServerHardwareMd5); + if (!isValid) { + // 进一步判断是未授权还是已过期 + try { + AuthInfo authInfo = AuthValidator.getAuthInfo(authString); + // 检查是否过期 + if (new java.util.Date().after(authInfo.getExpireTime())) { + return ApiResponse.failure("授权已过期"); + } else { + return ApiResponse.failure("未授权"); + } + } catch (Exception e) { + return ApiResponse.failure("未授权"); + } + } + + // 授权有效,继续执行原方法 + return point.proceed(); + } + + /** + * 读取授权文件内容 + * @return 授权文件内容,如果文件不存在或读取失败则返回null + */ + private String readLicenseFile() { + try { + // 尝试从类路径下读取 + ClassPathResource resource = new ClassPathResource(LICENSE_FILE_PATH); + if (resource.exists()) { + try (FileInputStream inputStream = new FileInputStream(resource.getFile())) { + byte[] bytes = new byte[inputStream.available()]; + inputStream.read(bytes); + return new String(bytes, StandardCharsets.UTF_8).trim(); + } + } + + // 尝试从文件系统读取 + File file = new File(LICENSE_FILE_PATH); + if (file.exists() && file.isFile()) { + try (FileInputStream inputStream = new FileInputStream(file)) { + byte[] bytes = new byte[inputStream.available()]; + inputStream.read(bytes); + return new String(bytes, StandardCharsets.UTF_8).trim(); + } + } + + return null; + } catch (IOException e) { + return null; + } + } +} diff --git a/src/main/java/com/yj/earth/auth/AuthGenerator.java b/src/main/java/com/yj/earth/auth/AuthGenerator.java index b77d911..5539d81 100644 --- a/src/main/java/com/yj/earth/auth/AuthGenerator.java +++ b/src/main/java/com/yj/earth/auth/AuthGenerator.java @@ -2,7 +2,6 @@ package com.yj.earth.auth; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.yj.earth.common.util.ServerUniqueIdUtil; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; diff --git a/src/main/java/com/yj/earth/auth/AuthValidator.java b/src/main/java/com/yj/earth/auth/AuthValidator.java index ebbcfcf..2ea5af8 100644 --- a/src/main/java/com/yj/earth/auth/AuthValidator.java +++ b/src/main/java/com/yj/earth/auth/AuthValidator.java @@ -1,6 +1,5 @@ package com.yj.earth.auth; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.yj.earth.common.util.ServerUniqueIdUtil; @@ -100,7 +99,7 @@ public class AuthValidator { public static void main(String[] args) { String serverUniqueId = ServerUniqueIdUtil.getServerUniqueId(); // 生成授权 - String authString = AuthGenerator.generateAuth("标准版", 1000, 30, serverUniqueId); + String authString = AuthGenerator.generateAuth("标准版", 1000, 30, "35A0DF1D05AEAE77E1E2715CC36A7368"); System.out.println("授权字符串:" + authString); // 验证授权 boolean isValid = AuthValidator.validateAuth(authString, serverUniqueId); diff --git a/src/main/java/com/yj/earth/business/controller/FileInfoController.java b/src/main/java/com/yj/earth/business/controller/FileInfoController.java index fcf9a40..a454816 100644 --- a/src/main/java/com/yj/earth/business/controller/FileInfoController.java +++ b/src/main/java/com/yj/earth/business/controller/FileInfoController.java @@ -2,6 +2,17 @@ package com.yj.earth.business.controller; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; +import com.drew.imaging.ImageMetadataReader; +import com.drew.imaging.ImageProcessingException; +import com.drew.metadata.Directory; +import com.drew.metadata.Metadata; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + import cn.hutool.crypto.digest.DigestUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -9,6 +20,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yj.earth.business.domain.FileInfo; import com.yj.earth.business.service.FileInfoService; import com.yj.earth.common.util.ApiResponse; +import com.yj.earth.common.util.JsonMapConverter; import com.yj.earth.vo.FileInfoVo; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -17,17 +29,16 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; +import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; +import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; @Tag(name = "文件数据管理") @@ -168,11 +179,7 @@ public class FileInfoController { @Operation(summary = "文件预览") @GetMapping("/preview/{id}") - public void previewFile( - @Parameter(description = "文件ID", required = true) - @PathVariable String id, - HttpServletResponse response) throws IOException { - + public void previewFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException { // 根据ID查询文件信息 FileInfo fileInfo = fileInfoService.getById(id); if (fileInfo == null) { @@ -200,42 +207,100 @@ public class FileInfoController { } } - @Operation(summary = "文件列表") - @GetMapping("/list") - public ApiResponse getFileList( - @Parameter(description = "页码", required = true) Integer pageNum, - @Parameter(description = "每页条数", required = true) Integer pageSize, - @Parameter(description = "文件名称") String fileName) { - - // 创建分页对象 - Page page = new Page<>(pageNum, pageSize); - // 构建查询条件 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - if (fileName != null && !fileName.isEmpty()) { - queryWrapper.like(FileInfo::getFileName, fileName); + public String handleLocationImageUpload(MultipartFile file) { + try { + // 校验文件是否为空 + if (file.isEmpty()) { + throw new IllegalArgumentException("上传文件不能为空"); + } + // 获取文件基本信息 + String originalFilename = file.getOriginalFilename(); + String fileSuffix = FileUtil.extName(originalFilename); + String contentType = file.getContentType(); + // 验证是否为图片文件 + if (contentType == null || !contentType.startsWith("image/")) { + throw new IllegalArgumentException("请上传图片文件"); + } + // 获取完整的上传目录路径 + Path fullUploadPath = Paths.get(getFullUploadPath()); + // 生成唯一文件名 + String uniqueFileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileSuffix; + // 创建文件存储目录 + Files.createDirectories(fullUploadPath); + // 构建完整文件路径并保存文件 + Path destFilePath = fullUploadPath.resolve(uniqueFileName); + // 先将文件保存到目标位置 + file.transferTo(destFilePath); + // 计算文件MD5(使用已保存的文件) + String fileMd5 = calculateFileMd5(destFilePath.toFile()); + // 检查文件是否已存在 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(FileInfo::getFileName, originalFilename) + .eq(FileInfo::getFileMd5, fileMd5); + if (fileInfoService.count(queryWrapper) > 0) { + throw new IllegalStateException("已存在相同的图片文件"); + } + // 提取图片元数据(使用已保存的文件,避免使用临时文件) + Map metadata; + try (InputStream is = Files.newInputStream(destFilePath)) { + metadata = extractImageMetadata(is); + } + // 保存文件信息到数据库 + FileInfo fileInfo = new FileInfo(); + fileInfo.setFileName(originalFilename); + fileInfo.setFileSuffix(fileSuffix); + fileInfo.setContentType(contentType); + fileInfo.setFileSize(file.getSize()); + fileInfo.setFilePath(uniqueFileName); + fileInfo.setFileMd5(fileMd5); + fileInfoService.save(fileInfo); + // 构建并返回结果 + Map result = new HashMap<>(); + result.put("previewUrl", "/fileInfo/preview/" + fileInfo.getId()); + result.put("downloadUrl", "/fileInfo/download/" + fileInfo.getId()); + result.put("metadata", metadata); + return JsonMapConverter.mapToJson(result); + } catch (IOException e) { + throw new RuntimeException("文件上传失败: " + e.getMessage(), e); } - // 按创建时间倒序排列、最新上传的文件在前 - queryWrapper.orderByDesc(FileInfo::getCreatedAt); + } - // 执行分页查询 - IPage fileInfoPage = fileInfoService.page(page, queryWrapper); + /** + * 计算文件的 MD5 + */ + private String calculateFileMd5(File file) throws IOException { + try (InputStream is = new FileInputStream(file)) { + return DigestUtils.md5DigestAsHex(is); + } + } - // 转换为VO对象并设置URL - List records = fileInfoPage.getRecords().stream().map(fileInfo -> { - FileInfoVo vo = new FileInfoVo(); - BeanUtils.copyProperties(fileInfo, vo); - vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId()); - vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId()); - return vo; - }).collect(Collectors.toList()); + /** + * 提取图片的EXIF元数据(包括定位信息) + */ + private Map extractImageMetadata(InputStream inputStream) { + try { + Map result = new HashMap<>(); + Metadata metadata = ImageMetadataReader.readMetadata(inputStream); - // 构建分页结果 - Page resultPage = new Page<>(); - resultPage.setRecords(records); - resultPage.setTotal(fileInfoPage.getTotal()); - resultPage.setSize(fileInfoPage.getSize()); - resultPage.setCurrent(fileInfoPage.getCurrent()); - resultPage.setPages(fileInfoPage.getPages()); - return ApiResponse.success(resultPage); + // 遍历所有元数据目录 + for (Directory directory : metadata.getDirectories()) { + String directoryName = directory.getName(); + Map directoryTags = new HashMap<>(); + + // 提取当前目录下的所有标签 + for (com.drew.metadata.Tag tag : directory.getTags()) { + directoryTags.put(tag.getTagName(), tag.getDescription()); + } + + // 存储当前目录的所有标签 + if (!directoryTags.isEmpty()) { + result.put(directoryName, directoryTags); + } + } + + return result; + } catch (ImageProcessingException | IOException e) { + return Collections.emptyMap(); + } } } diff --git a/src/main/java/com/yj/earth/business/controller/GraphHopperController.java b/src/main/java/com/yj/earth/business/controller/GraphHopperController.java index f9d96ed..a6d7924 100644 --- a/src/main/java/com/yj/earth/business/controller/GraphHopperController.java +++ b/src/main/java/com/yj/earth/business/controller/GraphHopperController.java @@ -8,6 +8,7 @@ import com.graphhopper.GraphHopper; import com.graphhopper.ResponsePath; import com.graphhopper.config.Profile; import com.graphhopper.util.shapes.GHPoint; +import com.yj.earth.annotation.CheckAuth; import com.yj.earth.business.domain.FileInfo; import com.yj.earth.business.service.FileInfoService; import com.yj.earth.common.config.GraphHopperProperties; @@ -51,6 +52,7 @@ public class GraphHopperController { private final AtomicBoolean isLoaded = new AtomicBoolean(false); @Operation(summary = "获取地图列表") + @CheckAuth @GetMapping("/list") public ApiResponse list() { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -60,6 +62,7 @@ public class GraphHopperController { @Operation(summary = "加载地图数据") @PostMapping("/loadMap") + @CheckAuth public ApiResponse loadMap(@Parameter(description = "文件ID") @RequestParam String fileId) { // 参数校验 if (fileId == null) { @@ -118,6 +121,7 @@ public class GraphHopperController { @Operation(summary = "路径规划") @PostMapping("/route") + @CheckAuth public ApiResponse calculateRoute(@RequestBody RouteRequest request) { // 校验地图是否加载完成 + 实例是否可用 if (!isLoaded.get() || currentHopper == null) { @@ -145,26 +149,36 @@ public class GraphHopperController { // 处理错误 if (response.hasErrors()) { - return ApiResponse.failure("路径计算失败: " + response.getErrors().toString()); - } + // 检查是否有超出范围的错误 + boolean hasOutOfBoundsError = response.getErrors().stream() + .anyMatch(e -> e instanceof com.graphhopper.util.exceptions.PointOutOfBoundsException); + if (hasOutOfBoundsError) { + // 返回超出范围的特定格式响应 + return ApiResponse.failure("路径超出地图范围"); + } else { + return ApiResponse.failure("路径计算失败: " + response.getErrors().toString()); + } + } // 解析结果 ResponsePath bestPath = response.getBest(); List pathPoints = new ArrayList<>(); bestPath.getPoints().forEach(ghPoint -> pathPoints.add(new Point(ghPoint.getLat(), ghPoint.getLon())) ); - // 封装返回 RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints); return ApiResponse.success(routeResponse); - + } catch (com.graphhopper.util.exceptions.PointOutOfBoundsException e) { + // 捕获单点超出范围的异常 + return ApiResponse.failure("路径超出地图范围"); } catch (Exception e) { return ApiResponse.failure("路径计算异常: " + e.getMessage()); } } @Operation(summary = "获取交通方式") + @CheckAuth @PostMapping("/profiles") public ApiResponse profiles() { return ApiResponse.success(graphHopperProperties.getProfiles()); diff --git a/src/main/java/com/yj/earth/business/controller/SourceController.java b/src/main/java/com/yj/earth/business/controller/SourceController.java index 25eeb92..b436d84 100644 --- a/src/main/java/com/yj/earth/business/controller/SourceController.java +++ b/src/main/java/com/yj/earth/business/controller/SourceController.java @@ -2,26 +2,40 @@ package com.yj.earth.business.controller; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.IdUtil; +import cn.hutool.crypto.digest.DigestUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.drew.imaging.ImageMetadataReader; +import com.drew.imaging.ImageProcessingException; +import com.drew.metadata.Directory; +import com.drew.metadata.Metadata; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.yj.earth.annotation.CheckAuth; +import com.yj.earth.business.domain.FileInfo; +import com.yj.earth.business.domain.Role; import com.yj.earth.business.domain.Source; -import com.yj.earth.business.service.RoleSourceService; -import com.yj.earth.business.service.SourceService; -import com.yj.earth.business.service.UserService; +import com.yj.earth.business.service.*; import com.yj.earth.common.service.SourceDataGenerator; import com.yj.earth.common.service.SourceParamsValidator; import com.yj.earth.common.util.ApiResponse; import com.yj.earth.common.util.MapUtil; import com.yj.earth.dto.source.*; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.util.*; import static com.yj.earth.common.constant.GlobalConstant.DIRECTORY; @@ -39,10 +53,19 @@ public class SourceController { @Resource private RoleSourceService roleSourceService; @Resource + private RoleService roleService; + @Resource private SourceParamsValidator sourceParamsValidator; - @Resource private SourceDataGenerator sourceDataGenerator; + @Resource + private FileInfoService fileInfoService; + + @Resource + private FileInfoController fileInfoControllerl; + + @Value("${file.upload.path}") + private String uploadPath; @PostMapping("/addDirectory") @Operation(summary = "新增目录资源") @@ -86,10 +109,10 @@ public class SourceController { source.setSourceName(sourceName); source.setParentId(addModelSourceDto.getParentId()); source.setTreeIndex(addModelSourceDto.getTreeIndex()); + source.setParams(addModelSourceDto.getParams()); source.setDetail(detail); source.setSourceType(MapUtil.getString(MapUtil.jsonToMap(detail), "fileType")); source.setIsShow(SHOW); - source.setParams(addModelSourceDto.getParams()); sourceService.save(source); // 添加资源到该用户的角色下 roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); @@ -196,6 +219,30 @@ public class SourceController { return ApiResponse.success(null); } + @PostMapping("/uploadLocationImage") + @Operation(summary = "新增定位图片") + public ApiResponse uploadLocationImage(@RequestParam("ids") @Parameter(description = "上传定位图片ID列表") List ids, + @RequestParam(value = "parentId", required = false) @Parameter(description = "父节点ID") String parentId, + @RequestParam(value = "treeIndex", required = false) @Parameter(description = "树状索引") Integer treeIndex, + @RequestParam("files") @Parameter(description = "带有定位的图片文件", required = true) MultipartFile[] files) { + for (int i = 0; i < files.length; i++) { + String detail = fileInfoControllerl.handleLocationImageUpload(files[i]); + // 构建并保存资源对象 + Source source = new Source(); + source.setId(ids.get(i)); + source.setSourceName(files[i].getOriginalFilename()); + source.setParentId(parentId); + source.setSourceType("locationImage"); + source.setTreeIndex(treeIndex); + source.setDetail(detail); + source.setIsShow(SHOW); + sourceService.save(source); + // 添加资源到该用户的角色下 + roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); + } + return ApiResponse.success(null); + } + @GetMapping("/type") @Operation(summary = "获取已有类型") public ApiResponse getSupportedSourceTypes() { @@ -208,4 +255,61 @@ public class SourceController { public String getExampleData(String sourceType) throws JsonProcessingException { return sourceDataGenerator.generateDefaultJson(sourceType); } + + @Operation(summary = "设置默认数据") + @GetMapping("/default") + public ApiResponse getDefaultData() { + log.info("开始初始化默认资源数据"); + String userId = StpUtil.getLoginIdAsString(); + String roleId = userService.getById(userId).getRoleId(); + + // 创建一级目录 + createSourceIfNotExists("倾斜模型", "directory", null, 0, 1, roleId); + createSourceIfNotExists("人工模型", "directory", null, 0, 1, roleId); + createSourceIfNotExists("卫星底图", "directory", null, 0, 1, roleId); + createSourceIfNotExists("地形", "directory", null, 0, 1, roleId); + + // 创建"在线图源"目录及其子资源 + Source onlineSource = createSourceIfNotExists("在线图源", "directory", null, 0, 1, roleId); + if (onlineSource != null) { + String onlineSourceId = onlineSource.getId(); + createSourceIfNotExists("卫星图", "arcgisWximagery", onlineSourceId, 0, 1, roleId); + createSourceIfNotExists("暗黑地图", "arcgisBlueImagery", onlineSourceId, 0, 1, roleId); + createSourceIfNotExists("路网图", "gdlwImagery", onlineSourceId, 0, 1, roleId); + createSourceIfNotExists("矢量图", "gdslImagery", onlineSourceId, 0, 1, roleId); + } + + log.info("默认资源数据初始化完成"); + return ApiResponse.success(null); + } + + /** + * 检查资源是否存在、不存在则创建并关联角色资源 + */ + private Source createSourceIfNotExists(String sourceName, String sourceType, String parentId, int treeIndex, int isShow, String roleId) { + // 检查资源是否已存在(通过名称和父ID组合判断唯一性) + Source existingSource = sourceService.getOne(new LambdaQueryWrapper() + .eq(Source::getSourceName, sourceName) + .eq(parentId != null, Source::getParentId, parentId) + .isNull(parentId == null, Source::getParentId)); + if (existingSource != null) { + return existingSource; + } + // 不存在则创建新资源 + try { + Source newSource = new Source(); + newSource.setId(cn.hutool.core.lang.UUID.fastUUID().toString(true)); + newSource.setSourceName(sourceName); + newSource.setSourceType(sourceType); + newSource.setParentId(parentId); + newSource.setTreeIndex(treeIndex); + newSource.setIsShow(isShow); + sourceService.save(newSource); + // 关联角色资源 + roleSourceService.addRoleSource(roleId, newSource.getId()); + return newSource; + } catch (Exception e) { + return null; + } + } } diff --git a/src/main/java/com/yj/earth/business/controller/TacticalCalculationController.java b/src/main/java/com/yj/earth/business/controller/TacticalCalculationController.java new file mode 100644 index 0000000..aabf0be --- /dev/null +++ b/src/main/java/com/yj/earth/business/controller/TacticalCalculationController.java @@ -0,0 +1,436 @@ +package com.yj.earth.business.controller; + +import com.yj.earth.common.util.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Tag(name = "战斗计算相关") +@RestController +@RequestMapping("/api/tactical") +public class TacticalCalculationController { + + // 日期时间格式化器 + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @PostMapping("/meet") + @Operation(summary = "计算相遇结果") + public ApiResponse calculateMeet(@Valid @RequestBody MeetInputDTO input) { + MeetResultDTO result = new MeetResultDTO(); + + // 计算相遇时间(小时) + double meetTimeHours; + if (input.isSameDirection()) { + // 同向而行:时间 = 距离 / (速度差) + double speedDiff = Math.abs(input.getSpeed1() - input.getSpeed2()); + if (speedDiff <= 0) { + throw new IllegalArgumentException("同向而行时,速度不能相等或后方速度小于前方"); + } + meetTimeHours = input.getInitialDistance() / speedDiff; + } else { + // 相向而行:时间 = 距离 / (速度和) + meetTimeHours = input.getInitialDistance() / (input.getSpeed1() + input.getSpeed2()); + } + + // 格式化时间结果 + result.setMeetTime(formatDuration(Duration.ofHours((long) meetTimeHours) + .plusMinutes((long) ((meetTimeHours % 1) * 60)))); + + // 计算相遇点距离 + result.setMeetDistanceFrom1(input.getSpeed1() * meetTimeHours); + result.setMeetDistanceFrom2(input.getSpeed2() * meetTimeHours); + + return ApiResponse.success(result); + } + + @PostMapping("/pursuit") + @Operation(summary = "计算追击结果") + public ApiResponse calculatePursuit(@Valid @RequestBody PursuitInputDTO input) { + PursuitResultDTO result = new PursuitResultDTO(); + + // 检查速度合理性 + if (input.getPursuerSpeed() <= input.getTargetSpeed()) { + throw new IllegalArgumentException("追击速度必须大于目标速度"); + } + + // 计算追击时间(小时) + double pursuitTimeHours = input.getDistance() / (input.getPursuerSpeed() - input.getTargetSpeed()); + result.setPursuitTime(formatDuration(Duration.ofHours((long) pursuitTimeHours) + .plusMinutes((long) ((pursuitTimeHours % 1) * 60)))); + + // 计算追击距离 + result.setPursuitDistance(input.getPursuerSpeed() * pursuitTimeHours); + return ApiResponse.success(result); + } + + @PostMapping("/formation/person") + @Operation(summary = "计算人员队列长度") + public ApiResponse calculatePersonFormation(@Valid @RequestBody PersonFormationInputDTO input) { + FormationLengthResultDTO result = new FormationLengthResultDTO(); + + // 人员队列长度 = (人数 - 1) * 间距 + if (input.getPersonCount() <= 1) { + result.setTotalLength(0.0); + } else { + result.setTotalLength((input.getPersonCount() - 1) * input.getDistanceBetween()); + } + return ApiResponse.success(result); + } + + @PostMapping("/formation/vehicle") + @Operation(summary = "计算车辆队列长度") + public ApiResponse calculateVehicleFormation(@Valid @RequestBody VehicleFormationInputDTO input) { + FormationLengthResultDTO result = new FormationLengthResultDTO(); + + // 车辆队列长度 = (单辆车长 * 数量) + (间距 * (数量 - 1)) + if (input.getVehicleCount() <= 0) { + result.setTotalLength(0.0); + } else if (input.getVehicleCount() == 1) { + result.setTotalLength(input.getVehicleLength()); + } else { + result.setTotalLength((input.getVehicleLength() * input.getVehicleCount()) + + (input.getDistanceBetween() * (input.getVehicleCount() - 1))); + } + + return ApiResponse.success(result); + } + + @PostMapping("/formation/gun") + @Operation(summary = "计算火炮队列长度") + public ApiResponse calculateGunFormation(@Valid @RequestBody GunFormationInputDTO input) { + FormationLengthResultDTO result = new FormationLengthResultDTO(); + + // 火炮本身长度 + double gunTotalLength = input.getGunLength() * input.getGunCount(); + // 牵引车辆长度 + double vehicleTotalLength = input.getVehicleLength() * input.getVehicleCount(); + // 车辆间距 + double vehicleDistance = input.getVehicleDistance() * (input.getVehicleCount() - 1); + + // 总长度 + result.setTotalLength(gunTotalLength + vehicleTotalLength + vehicleDistance); + return ApiResponse.success(result); + } + + @PostMapping("/march/time") + @Operation(summary = "计算行军时间") + public ApiResponse calculateMarchTime(@Valid @RequestBody MarchTimeInputDTO input) { + MarchTimeResultDTO result = new MarchTimeResultDTO(); + + // 总行军距离 = 行军距离 + 部队长度(需要完全通过) + double totalDistance = input.getMarchDistance() + input.getFormationLength(); + + // 行军时间(小时)= 总距离 / 速度 + 等待时间 + double marchTimeHours = (totalDistance / input.getSpeed()) + input.getWaitHours(); + + // 格式化行军时间 + result.setTotalMarchTime(formatDuration(Duration.ofHours((long) marchTimeHours) + .plusMinutes((long) ((marchTimeHours % 1) * 60)))); + + // 计算出发时间(如果提供了要求抵达时间) + if (input.getRequiredArriveTime() != null) { + long hours = (long) marchTimeHours; + long minutes = (long) ((marchTimeHours - hours) * 60); + LocalDateTime departTime = input.getRequiredArriveTime() + .minusHours(hours) + .minusMinutes(minutes); + result.setDepartureTime(departTime.format(DATE_TIME_FORMATTER)); + } + + return ApiResponse.success(result); + } + + @PostMapping("/material/consumption") + @Operation(summary = "计算物资消耗") + public ApiResponse calculateMaterialConsumption(@Valid @RequestBody MaterialConsumptionInputDTO input) { + MaterialConsumptionResultDTO result = new MaterialConsumptionResultDTO(); + + switch (input.getMaterialType()) { + case AMMUNITION: + // 弹药消耗 = 单位基数 * 装备数量 * 时间 + result.setTotalConsumption(input.getBaseConsumption() * input.getEquipmentCount() * input.getHours()); + break; + case OIL: + // 油料消耗 = 单位时间耗油量 * 装备数量 * 时间 + result.setTotalConsumption(input.getBaseConsumption() * input.getEquipmentCount() * input.getHours()); + break; + case WEAPON: + // 武器需求 = 总需求 / (单装备配备数) + result.setTotalConsumption(Math.ceil(input.getTotalRequirement() / input.getBaseConsumption())); + break; + } + + return ApiResponse.success(result); + } + + @PostMapping("/unit/formation") + @Operation(summary = "计算部队编成") + public ApiResponse calculateUnitFormation(@Valid @RequestBody UnitFormationInputDTO input) { + UnitFormationResultDTO result = new UnitFormationResultDTO(); + + // 计算编成数量 + result.setFormationCount(Math.ceil(input.getTotalStrength() / input.getUnitStrength())); + + // 计算最后一个编成的兵力 + double lastFormationStrength = input.getTotalStrength() % input.getUnitStrength(); + if (lastFormationStrength <= 0) { + lastFormationStrength = input.getUnitStrength(); + } + result.setLastFormationStrength(lastFormationStrength); + return ApiResponse.success(result); + } + + @PostMapping("/special/crossRiver") + @Operation(summary = "计算渡河时间") + public ApiResponse calculateCrossRiverTime(@Valid @RequestBody CrossRiverInputDTO input) { + SpecialTimeResultDTO result = new SpecialTimeResultDTO(); + + // 计算批次数量 + double batchCount = Math.ceil(input.getPersonCount() / input.getBatchCapacity()); + + // 总时间 = 批次 * 单批次时间 + 准备时间 + double totalHours = (batchCount * input.getBatchHours()) + input.getPrepareHours(); + + // 格式化结果 + result.setTotalTime(formatDuration(Duration.ofHours((long) totalHours) + .plusMinutes((long) ((totalHours % 1) * 60)))); + + return ApiResponse.success(result); + } + + @PostMapping("/loss/rate") + @Operation(summary = "计算损失率") + public ApiResponse calculateLossRate(@Valid @RequestBody LossRateInputDTO input) { + LossRateResultDTO result = new LossRateResultDTO(); + + if (input.getTotalStrength() <= 0) { + throw new IllegalArgumentException("总兵力必须大于0"); + } + + // 计算损失率 + result.setLossRate((input.getLostStrength() / input.getTotalStrength()) * 100); + + // 计算剩余兵力 + result.setRemainingStrength(input.getTotalStrength() - input.getLostStrength()); + return ApiResponse.success(result); + } + + // 格式化Duration为"X天X时X分X秒" + private String formatDuration(Duration duration) { + long days = duration.toDays(); + long hours = duration.toHours() % 24; + long minutes = duration.toMinutes() % 60; + long seconds = duration.getSeconds() % 60; + + return String.format("%d天%d时%d分%d秒", days, hours, minutes, seconds); + } + + // 解析时间字符串为Duration + private Duration parseTimeString(String timeStr) { + Pattern pattern = Pattern.compile("(?\\d+)天(?\\d+)时(?\\d+)分(?\\d+)秒"); + Matcher matcher = pattern.matcher(timeStr); + + if (matcher.matches()) { + int days = Integer.parseInt(matcher.group("d")); + int hours = Integer.parseInt(matcher.group("h")); + int minutes = Integer.parseInt(matcher.group("m")); + int seconds = Integer.parseInt(matcher.group("s")); + + return Duration.ofDays(days) + .plusHours(hours) + .plusMinutes(minutes) + .plusSeconds(seconds); + } + return Duration.ZERO; + } + + + @Data + public static class MeetInputDTO { + @Schema(description = "初始距离") + private double initialDistance; + @Schema(description = "速度1") + private double speed1; + @Schema(description = "速度2") + private double speed2; + @Schema(description = "是否同向") + private boolean isSameDirection; + } + + @Data + public static class MeetResultDTO { + @Schema(description = "相遇时间") + private String meetTime; + @Schema(description = "距离1的距离") + private double meetDistanceFrom1; + @Schema(description = "距离2的距离") + private double meetDistanceFrom2; + } + + @Data + public static class PursuitInputDTO { + @Schema(description = "初始距离") + private double distance; + @Schema(description = "追击者速度") + private double pursuerSpeed; + @Schema(description = "目标速度") + private double targetSpeed; + } + + @Data + public static class PursuitResultDTO { + @Schema(description = "追击时间") + private String pursuitTime; + @Schema(description = "追击距离") + private double pursuitDistance; + } + + @Data + public static class PersonFormationInputDTO { + @Schema(description = "人数") + private int personCount; + @Schema(description = "人间距") + private double distanceBetween; + } + + @Data + public static class VehicleFormationInputDTO { + @Schema(description = "车辆数量") + private int vehicleCount; + @Schema(description = "车辆长") + private double vehicleLength; + @Schema(description = "车辆距") + private double distanceBetween; + } + + @Data + public static class GunFormationInputDTO { + @Schema(description = "火炮数量") + private int gunCount; + @Schema(description = "单门炮长") + private double gunLength; + @Schema(description = "牵引车辆数量") + private int vehicleCount; + @Schema(description = "单辆车长") + private double vehicleLength; + @Schema(description = "车距") + private double vehicleDistance; + } + + @Data + public static class FormationLengthResultDTO { + @Schema(description = "总长度") + private double totalLength; + } + + @Data + public static class MarchTimeInputDTO { + @Schema(description = "行军距离") + private double marchDistance; + @Schema(description = "部队长度") + private double formationLength; + @Schema(description = "速度") + private double speed; + @Schema(description = "等待时间(小时)") + private double waitHours; + @Schema(description = "要求抵达时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime requiredArriveTime; + } + + @Data + public static class MarchTimeResultDTO { + @Schema(description = "总行军时间") + private String totalMarchTime; + @Schema(description = "出发时间") + private String departureTime; + } + + @Data + public static class MaterialConsumptionInputDTO { + @Schema(description = "物资类型") + private MaterialType materialType; + @Schema(description = "单位消耗/配备数") + private double baseConsumption; + @Schema(description = "装备数量") + private int equipmentCount; + @Schema(description = "时间(小时)") + private double hours; + @Schema(description = "总需求") + private double totalRequirement; + + public enum MaterialType { + AMMUNITION, OIL, WEAPON + } + } + + @Data + public static class MaterialConsumptionResultDTO { + @Schema(description = "总消耗量/需求量") + private double totalConsumption; + } + + @Data + public static class UnitFormationInputDTO { + @Schema(description = "总兵力") + private double totalStrength; + @Schema(description = "单位兵力") + private double unitStrength; + } + + @Data + public static class UnitFormationResultDTO { + @Schema(description = "编成数量") + private double formationCount; + @Schema(description = "最后一个编成的兵力") + private double lastFormationStrength; + } + + @Data + public static class CrossRiverInputDTO { + @Schema(description = "总人数") + private int personCount; + @Schema(description = "单批次容量") + private int batchCapacity; + @Schema(description = "单批次时间(小时)") + private double batchHours; + @Schema(description = "准备时间(小时)") + private double prepareHours; + } + + @Data + public static class SpecialTimeResultDTO { + @Schema(description = "总时间") + private String totalTime; + } + + @Data + public static class LossRateInputDTO { + @Schema(description = "总兵力") + private double totalStrength; + @Schema(description = "损失兵力") + private double lostStrength; + } + + @Data + public static class LossRateResultDTO { + @Schema(description = "损失率(百分比)") + private double lossRate; + @Schema(description = "剩余兵力") + private double remainingStrength; + } +} diff --git a/src/main/java/com/yj/earth/common/service/ServerInitService.java b/src/main/java/com/yj/earth/common/service/ServerInitService.java index 074ecbb..731574e 100644 --- a/src/main/java/com/yj/earth/common/service/ServerInitService.java +++ b/src/main/java/com/yj/earth/common/service/ServerInitService.java @@ -1,11 +1,14 @@ package com.yj.earth.common.service; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.lang.UUID; import cn.hutool.crypto.digest.BCrypt; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yj.earth.business.domain.Role; import com.yj.earth.business.domain.Source; import com.yj.earth.business.domain.User; import com.yj.earth.business.service.RoleService; +import com.yj.earth.business.service.RoleSourceService; import com.yj.earth.business.service.SourceService; import com.yj.earth.business.service.UserService; import lombok.extern.slf4j.Slf4j; @@ -23,7 +26,8 @@ public class ServerInitService { private UserService userService; @Resource private RoleService roleService; - + @Resource + private RoleSourceService roleSourceService; public void init() { // 查询数据库所有需要加载的资源 List list =sourceService.list(new LambdaQueryWrapper() @@ -39,9 +43,14 @@ public class ServerInitService { } public void checkDefaultData() { + checkDefaultUserAndRole(); + checkDefaultSource(); + } + + public void checkDefaultUserAndRole() { // 查询角色表和用户表是否有数据 if(roleService.count() == 0 && userService.count() == 0) { - log.info("初始化默认数据"); + log.info("初始化默认用户角色数据"); // 新增一个管理员角色 Role adminRole = new Role(); adminRole.setRoleName("管理员"); @@ -64,4 +73,98 @@ public class ServerInitService { userService.save(user); } } + + public void checkDefaultSource() { + if(sourceService.count() == 0) { + // 查询管理员角色 + Role adminRole = roleService.getOne(new LambdaQueryWrapper().eq(Role::getRoleName, "管理员")); + log.info("初始化默认资源数据"); + Source source1 = new Source(); + source1.setId(UUID.fastUUID().toString(true)); + source1.setSourceName("倾斜模型"); + source1.setSourceType("directory"); + source1.setTreeIndex(0); + source1.setIsShow(1); + sourceService.save(source1); + roleSourceService.addRoleSource(adminRole.getId(), source1.getId()); + + Source source2 = new Source(); + source2.setId(UUID.fastUUID().toString(true)); + source2.setSourceName("人工模型"); + source2.setSourceType("directory"); + source2.setTreeIndex(0); + source2.setIsShow(1); + sourceService.save(source2); + roleSourceService.addRoleSource(adminRole.getId(), source2.getId()); + + Source source3 = new Source(); + source3.setId(UUID.fastUUID().toString(true)); + source3.setSourceName("卫星底图"); + source3.setSourceType("directory"); + source3.setTreeIndex(0); + source3.setIsShow(1); + sourceService.save(source3); + roleSourceService.addRoleSource(adminRole.getId(), source3.getId()); + + Source source4 = new Source(); + source4.setId(UUID.fastUUID().toString(true)); + source4.setSourceName("地形"); + source4.setSourceType("directory"); + source4.setTreeIndex(0); + source4.setIsShow(1); + sourceService.save(source4); + roleSourceService.addRoleSource(adminRole.getId(), source4.getId()); + + Source source5 = new Source(); + source5.setId(UUID.fastUUID().toString(true)); + source5.setSourceName("在线图源"); + source5.setSourceType("directory"); + source5.setTreeIndex(0); + source5.setIsShow(1); + sourceService.save(source5); + roleSourceService.addRoleSource(adminRole.getId(), source5.getId()); + + Source source6 = new Source(); + source6.setId(UUID.fastUUID().toString(true)); + source6.setSourceName("卫星图"); + source6.setSourceType("arcgisWximagery"); + source6.setParentId(source5.getId()); + source6.setTreeIndex(0); + source6.setIsShow(1); + sourceService.save(source6); + roleSourceService.addRoleSource(adminRole.getId(), source6.getId()); + + Source source7 = new Source(); + source7.setId(UUID.fastUUID().toString(true)); + source7.setSourceName("暗黑地图"); + source7.setSourceType("arcgisBlueImagery"); + source7.setParentId(source5.getId()); + source7.setTreeIndex(0); + source7.setIsShow(1); + sourceService.save(source7); + roleSourceService.addRoleSource(adminRole.getId(), source7.getId()); + + Source source8 = new Source(); + source8.setId(UUID.fastUUID().toString(true)); + source8.setSourceName("路网图"); + source8.setSourceType("gdlwImagery"); + source8.setParentId(source5.getId()); + source8.setTreeIndex(0); + source8.setIsShow(1); + sourceService.save(source8); + roleSourceService.addRoleSource(adminRole.getId(), source8.getId()); + + Source source9 = new Source(); + source9.setId(UUID.fastUUID().toString(true)); + source9.setSourceName("矢量图"); + source9.setSourceType("gdslImagery"); + source9.setParentId(source5.getId()); + source9.setTreeIndex(0); + source9.setIsShow(1); + sourceService.save(source9); + roleSourceService.addRoleSource(adminRole.getId(), source9.getId()); + + + } + } } diff --git a/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java b/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java index f862d24..7a30b70 100644 --- a/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java +++ b/src/main/java/com/yj/earth/common/service/SourceDataGenerator.java @@ -206,7 +206,7 @@ public class SourceDataGenerator { Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); if (actualTypeArguments.length == 2) { try { - // key默认用字符串"key",value按泛型类型设默认值(字符串为"") + // key默认用字符串"key"、value按泛型类型设默认值(字符串为"") Object key = "key"; Object value = actualTypeArguments[1] instanceof Class ? initializeObject((Class) actualTypeArguments[1]) @@ -238,7 +238,7 @@ public class SourceDataGenerator { } /** - * 获取类型默认值:字符串返回空字符串,其他类型按原有逻辑 + * 获取类型默认值:字符串返回空字符串、其他类型按原有逻辑 */ private static Object getDefaultValue(Class type) { if (type.equals(String.class)) { diff --git a/src/main/java/com/yj/earth/common/util/CodeUtil.java b/src/main/java/com/yj/earth/common/util/CodeUtil.java index a6cacef..c885103 100644 --- a/src/main/java/com/yj/earth/common/util/CodeUtil.java +++ b/src/main/java/com/yj/earth/common/util/CodeUtil.java @@ -34,7 +34,7 @@ public class CodeUtil { } // 传入需要生成代码的表名 - Generation("file_info"); + Generation("model"); } public static void Generation(String... tableName) { diff --git a/src/main/java/com/yj/earth/common/util/ServerUniqueIdUtil.java b/src/main/java/com/yj/earth/common/util/ServerUniqueIdUtil.java index 98b8231..ade77a4 100644 --- a/src/main/java/com/yj/earth/common/util/ServerUniqueIdUtil.java +++ b/src/main/java/com/yj/earth/common/util/ServerUniqueIdUtil.java @@ -5,9 +5,8 @@ import oshi.SystemInfo; import oshi.hardware.*; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; import java.util.HexFormat; +import java.util.List; /** * 服务器唯一标识工具类 diff --git a/src/main/java/com/yj/earth/datasource/DatabaseManager.java b/src/main/java/com/yj/earth/datasource/DatabaseManager.java index 38673b0..ab2e3ac 100644 --- a/src/main/java/com/yj/earth/datasource/DatabaseManager.java +++ b/src/main/java/com/yj/earth/datasource/DatabaseManager.java @@ -47,6 +47,8 @@ public class DatabaseManager { classes.add(Source.class); classes.add(RoleSource.class); classes.add(FileInfo.class); + classes.add(ModelType.class); + classes.add(Model.class); ENTITY_CLASSES = Collections.unmodifiableList(classes); } diff --git a/src/main/java/com/yj/earth/design/Model.java b/src/main/java/com/yj/earth/design/Model.java new file mode 100644 index 0000000..e148c2b --- /dev/null +++ b/src/main/java/com/yj/earth/design/Model.java @@ -0,0 +1,33 @@ +package com.yj.earth.design; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Model { + @Schema(description = "主键") + private String id; + @Schema(description = "模型类型ID") + private String modelTypeId; + @Schema(description = "模型名称") + private String modelName; + @Schema(description = "模型类型") + private String modelType; + @Schema(description = "海报类型") + private String posterType; + @Schema(description = "海报数据") + private byte[] poster; + @Schema(description = "模型数据") + private byte[] data; + @Schema(description = "模型视图") + private String view; + @Schema(description = "创建时间") + private LocalDateTime createdAt; + @Schema(description = "更新时间") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/yj/earth/design/ModelType.java b/src/main/java/com/yj/earth/design/ModelType.java new file mode 100644 index 0000000..f2479a8 --- /dev/null +++ b/src/main/java/com/yj/earth/design/ModelType.java @@ -0,0 +1,20 @@ +package com.yj.earth.design; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class ModelType { + @Schema(description = "主键") + private String id; + @Schema(description = "模型类型名称") + private String name; + @Schema(description = "父级节点ID") + private String parentId; + @Schema(description = "创建时间") + private LocalDateTime createdAt; + @Schema(description = "更新时间") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/yj/earth/dto/source/UploadLocationImageDto.java b/src/main/java/com/yj/earth/dto/source/UploadLocationImageDto.java new file mode 100644 index 0000000..f5be6ac --- /dev/null +++ b/src/main/java/com/yj/earth/dto/source/UploadLocationImageDto.java @@ -0,0 +1,16 @@ +package com.yj.earth.dto.source; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class UploadLocationImageDto { + @Schema(description = "资源ID列表") + private List ids; + @Schema(description = "父节点ID") + private String parentId; + @Schema(description = "树状索引") + private Integer treeIndex; +} diff --git a/src/main/java/com/yj/earth/params/DiffuseScan.java b/src/main/java/com/yj/earth/params/DiffuseScan.java index bf8bda2..f913827 100644 --- a/src/main/java/com/yj/earth/params/DiffuseScan.java +++ b/src/main/java/com/yj/earth/params/DiffuseScan.java @@ -31,7 +31,7 @@ public class DiffuseScan { @Data public static class CustomView { - // 空对象,暂无需字段 + // 空对象、暂无需字段 } @Data diff --git a/src/main/java/com/yj/earth/params/Layer.java b/src/main/java/com/yj/earth/params/Layer.java index 4fa9775..e9b220b 100644 --- a/src/main/java/com/yj/earth/params/Layer.java +++ b/src/main/java/com/yj/earth/params/Layer.java @@ -6,6 +6,9 @@ import lombok.Data; @Data @SourceType("layer") public class Layer { + private String id; + private String name; + private Boolean show; private Integer alpha; private Integer brightness; } diff --git a/src/main/java/com/yj/earth/params/RadarScan.java b/src/main/java/com/yj/earth/params/RadarScan.java index 5c4e665..1801cfc 100644 --- a/src/main/java/com/yj/earth/params/RadarScan.java +++ b/src/main/java/com/yj/earth/params/RadarScan.java @@ -22,7 +22,7 @@ public class RadarScan { @Data public static class CustomView { - // 空对象,暂无需字段 + // 空对象、暂无需字段 } @Data diff --git a/src/main/java/com/yj/earth/params/Roam.java b/src/main/java/com/yj/earth/params/Roam.java new file mode 100644 index 0000000..38ecbdb --- /dev/null +++ b/src/main/java/com/yj/earth/params/Roam.java @@ -0,0 +1,35 @@ +package com.yj.earth.params; + +import com.yj.earth.annotation.SourceType; +import lombok.Data; + +import java.util.List; + +@Data +@SourceType("roam") +public class Roam { + private String name; + private List points; + private String repeat; + + @Data + public static class Point { + private int duration; + private Position position; + private Orientation orientation; + } + + @Data + public static class Position { + private double lng; + private double lat; + private double alt; + } + + @Data + public static class Orientation { + private double heading; + private double pitch; + private double roll; + } +}