资源相关
This commit is contained in:
		
							
								
								
									
										7
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								pom.xml
									
									
									
									
									
								
							| @ -149,6 +149,13 @@ | |||||||
|             <artifactId>aspectjweaver</artifactId> |             <artifactId>aspectjweaver</artifactId> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|  |         <!-- 图片元数据信息 --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>com.drewnoakes</groupId> | ||||||
|  |             <artifactId>metadata-extractor</artifactId> | ||||||
|  |             <version>2.18.0</version> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|         <!-- HttpClient5 --> |         <!-- HttpClient5 --> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.apache.httpcomponents</groupId> |             <groupId>org.apache.httpcomponents</groupId> | ||||||
|  | |||||||
| @ -49,6 +49,7 @@ public class ServerApp implements CommandLineRunner { | |||||||
|         serverInitService.init(); |         serverInitService.init(); | ||||||
|         // 检查默认数据 |         // 检查默认数据 | ||||||
|         serverInitService.checkDefaultData(); |         serverInitService.checkDefaultData(); | ||||||
|  |         // 打印项目文档地址 | ||||||
|         log.info("项目文档地址: {}", "http://" + serverConfig.getHost() + ":" + serverConfig.getPort() + "/doc.html"); |         log.info("项目文档地址: {}", "http://" + serverConfig.getHost() + ":" + serverConfig.getPort() + "/doc.html"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/main/java/com/yj/earth/annotation/CheckAuth.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/main/java/com/yj/earth/annotation/CheckAuth.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | package com.yj.earth.annotation; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.*; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 授权验证注解 | ||||||
|  |  */ | ||||||
|  | @Target({ElementType.METHOD}) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Documented | ||||||
|  | public @interface CheckAuth { | ||||||
|  | } | ||||||
							
								
								
									
										99
									
								
								src/main/java/com/yj/earth/aspect/AuthAspect.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/main/java/com/yj/earth/aspect/AuthAspect.java
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -2,7 +2,6 @@ package com.yj.earth.auth; | |||||||
|  |  | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import com.yj.earth.common.util.ServerUniqueIdUtil; |  | ||||||
|  |  | ||||||
| import javax.crypto.Cipher; | import javax.crypto.Cipher; | ||||||
| import javax.crypto.spec.SecretKeySpec; | import javax.crypto.spec.SecretKeySpec; | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| package com.yj.earth.auth; | package com.yj.earth.auth; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; |  | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import com.yj.earth.common.util.ServerUniqueIdUtil; | import com.yj.earth.common.util.ServerUniqueIdUtil; | ||||||
|  |  | ||||||
| @ -100,7 +99,7 @@ public class AuthValidator { | |||||||
|     public static void main(String[] args) { |     public static void main(String[] args) { | ||||||
|         String serverUniqueId = ServerUniqueIdUtil.getServerUniqueId(); |         String serverUniqueId = ServerUniqueIdUtil.getServerUniqueId(); | ||||||
|         // 生成授权 |         // 生成授权 | ||||||
|         String authString = AuthGenerator.generateAuth("标准版", 1000, 30, serverUniqueId); |         String authString = AuthGenerator.generateAuth("标准版", 1000, 30, "35A0DF1D05AEAE77E1E2715CC36A7368"); | ||||||
|         System.out.println("授权字符串:" + authString); |         System.out.println("授权字符串:" + authString); | ||||||
|         // 验证授权 |         // 验证授权 | ||||||
|         boolean isValid = AuthValidator.validateAuth(authString, serverUniqueId); |         boolean isValid = AuthValidator.validateAuth(authString, serverUniqueId); | ||||||
|  | |||||||
| @ -2,6 +2,17 @@ package com.yj.earth.business.controller; | |||||||
|  |  | ||||||
| import cn.hutool.core.io.FileUtil; | import cn.hutool.core.io.FileUtil; | ||||||
| import cn.hutool.core.util.IdUtil; | 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 cn.hutool.crypto.digest.DigestUtil; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import com.baomidou.mybatisplus.core.metadata.IPage; | 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.domain.FileInfo; | ||||||
| import com.yj.earth.business.service.FileInfoService; | import com.yj.earth.business.service.FileInfoService; | ||||||
| import com.yj.earth.common.util.ApiResponse; | import com.yj.earth.common.util.ApiResponse; | ||||||
|  | import com.yj.earth.common.util.JsonMapConverter; | ||||||
| import com.yj.earth.vo.FileInfoVo; | import com.yj.earth.vo.FileInfoVo; | ||||||
| import io.swagger.v3.oas.annotations.Operation; | import io.swagger.v3.oas.annotations.Operation; | ||||||
| import io.swagger.v3.oas.annotations.Parameter; | 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.BeanUtils; | ||||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||||
| import org.springframework.http.HttpHeaders; | import org.springframework.http.HttpHeaders; | ||||||
|  | import org.springframework.util.DigestUtils; | ||||||
| import org.springframework.web.bind.annotation.*; | import org.springframework.web.bind.annotation.*; | ||||||
| import org.springframework.web.multipart.MultipartFile; | import org.springframework.web.multipart.MultipartFile; | ||||||
|  |  | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| import java.io.File; | import java.io.InputStream; | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.OutputStream; |  | ||||||
| import java.net.URLEncoder; | import java.net.URLEncoder; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.ArrayList; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.Map; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| @Tag(name = "文件数据管理") | @Tag(name = "文件数据管理") | ||||||
| @ -168,11 +179,7 @@ public class FileInfoController { | |||||||
|  |  | ||||||
|     @Operation(summary = "文件预览") |     @Operation(summary = "文件预览") | ||||||
|     @GetMapping("/preview/{id}") |     @GetMapping("/preview/{id}") | ||||||
|     public void previewFile( |     public void previewFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException { | ||||||
|             @Parameter(description = "文件ID", required = true) |  | ||||||
|             @PathVariable String id, |  | ||||||
|             HttpServletResponse response) throws IOException { |  | ||||||
|  |  | ||||||
|         // 根据ID查询文件信息 |         // 根据ID查询文件信息 | ||||||
|         FileInfo fileInfo = fileInfoService.getById(id); |         FileInfo fileInfo = fileInfoService.getById(id); | ||||||
|         if (fileInfo == null) { |         if (fileInfo == null) { | ||||||
| @ -200,42 +207,100 @@ public class FileInfoController { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Operation(summary = "文件列表") |     public String handleLocationImageUpload(MultipartFile file) { | ||||||
|     @GetMapping("/list") |         try { | ||||||
|     public ApiResponse getFileList( |             // 校验文件是否为空 | ||||||
|             @Parameter(description = "页码", required = true) Integer pageNum, |             if (file.isEmpty()) { | ||||||
|             @Parameter(description = "每页条数", required = true) Integer pageSize, |                 throw new IllegalArgumentException("上传文件不能为空"); | ||||||
|             @Parameter(description = "文件名称") String fileName) { |             } | ||||||
|  |             // 获取文件基本信息 | ||||||
|         // 创建分页对象 |             String originalFilename = file.getOriginalFilename(); | ||||||
|         Page<FileInfo> page = new Page<>(pageNum, pageSize); |             String fileSuffix = FileUtil.extName(originalFilename); | ||||||
|         // 构建查询条件 |             String contentType = file.getContentType(); | ||||||
|         LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>(); |             // 验证是否为图片文件 | ||||||
|         if (fileName != null && !fileName.isEmpty()) { |             if (contentType == null || !contentType.startsWith("image/")) { | ||||||
|             queryWrapper.like(FileInfo::getFileName, fileName); |                 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<FileInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||||||
|  |             queryWrapper.eq(FileInfo::getFileName, originalFilename) | ||||||
|  |                     .eq(FileInfo::getFileMd5, fileMd5); | ||||||
|  |             if (fileInfoService.count(queryWrapper) > 0) { | ||||||
|  |                 throw new IllegalStateException("已存在相同的图片文件"); | ||||||
|  |             } | ||||||
|  |             // 提取图片元数据(使用已保存的文件,避免使用临时文件) | ||||||
|  |             Map<String, Object> 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<String, Object> 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<FileInfo> 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<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> { |      * 提取图片的EXIF元数据(包括定位信息) | ||||||
|             FileInfoVo vo = new FileInfoVo(); |      */ | ||||||
|             BeanUtils.copyProperties(fileInfo, vo); |     private Map<String, Object> extractImageMetadata(InputStream inputStream) { | ||||||
|             vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId()); |         try { | ||||||
|             vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId()); |             Map<String, Object> result = new HashMap<>(); | ||||||
|             return vo; |             Metadata metadata = ImageMetadataReader.readMetadata(inputStream); | ||||||
|         }).collect(Collectors.toList()); |  | ||||||
|  |  | ||||||
|         // 构建分页结果 |             // 遍历所有元数据目录 | ||||||
|         Page<FileInfoVo> resultPage = new Page<>(); |             for (Directory directory : metadata.getDirectories()) { | ||||||
|         resultPage.setRecords(records); |                 String directoryName = directory.getName(); | ||||||
|         resultPage.setTotal(fileInfoPage.getTotal()); |                 Map<String, String> directoryTags = new HashMap<>(); | ||||||
|         resultPage.setSize(fileInfoPage.getSize()); |  | ||||||
|         resultPage.setCurrent(fileInfoPage.getCurrent()); |                 // 提取当前目录下的所有标签 | ||||||
|         resultPage.setPages(fileInfoPage.getPages()); |                 for (com.drew.metadata.Tag tag : directory.getTags()) { | ||||||
|         return ApiResponse.success(resultPage); |                     directoryTags.put(tag.getTagName(), tag.getDescription()); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // 存储当前目录的所有标签 | ||||||
|  |                 if (!directoryTags.isEmpty()) { | ||||||
|  |                     result.put(directoryName, directoryTags); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return result; | ||||||
|  |         } catch (ImageProcessingException | IOException e) { | ||||||
|  |             return Collections.emptyMap(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import com.graphhopper.GraphHopper; | |||||||
| import com.graphhopper.ResponsePath; | import com.graphhopper.ResponsePath; | ||||||
| import com.graphhopper.config.Profile; | import com.graphhopper.config.Profile; | ||||||
| import com.graphhopper.util.shapes.GHPoint; | import com.graphhopper.util.shapes.GHPoint; | ||||||
|  | import com.yj.earth.annotation.CheckAuth; | ||||||
| import com.yj.earth.business.domain.FileInfo; | import com.yj.earth.business.domain.FileInfo; | ||||||
| import com.yj.earth.business.service.FileInfoService; | import com.yj.earth.business.service.FileInfoService; | ||||||
| import com.yj.earth.common.config.GraphHopperProperties; | import com.yj.earth.common.config.GraphHopperProperties; | ||||||
| @ -51,6 +52,7 @@ public class GraphHopperController { | |||||||
|     private final AtomicBoolean isLoaded = new AtomicBoolean(false); |     private final AtomicBoolean isLoaded = new AtomicBoolean(false); | ||||||
|  |  | ||||||
|     @Operation(summary = "获取地图列表") |     @Operation(summary = "获取地图列表") | ||||||
|  |     @CheckAuth | ||||||
|     @GetMapping("/list") |     @GetMapping("/list") | ||||||
|     public ApiResponse list() { |     public ApiResponse list() { | ||||||
|         LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>(); |         LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||||||
| @ -60,6 +62,7 @@ public class GraphHopperController { | |||||||
|  |  | ||||||
|     @Operation(summary = "加载地图数据") |     @Operation(summary = "加载地图数据") | ||||||
|     @PostMapping("/loadMap") |     @PostMapping("/loadMap") | ||||||
|  |     @CheckAuth | ||||||
|     public ApiResponse loadMap(@Parameter(description = "文件ID") @RequestParam String fileId) { |     public ApiResponse loadMap(@Parameter(description = "文件ID") @RequestParam String fileId) { | ||||||
|         // 参数校验 |         // 参数校验 | ||||||
|         if (fileId == null) { |         if (fileId == null) { | ||||||
| @ -118,6 +121,7 @@ public class GraphHopperController { | |||||||
|  |  | ||||||
|     @Operation(summary = "路径规划") |     @Operation(summary = "路径规划") | ||||||
|     @PostMapping("/route") |     @PostMapping("/route") | ||||||
|  |     @CheckAuth | ||||||
|     public ApiResponse calculateRoute(@RequestBody RouteRequest request) { |     public ApiResponse calculateRoute(@RequestBody RouteRequest request) { | ||||||
|         // 校验地图是否加载完成 + 实例是否可用 |         // 校验地图是否加载完成 + 实例是否可用 | ||||||
|         if (!isLoaded.get() || currentHopper == null) { |         if (!isLoaded.get() || currentHopper == null) { | ||||||
| @ -145,26 +149,36 @@ public class GraphHopperController { | |||||||
|  |  | ||||||
|             // 处理错误 |             // 处理错误 | ||||||
|             if (response.hasErrors()) { |             if (response.hasErrors()) { | ||||||
|  |                 // 检查是否有超出范围的错误 | ||||||
|  |                 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()); |                     return ApiResponse.failure("路径计算失败: " + response.getErrors().toString()); | ||||||
|                 } |                 } | ||||||
|  |             } | ||||||
|             // 解析结果 |             // 解析结果 | ||||||
|             ResponsePath bestPath = response.getBest(); |             ResponsePath bestPath = response.getBest(); | ||||||
|             List<Point> pathPoints = new ArrayList<>(); |             List<Point> pathPoints = new ArrayList<>(); | ||||||
|             bestPath.getPoints().forEach(ghPoint -> |             bestPath.getPoints().forEach(ghPoint -> | ||||||
|                     pathPoints.add(new Point(ghPoint.getLat(), ghPoint.getLon())) |                     pathPoints.add(new Point(ghPoint.getLat(), ghPoint.getLon())) | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|             // 封装返回 |             // 封装返回 | ||||||
|             RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints); |             RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints); | ||||||
|             return ApiResponse.success(routeResponse); |             return ApiResponse.success(routeResponse); | ||||||
|  |         } catch (com.graphhopper.util.exceptions.PointOutOfBoundsException e) { | ||||||
|  |             // 捕获单点超出范围的异常 | ||||||
|  |             return ApiResponse.failure("路径超出地图范围"); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             return ApiResponse.failure("路径计算异常: " + e.getMessage()); |             return ApiResponse.failure("路径计算异常: " + e.getMessage()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Operation(summary = "获取交通方式") |     @Operation(summary = "获取交通方式") | ||||||
|  |     @CheckAuth | ||||||
|     @PostMapping("/profiles") |     @PostMapping("/profiles") | ||||||
|     public ApiResponse profiles() { |     public ApiResponse profiles() { | ||||||
|         return ApiResponse.success(graphHopperProperties.getProfiles()); |         return ApiResponse.success(graphHopperProperties.getProfiles()); | ||||||
|  | |||||||
| @ -2,26 +2,40 @@ package com.yj.earth.business.controller; | |||||||
|  |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
| import cn.hutool.core.io.FileUtil; | 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.LambdaQueryWrapper; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 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.core.JsonProcessingException; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | 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.domain.Source; | ||||||
| import com.yj.earth.business.service.RoleSourceService; | import com.yj.earth.business.service.*; | ||||||
| import com.yj.earth.business.service.SourceService; |  | ||||||
| import com.yj.earth.business.service.UserService; |  | ||||||
| import com.yj.earth.common.service.SourceDataGenerator; | import com.yj.earth.common.service.SourceDataGenerator; | ||||||
| import com.yj.earth.common.service.SourceParamsValidator; | import com.yj.earth.common.service.SourceParamsValidator; | ||||||
| import com.yj.earth.common.util.ApiResponse; | import com.yj.earth.common.util.ApiResponse; | ||||||
| import com.yj.earth.common.util.MapUtil; | import com.yj.earth.common.util.MapUtil; | ||||||
| import com.yj.earth.dto.source.*; | import com.yj.earth.dto.source.*; | ||||||
| import io.swagger.v3.oas.annotations.Operation; | import io.swagger.v3.oas.annotations.Operation; | ||||||
|  | import io.swagger.v3.oas.annotations.Parameter; | ||||||
| import io.swagger.v3.oas.annotations.tags.Tag; | import io.swagger.v3.oas.annotations.tags.Tag; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.springframework.beans.BeanUtils; | import org.springframework.beans.BeanUtils; | ||||||
|  | import org.springframework.beans.factory.annotation.Value; | ||||||
| import org.springframework.web.bind.annotation.*; | import org.springframework.web.bind.annotation.*; | ||||||
|  | import org.springframework.web.multipart.MultipartFile; | ||||||
|  |  | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  |  | ||||||
| import static com.yj.earth.common.constant.GlobalConstant.DIRECTORY; | import static com.yj.earth.common.constant.GlobalConstant.DIRECTORY; | ||||||
| @ -39,10 +53,19 @@ public class SourceController { | |||||||
|     @Resource |     @Resource | ||||||
|     private RoleSourceService roleSourceService; |     private RoleSourceService roleSourceService; | ||||||
|     @Resource |     @Resource | ||||||
|  |     private RoleService roleService; | ||||||
|  |     @Resource | ||||||
|     private SourceParamsValidator sourceParamsValidator; |     private SourceParamsValidator sourceParamsValidator; | ||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private SourceDataGenerator sourceDataGenerator; |     private SourceDataGenerator sourceDataGenerator; | ||||||
|  |     @Resource | ||||||
|  |     private FileInfoService fileInfoService; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private FileInfoController fileInfoControllerl; | ||||||
|  |  | ||||||
|  |     @Value("${file.upload.path}") | ||||||
|  |     private String uploadPath; | ||||||
|  |  | ||||||
|     @PostMapping("/addDirectory") |     @PostMapping("/addDirectory") | ||||||
|     @Operation(summary = "新增目录资源") |     @Operation(summary = "新增目录资源") | ||||||
| @ -86,10 +109,10 @@ public class SourceController { | |||||||
|         source.setSourceName(sourceName); |         source.setSourceName(sourceName); | ||||||
|         source.setParentId(addModelSourceDto.getParentId()); |         source.setParentId(addModelSourceDto.getParentId()); | ||||||
|         source.setTreeIndex(addModelSourceDto.getTreeIndex()); |         source.setTreeIndex(addModelSourceDto.getTreeIndex()); | ||||||
|  |         source.setParams(addModelSourceDto.getParams()); | ||||||
|         source.setDetail(detail); |         source.setDetail(detail); | ||||||
|         source.setSourceType(MapUtil.getString(MapUtil.jsonToMap(detail), "fileType")); |         source.setSourceType(MapUtil.getString(MapUtil.jsonToMap(detail), "fileType")); | ||||||
|         source.setIsShow(SHOW); |         source.setIsShow(SHOW); | ||||||
|         source.setParams(addModelSourceDto.getParams()); |  | ||||||
|         sourceService.save(source); |         sourceService.save(source); | ||||||
|         // 添加资源到该用户的角色下 |         // 添加资源到该用户的角色下 | ||||||
|         roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); |         roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); | ||||||
| @ -196,6 +219,30 @@ public class SourceController { | |||||||
|         return ApiResponse.success(null); |         return ApiResponse.success(null); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @PostMapping("/uploadLocationImage") | ||||||
|  |     @Operation(summary = "新增定位图片") | ||||||
|  |     public ApiResponse uploadLocationImage(@RequestParam("ids") @Parameter(description = "上传定位图片ID列表") List<String> 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") |     @GetMapping("/type") | ||||||
|     @Operation(summary = "获取已有类型") |     @Operation(summary = "获取已有类型") | ||||||
|     public ApiResponse getSupportedSourceTypes() { |     public ApiResponse getSupportedSourceTypes() { | ||||||
| @ -208,4 +255,61 @@ public class SourceController { | |||||||
|     public String getExampleData(String sourceType) throws JsonProcessingException { |     public String getExampleData(String sourceType) throws JsonProcessingException { | ||||||
|         return sourceDataGenerator.generateDefaultJson(sourceType); |         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<Source>() | ||||||
|  |                 .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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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+)天(?<h>\\d+)时(?<m>\\d+)分(?<s>\\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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,11 +1,14 @@ | |||||||
| package com.yj.earth.common.service; | 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 cn.hutool.crypto.digest.BCrypt; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import com.yj.earth.business.domain.Role; | import com.yj.earth.business.domain.Role; | ||||||
| import com.yj.earth.business.domain.Source; | import com.yj.earth.business.domain.Source; | ||||||
| import com.yj.earth.business.domain.User; | import com.yj.earth.business.domain.User; | ||||||
| import com.yj.earth.business.service.RoleService; | 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.SourceService; | ||||||
| import com.yj.earth.business.service.UserService; | import com.yj.earth.business.service.UserService; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| @ -23,7 +26,8 @@ public class ServerInitService { | |||||||
|     private UserService userService; |     private UserService userService; | ||||||
|     @Resource |     @Resource | ||||||
|     private RoleService roleService; |     private RoleService roleService; | ||||||
|  |     @Resource | ||||||
|  |     private RoleSourceService roleSourceService; | ||||||
|     public void init() { |     public void init() { | ||||||
|         // 查询数据库所有需要加载的资源 |         // 查询数据库所有需要加载的资源 | ||||||
|         List<Source> list =sourceService.list(new LambdaQueryWrapper<Source>() |         List<Source> list =sourceService.list(new LambdaQueryWrapper<Source>() | ||||||
| @ -39,9 +43,14 @@ public class ServerInitService { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void checkDefaultData() { |     public void checkDefaultData() { | ||||||
|  |         checkDefaultUserAndRole(); | ||||||
|  |         checkDefaultSource(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void checkDefaultUserAndRole() { | ||||||
|         // 查询角色表和用户表是否有数据 |         // 查询角色表和用户表是否有数据 | ||||||
|         if(roleService.count() == 0 && userService.count() == 0) { |         if(roleService.count() == 0 && userService.count() == 0) { | ||||||
|             log.info("初始化默认数据"); |             log.info("初始化默认用户角色数据"); | ||||||
|             // 新增一个管理员角色 |             // 新增一个管理员角色 | ||||||
|             Role adminRole = new Role(); |             Role adminRole = new Role(); | ||||||
|             adminRole.setRoleName("管理员"); |             adminRole.setRoleName("管理员"); | ||||||
| @ -64,4 +73,98 @@ public class ServerInitService { | |||||||
|             userService.save(user); |             userService.save(user); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void checkDefaultSource() { | ||||||
|  |         if(sourceService.count() == 0) { | ||||||
|  |             // 查询管理员角色 | ||||||
|  |             Role adminRole = roleService.getOne(new LambdaQueryWrapper<Role>().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()); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -206,7 +206,7 @@ public class SourceDataGenerator { | |||||||
|             Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); |             Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); | ||||||
|             if (actualTypeArguments.length == 2) { |             if (actualTypeArguments.length == 2) { | ||||||
|                 try { |                 try { | ||||||
|                     // key默认用字符串"key",value按泛型类型设默认值(字符串为"") |                     // key默认用字符串"key"、value按泛型类型设默认值(字符串为"") | ||||||
|                     Object key = "key"; |                     Object key = "key"; | ||||||
|                     Object value = actualTypeArguments[1] instanceof Class |                     Object value = actualTypeArguments[1] instanceof Class | ||||||
|                             ? initializeObject((Class<?>) actualTypeArguments[1]) |                             ? initializeObject((Class<?>) actualTypeArguments[1]) | ||||||
| @ -238,7 +238,7 @@ public class SourceDataGenerator { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取类型默认值:字符串返回空字符串,其他类型按原有逻辑 |      * 获取类型默认值:字符串返回空字符串、其他类型按原有逻辑 | ||||||
|      */ |      */ | ||||||
|     private static Object getDefaultValue(Class<?> type) { |     private static Object getDefaultValue(Class<?> type) { | ||||||
|         if (type.equals(String.class)) { |         if (type.equals(String.class)) { | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ public class CodeUtil { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 传入需要生成代码的表名 |         // 传入需要生成代码的表名 | ||||||
|         Generation("file_info"); |         Generation("model"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void Generation(String... tableName) { |     public static void Generation(String... tableName) { | ||||||
|  | |||||||
| @ -5,9 +5,8 @@ import oshi.SystemInfo; | |||||||
| import oshi.hardware.*; | import oshi.hardware.*; | ||||||
|  |  | ||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
| import java.security.NoSuchAlgorithmException; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.HexFormat; | import java.util.HexFormat; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 服务器唯一标识工具类 |  * 服务器唯一标识工具类 | ||||||
|  | |||||||
| @ -47,6 +47,8 @@ public class DatabaseManager { | |||||||
|         classes.add(Source.class); |         classes.add(Source.class); | ||||||
|         classes.add(RoleSource.class); |         classes.add(RoleSource.class); | ||||||
|         classes.add(FileInfo.class); |         classes.add(FileInfo.class); | ||||||
|  |         classes.add(ModelType.class); | ||||||
|  |         classes.add(Model.class); | ||||||
|         ENTITY_CLASSES = Collections.unmodifiableList(classes); |         ENTITY_CLASSES = Collections.unmodifiableList(classes); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								src/main/java/com/yj/earth/design/Model.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/main/java/com/yj/earth/design/Model.java
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								src/main/java/com/yj/earth/design/ModelType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/main/java/com/yj/earth/design/ModelType.java
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
| @ -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<String> ids; | ||||||
|  |     @Schema(description = "父节点ID") | ||||||
|  |     private String parentId; | ||||||
|  |     @Schema(description = "树状索引") | ||||||
|  |     private Integer treeIndex; | ||||||
|  | } | ||||||
| @ -31,7 +31,7 @@ public class DiffuseScan { | |||||||
|  |  | ||||||
|     @Data |     @Data | ||||||
|     public static class CustomView { |     public static class CustomView { | ||||||
|         // 空对象,暂无需字段 |         // 空对象、暂无需字段 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Data |     @Data | ||||||
|  | |||||||
| @ -6,6 +6,9 @@ import lombok.Data; | |||||||
| @Data | @Data | ||||||
| @SourceType("layer") | @SourceType("layer") | ||||||
| public class Layer { | public class Layer { | ||||||
|  |     private String id; | ||||||
|  |     private String name; | ||||||
|  |     private Boolean show; | ||||||
|     private Integer alpha; |     private Integer alpha; | ||||||
|     private Integer brightness; |     private Integer brightness; | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ public class RadarScan { | |||||||
|  |  | ||||||
|     @Data |     @Data | ||||||
|     public static class CustomView { |     public static class CustomView { | ||||||
|         // 空对象,暂无需字段 |         // 空对象、暂无需字段 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Data |     @Data | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								src/main/java/com/yj/earth/params/Roam.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/main/java/com/yj/earth/params/Roam.java
									
									
									
									
									
										Normal file
									
								
							| @ -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<Point> 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 ZZX9599
					ZZX9599