模型库
This commit is contained in:
		| @ -61,13 +61,13 @@ public class AuthAspect { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 授权有效,继续执行原方法 | ||||
|         // 授权有效、继续执行原方法 | ||||
|         return point.proceed(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 读取授权文件内容 | ||||
|      * @return 授权文件内容,如果文件不存在或读取失败则返回null | ||||
|      * @return 授权文件内容、如果文件不存在或读取失败则返回null | ||||
|      */ | ||||
|     private String readLicenseFile() { | ||||
|         try { | ||||
|  | ||||
| @ -70,4 +70,8 @@ public class AuthGenerator { | ||||
|             throw new RuntimeException("授权信息加密失败", e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void main(String[] args) { | ||||
|         System.out.println(generateAuth("标准版", 1000, 30, "DAC653349FD15F1E6DB2F9322AD628F4")); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,17 +4,22 @@ 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.lang.Rational; | ||||
| import com.drew.metadata.Directory; | ||||
| import com.drew.metadata.Metadata; | ||||
|  | ||||
| import java.io.*; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.NoSuchFileException; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.nio.file.attribute.BasicFileAttributes; | ||||
| import java.util.*; | ||||
|  | ||||
| import cn.hutool.crypto.digest.DigestUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import com.drew.metadata.MetadataException; | ||||
| import com.drew.metadata.exif.GpsDirectory; | ||||
| import com.yj.earth.business.domain.FileInfo; | ||||
| import com.yj.earth.business.service.FileInfoService; | ||||
| import com.yj.earth.common.util.ApiResponse; | ||||
| @ -46,41 +51,22 @@ public class FileInfoController { | ||||
|     @Resource | ||||
|     private FileInfoService fileInfoService; | ||||
|  | ||||
|     @Value("${file.upload.path}") | ||||
|     private String uploadPath; | ||||
|  | ||||
|     // 获取项目根目录 | ||||
|     private String getProjectRootPath() { | ||||
|         return System.getProperty("user.dir"); | ||||
|     } | ||||
|  | ||||
|     // 获取完整的上传目录路径 | ||||
|     private String getFullUploadPath() { | ||||
|         // 拼接项目根目录和配置的上传路径 | ||||
|         return getProjectRootPath() + File.separator + uploadPath; | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "文件上传") | ||||
|     @PostMapping("/upload") | ||||
|     public ApiResponse uploadFiles(@Parameter(description = "上传的文件数组", required = true) @RequestParam("files") MultipartFile[] files) throws IOException { | ||||
|  | ||||
|         // 校验文件数组是否为空 | ||||
|         if (files == null || files.length == 0) { | ||||
|             return ApiResponse.failure("上传文件不能为空"); | ||||
|         } | ||||
|  | ||||
|         // 获取完整的上传目录路径 | ||||
|         String fullUploadPath = getFullUploadPath(); | ||||
|  | ||||
|         String fullUploadPath = fileInfoService.getFullUploadPath(); | ||||
|         List<FileInfoVo> fileInfoVoList = new ArrayList<>(); | ||||
|  | ||||
|         // 遍历处理每个文件 | ||||
|         for (MultipartFile file : files) { | ||||
|             // 跳过空文件 | ||||
|             if (file.isEmpty()) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // 获取原始文件名和后缀 | ||||
|             String originalFilename = file.getOriginalFilename(); | ||||
|             String fileSuffix = FileUtil.extName(originalFilename); | ||||
| @ -106,7 +92,7 @@ public class FileInfoController { | ||||
|             // 查询有没有文件名一样并且 MD5 也一样的数据 | ||||
|             LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||||
|             queryWrapper.eq(FileInfo::getFileName, originalFilename).eq(FileInfo::getFileMd5, fileMd5); | ||||
|             if (fileInfoService.count(queryWrapper) > 0) {  // 修复了此处的bug、添加了queryWrapper参数 | ||||
|             if (fileInfoService.count(queryWrapper) > 0) { | ||||
|                 return ApiResponse.failure("已存在文件名相同且内容完全一致的文件"); | ||||
|             } | ||||
|  | ||||
| @ -116,7 +102,7 @@ public class FileInfoController { | ||||
|             fileInfo.setFileSuffix(fileSuffix); | ||||
|             fileInfo.setContentType(contentType); | ||||
|             fileInfo.setFileSize(file.getSize()); | ||||
|             fileInfo.setFilePath(uniqueFileName);  // 只保存相对文件名、不保存完整路径 | ||||
|             fileInfo.setFilePath(uniqueFileName); | ||||
|             fileInfo.setFileMd5(fileMd5); | ||||
|  | ||||
|             // 保存文件信息并获取ID | ||||
| @ -152,7 +138,7 @@ public class FileInfoController { | ||||
|         } | ||||
|  | ||||
|         // 构建完整文件路径 | ||||
|         String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath(); | ||||
|         String fullPath = fileInfoService.getFullUploadPath() + File.separator + fileInfo.getFilePath(); | ||||
|         File file = new File(fullPath); | ||||
|  | ||||
|         // 校验文件是否存在 | ||||
| @ -185,7 +171,7 @@ public class FileInfoController { | ||||
|         } | ||||
|  | ||||
|         // 构建完整文件路径 | ||||
|         String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath(); | ||||
|         String fullPath = fileInfoService.getFullUploadPath() + File.separator + fileInfo.getFilePath(); | ||||
|         File file = new File(fullPath); | ||||
|  | ||||
|         // 校验文件是否存在 | ||||
| @ -204,100 +190,59 @@ public class FileInfoController { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public String handleLocationImageUpload(MultipartFile file) { | ||||
|     @Operation(summary = "本地文件预览") | ||||
|     @GetMapping("/previewLocal") | ||||
|     public void previewLocalFile(@Parameter(description = "本地文件绝对路径") @RequestParam String fileAbsolutePath, HttpServletResponse response) { | ||||
|         Path targetFilePath = null; | ||||
|         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<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 JsonUtil.mapToJson(result); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("文件上传失败: " + e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|             // 标准化路径 | ||||
|             targetFilePath = Paths.get(fileAbsolutePath).toRealPath(); | ||||
|  | ||||
|     /** | ||||
|      * 计算文件的 MD5 | ||||
|      */ | ||||
|     private String calculateFileMd5(File file) throws IOException { | ||||
|         try (InputStream is = new FileInputStream(file)) { | ||||
|             return DigestUtils.md5DigestAsHex(is); | ||||
|         } | ||||
|     } | ||||
|             // 校验文件合法性:是否存在、是否为普通文件 | ||||
|             BasicFileAttributes fileAttr = Files.readAttributes(targetFilePath, BasicFileAttributes.class); | ||||
|             if (!fileAttr.isRegularFile()) { | ||||
|                 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); | ||||
|                 fileInfoService.writeResponseMessage(response, "目标路径不是有效的文件"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|     /** | ||||
|      * 提取图片的EXIF元数据(包括定位信息) | ||||
|      */ | ||||
|     private Map<String, Object> extractImageMetadata(InputStream inputStream) { | ||||
|         try { | ||||
|             Map<String, Object> result = new HashMap<>(); | ||||
|             Metadata metadata = ImageMetadataReader.readMetadata(inputStream); | ||||
|             // 设置预览响应头 | ||||
|             String fileName = targetFilePath.getFileName().toString(); | ||||
|             String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); | ||||
|             String contentType = Files.probeContentType(targetFilePath); | ||||
|  | ||||
|             // 遍历所有元数据目录 | ||||
|             for (Directory directory : metadata.getDirectories()) { | ||||
|                 String directoryName = directory.getName(); | ||||
|                 Map<String, String> directoryTags = new HashMap<>(); | ||||
|             // 对于文本类型文件、指定字符编码 | ||||
|             if (contentType != null && contentType.startsWith("text/")) { | ||||
|                 response.setContentType(contentType + "; charset=UTF-8"); | ||||
|             } else { | ||||
|                 response.setContentType(contentType != null ? contentType : "application/octet-stream"); | ||||
|             } | ||||
|  | ||||
|                 // 提取当前目录下的所有标签 | ||||
|                 for (com.drew.metadata.Tag tag : directory.getTags()) { | ||||
|                     directoryTags.put(tag.getTagName(), tag.getDescription()); | ||||
|                 } | ||||
|  | ||||
|                 // 存储当前目录的所有标签 | ||||
|                 if (!directoryTags.isEmpty()) { | ||||
|                     result.put(directoryName, directoryTags); | ||||
|             response.setContentLengthLong(fileAttr.size()); | ||||
|             // 关键修改:将attachment改为inline实现预览 | ||||
|             response.setHeader(HttpHeaders.CONTENT_DISPOSITION, | ||||
|                     "inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); | ||||
|  | ||||
|             // 写入文件流 | ||||
|             try (InputStream inputStream = Files.newInputStream(targetFilePath); | ||||
|                  OutputStream outputStream = response.getOutputStream()) { | ||||
|                 byte[] buffer = new byte[1024 * 8]; | ||||
|                 int len; | ||||
|                 while ((len = inputStream.read(buffer)) != -1) { | ||||
|                     outputStream.write(buffer, 0, len); | ||||
|                 } | ||||
|                 outputStream.flush(); | ||||
|             } | ||||
|  | ||||
|             return result; | ||||
|         } catch (ImageProcessingException | IOException e) { | ||||
|             return Collections.emptyMap(); | ||||
|         } catch (NoSuchFileException e) { | ||||
|             response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||||
|             fileInfoService.writeResponseMessage(response, "文件不存在:" + fileAbsolutePath); | ||||
|         } catch (SecurityException e) { | ||||
|             response.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||||
|             fileInfoService.writeResponseMessage(response, "访问拒绝:无权限读取该文件"); | ||||
|         } catch (Exception e) { | ||||
|             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); | ||||
|             fileInfoService.writeResponseMessage(response, "预览失败:" + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,65 +0,0 @@ | ||||
| package com.yj.earth.business.controller; | ||||
|  | ||||
| import com.yj.earth.business.service.ModelService; | ||||
| import com.yj.earth.business.service.ModelTypeService; | ||||
| import com.yj.earth.common.util.ApiResponse; | ||||
| import com.yj.earth.datasource.DatabaseManager; | ||||
| import com.yj.earth.design.Model; | ||||
| import com.yj.earth.design.ModelType; | ||||
| import com.yj.earth.dto.model.CreateModelFileDto; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| 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 javax.annotation.Resource; | ||||
| import java.io.File; | ||||
| import java.sql.Connection; | ||||
| import java.sql.DriverManager; | ||||
|  | ||||
| @Tag(name = "模型相关") | ||||
| @RestController | ||||
| @RequestMapping("/model") | ||||
| public class ModelController { | ||||
|     @Resource | ||||
|     private ModelService modelService; | ||||
|     @Resource | ||||
|     private ModelTypeService modelTypeService; | ||||
|  | ||||
|     @Operation(summary = "创建模型库") | ||||
|     @PostMapping("/createModelFile") | ||||
|     public ApiResponse createModelFile(@RequestBody CreateModelFileDto createModelFileDto) { | ||||
|         try { | ||||
|             // 获取参数并处理文件路径 | ||||
|             String folderPath = createModelFileDto.getFolderPath(); | ||||
|             String modelFileName = createModelFileDto.getModelFileName() + ".model"; | ||||
|             // 创建文件夹(如果不存在) | ||||
|             File folder = new File(folderPath); | ||||
|             if (!folder.exists()) { | ||||
|                 boolean folderCreated = folder.mkdirs(); | ||||
|                 if (!folderCreated) { | ||||
|                     return ApiResponse.failure("无法创建文件夹: " + folderPath); | ||||
|                 } | ||||
|             } | ||||
|             // 构建完整文件路径 | ||||
|             String filePath = folderPath + File.separator + modelFileName; | ||||
|             // 加载 SQLite 驱动并创建数据库文件 | ||||
|             Class.forName("org.sqlite.JDBC"); | ||||
|             // SQLite会自动创建不存在的数据库文件 | ||||
|             try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + filePath)) { | ||||
|                 if (connection != null) { | ||||
|                     // 初始化表结构 | ||||
|                     DatabaseManager.createTablesForEntities(DatabaseManager.DatabaseType.SQLITE, ModelType.class, connection); | ||||
|                     DatabaseManager.createTablesForEntities(DatabaseManager.DatabaseType.SQLITE, Model.class, connection); | ||||
|                     return ApiResponse.success(null); | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             return ApiResponse.failure("创建模型库失败: " + e.getMessage()); | ||||
|         } | ||||
|         return ApiResponse.failure("未知错误"); | ||||
|     } | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package com.yj.earth.business.controller; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
|  | ||||
| @Tag(name = "模型类型管理") | ||||
| @RestController | ||||
| @RequestMapping("/modelType") | ||||
| public class ModelTypeController { | ||||
|  | ||||
| } | ||||
| @ -2,20 +2,8 @@ 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.*; | ||||
| import com.yj.earth.common.service.SourceDataGenerator; | ||||
| @ -24,6 +12,7 @@ import com.yj.earth.common.util.ApiResponse; | ||||
| import com.yj.earth.common.util.JsonUtil; | ||||
| import com.yj.earth.common.util.MapUtil; | ||||
| import com.yj.earth.dto.source.*; | ||||
| import com.yj.earth.params.Point; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| @ -34,9 +23,6 @@ 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; | ||||
| @ -62,12 +48,6 @@ public class SourceController { | ||||
|     @Resource | ||||
|     private FileInfoService fileInfoService; | ||||
|  | ||||
|     @Resource | ||||
|     private FileInfoController fileInfoControllerl; | ||||
|  | ||||
|     @Value("${file.upload.path}") | ||||
|     private String uploadPath; | ||||
|  | ||||
|     @PostMapping("/addDirectory") | ||||
|     @Operation(summary = "新增目录资源") | ||||
|     public ApiResponse addDirectory(@RequestBody AddDirectoryDto addDirectoryDto) { | ||||
| @ -223,23 +203,75 @@ public class SourceController { | ||||
|     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(value = "params", required = false) @Parameter(description = "参数") String params, | ||||
|                                            @RequestParam(value = "sourceType", required = false) @Parameter(description = "资源类型") String sourceType, | ||||
|                                            @RequestParam("files") @Parameter(description = "带有定位的图片文件", required = true) MultipartFile[] files) { | ||||
|         // 验证并转换参数 | ||||
|         sourceParamsValidator.validateAndConvert( | ||||
|                 sourceType, | ||||
|                 JsonUtil.jsonToMap(params) | ||||
|         ); | ||||
|  | ||||
|         List<Source> sources = new ArrayList<>(); | ||||
|         for (int i = 0; i < files.length; i++) { | ||||
|             String detail = fileInfoControllerl.handleLocationImageUpload(files[i]); | ||||
|             Map<String, Object> dataMap = fileInfoService.handleLocationImageUpload(files[i]); | ||||
|             // 构建并保存资源对象 | ||||
|             Source source = new Source(); | ||||
|             source.setId(ids.get(i)); | ||||
|             source.setSourceName(files[i].getOriginalFilename()); | ||||
|             source.setParentId(parentId); | ||||
|             source.setSourceType("locationImage"); | ||||
|             source.setSourceType(sourceType); | ||||
|             source.setTreeIndex(treeIndex); | ||||
|             source.setDetail(detail); | ||||
|  | ||||
|             // 转换为对象 | ||||
|             Point point = JsonUtil.mapToObject(JsonUtil.jsonToMap(params), Point.class); | ||||
|             point.setId(ids.get(i)); | ||||
|             Point.Position position = new Point.Position(); | ||||
|             point.setName(files[i].getOriginalFilename()); | ||||
|             point.getLabel().setText(files[i].getOriginalFilename()); | ||||
|             Object lonObj = dataMap.get("lon"); | ||||
|             if (lonObj != null && lonObj instanceof Double) { | ||||
|                 position.setLng((Double) lonObj); | ||||
|             } | ||||
|  | ||||
|             Object latObj = dataMap.get("lat"); | ||||
|             if (latObj != null && latObj instanceof Double) { | ||||
|                 position.setLat((Double) latObj); | ||||
|             } | ||||
|  | ||||
|             Object altObj = dataMap.get("alt"); | ||||
|             if (altObj != null && altObj instanceof Double) { | ||||
|                 position.setAlt((Double) altObj); | ||||
|             } | ||||
|             point.setPosition(position); | ||||
|  | ||||
|             if ("linkImage".equals(sourceType)) { | ||||
|                 // 设置地址 | ||||
|                 List<Point.Attribute.Link.LinkContent> list = new ArrayList<>(); | ||||
|                 Point.Attribute.Link.LinkContent linkContent = new Point.Attribute.Link.LinkContent(); | ||||
|                 linkContent.setName("带定位照片"); | ||||
|                 linkContent.setUrl(dataMap.get("url").toString()); | ||||
|                 list.add(linkContent); | ||||
|                 point.getAttribute().getLink().setContent(list); | ||||
|             } else { | ||||
|                 List<Point.Attribute.Vr.VrContent> list = new ArrayList<>(); | ||||
|                 Point.Attribute.Vr.VrContent vrContent = new Point.Attribute.Vr.VrContent(); | ||||
|                 vrContent.setName("带全景照片"); | ||||
|                 vrContent.setUrl(dataMap.get("url").toString()); | ||||
|                 list.add(vrContent); | ||||
|                 point.getAttribute().getVr().setContent(list); | ||||
|             } | ||||
|  | ||||
|             // 将 vrImage 转化为 JSON | ||||
|             source.setParams(JsonUtil.toJson(point)); | ||||
|             source.setIsShow(SHOW); | ||||
|             sourceService.save(source); | ||||
|             // 添加资源到该用户的角色下 | ||||
|             roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); | ||||
|  | ||||
|             sources.add(source); | ||||
|         } | ||||
|         return ApiResponse.success(null); | ||||
|         return ApiResponse.success(sources); | ||||
|     } | ||||
|  | ||||
|     @GetMapping("/type") | ||||
|  | ||||
| @ -38,7 +38,7 @@ public class TacticalCalculationController { | ||||
|             // 同向而行:时间 = 距离 / (速度差) | ||||
|             double speedDiff = Math.abs(input.getSpeed1() - input.getSpeed2()); | ||||
|             if (speedDiff <= 0) { | ||||
|                 throw new IllegalArgumentException("同向而行时,速度不能相等或后方速度小于前方"); | ||||
|                 throw new IllegalArgumentException("同向而行时、速度不能相等或后方速度小于前方"); | ||||
|             } | ||||
|             meetTimeHours = input.getInitialDistance() / speedDiff; | ||||
|         } else { | ||||
|  | ||||
| @ -1,49 +1,34 @@ | ||||
| package com.yj.earth.business.domain; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.FieldFill; | ||||
| import com.baomidou.mybatisplus.annotation.IdType; | ||||
| import com.baomidou.mybatisplus.annotation.TableField; | ||||
| import com.baomidou.mybatisplus.annotation.TableId; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
|  | ||||
| @Data | ||||
| public class Model implements Serializable { | ||||
|  | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class Model { | ||||
|     @Schema(description = "主键") | ||||
|     @TableId(value = "id", type = IdType.ASSIGN_UUID) | ||||
|     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 = "视图") | ||||
|     @Schema(description = "海报数据") | ||||
|     private String poster; | ||||
|     @Schema(description = "模型数据") | ||||
|     private String data; | ||||
|     @Schema(description = "模型视图") | ||||
|     private String view; | ||||
|  | ||||
|     @TableField(fill = FieldFill.INSERT) | ||||
|     @Schema(description = "创建时间") | ||||
|     private LocalDateTime createdAt; | ||||
|  | ||||
|     @TableField(fill = FieldFill.UPDATE) | ||||
|     @Schema(description = "更新时间") | ||||
|     private LocalDateTime updatedAt; | ||||
| } | ||||
|  | ||||
| @ -1,35 +1,20 @@ | ||||
| package com.yj.earth.business.domain; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.FieldFill; | ||||
| import com.baomidou.mybatisplus.annotation.IdType; | ||||
| import com.baomidou.mybatisplus.annotation.TableField; | ||||
| import com.baomidou.mybatisplus.annotation.TableId; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| @Data | ||||
| public class ModelType implements Serializable { | ||||
|  | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
| public class ModelType { | ||||
|     @Schema(description = "主键") | ||||
|     @TableId(value = "id", type = IdType.ASSIGN_UUID) | ||||
|     private String id; | ||||
|  | ||||
|     @Schema(description = "模型类型名称") | ||||
|     private String name; | ||||
|  | ||||
|     @Schema(description = "模型类型父级ID") | ||||
|     @Schema(description = "父级节点ID") | ||||
|     private String parentId; | ||||
|  | ||||
|     @Schema(description = "创建时间") | ||||
|     @TableField(fill = FieldFill.INSERT) | ||||
|     private LocalDateTime createdAt; | ||||
|  | ||||
|     @Schema(description = "更新时间") | ||||
|     @TableField(fill = FieldFill.UPDATE) | ||||
|     private LocalDateTime updatedAt; | ||||
| } | ||||
|  | ||||
| @ -1,18 +0,0 @@ | ||||
| package com.yj.earth.business.mapper; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  Mapper 接口 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Mapper | ||||
| public interface ModelMapper extends BaseMapper<Model> { | ||||
|  | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| package com.yj.earth.business.mapper; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  Mapper 接口 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Mapper | ||||
| public interface ModelTypeMapper extends BaseMapper<ModelType> { | ||||
|  | ||||
| } | ||||
| @ -2,8 +2,16 @@ package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.FileInfo; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| public interface FileInfoService extends IService<FileInfo> { | ||||
|     // 根据文件ID获取文件绝对路径 | ||||
|     String getFileAbsolutePath(String id); | ||||
|     Map<String, Object> handleLocationImageUpload(MultipartFile file); | ||||
|     String uploadWithPreview(MultipartFile file); | ||||
|     void writeResponseMessage(HttpServletResponse response, String message); | ||||
|     String getFullUploadPath(); | ||||
| } | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| public interface ModelService extends IService<Model> { | ||||
|  | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| public interface ModelTypeService extends IService<ModelType> { | ||||
|  | ||||
| } | ||||
| @ -1,21 +1,38 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import cn.hutool.core.io.FileUtil; | ||||
| import com.drew.imaging.ImageMetadataReader; | ||||
| import com.drew.lang.Rational; | ||||
| import com.drew.metadata.Metadata; | ||||
| import com.drew.metadata.exif.GpsDirectory; | ||||
| import com.yj.earth.business.domain.FileInfo; | ||||
| import com.yj.earth.business.mapper.FileInfoMapper; | ||||
| import com.yj.earth.business.service.FileInfoService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import com.yj.earth.datasource.DatabaseManager; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.util.DigestUtils; | ||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
|  | ||||
| @Service | ||||
| public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService { | ||||
|  | ||||
|     @Value("${file.upload.path}") | ||||
|     private String uploadPath; | ||||
|     private static final String DEFAULT_UPLOAD_PATH = "upload"; | ||||
|  | ||||
|     public String getFileAbsolutePath(String id) { | ||||
|  | ||||
| @ -26,7 +43,7 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i | ||||
|         } | ||||
|  | ||||
|         // 构建完整文件路径 | ||||
|         String fullPath = uploadPath + File.separator + fileInfo.getFilePath(); | ||||
|         String fullPath = DEFAULT_UPLOAD_PATH + File.separator + fileInfo.getFilePath(); | ||||
|         File file = new File(fullPath); | ||||
|  | ||||
|         // 校验文件是否存在 | ||||
| @ -37,4 +54,187 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i | ||||
|         // 获取并返回绝对路径 | ||||
|         return file.getAbsolutePath(); | ||||
|     } | ||||
|  | ||||
|     public Map<String, Object> handleLocationImageUpload(MultipartFile file) { | ||||
|         // 构建并返回结果 | ||||
|         Map<String, Object> result = new HashMap<>(); | ||||
|         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()); | ||||
|             // 提取图片元数据(使用已保存的文件、避免使用临时文件) | ||||
|             Map<String, Double> 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); | ||||
|             this.save(fileInfo); | ||||
|             result.put("url", "/fileInfo/preview/" + fileInfo.getId()); | ||||
|             result.put("lon", metadata.get("lon")); | ||||
|             result.put("lat", metadata.get("lat")); | ||||
|             result.put("alt", metadata.get("alt")); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("文件上传失败: " + e.getMessage(), e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public String uploadWithPreview(MultipartFile file) { | ||||
|         FileInfo fileInfo = new FileInfo(); | ||||
|         // 构建并返回结果 | ||||
|         Map<String, Object> result = new HashMap<>(); | ||||
|         try { | ||||
|             // 校验文件是否为空 | ||||
|             if (file.isEmpty()) { | ||||
|                 throw new IllegalArgumentException("上传文件不能为空"); | ||||
|             } | ||||
|             // 获取文件基本信息 | ||||
|             String originalFilename = file.getOriginalFilename(); | ||||
|             String fileSuffix = FileUtil.extName(originalFilename); | ||||
|             String contentType = file.getContentType(); | ||||
|             // 获取完整的上传目录路径 | ||||
|             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()); | ||||
|             // 保存文件信息到数据库 | ||||
|             fileInfo.setFileName(originalFilename); | ||||
|             fileInfo.setFileSuffix(fileSuffix); | ||||
|             fileInfo.setContentType(contentType); | ||||
|             fileInfo.setFileSize(file.getSize()); | ||||
|             fileInfo.setFilePath(uniqueFileName); | ||||
|             fileInfo.setFileMd5(fileMd5); | ||||
|             this.save(fileInfo); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("文件上传失败: " + e.getMessage(), e); | ||||
|         } | ||||
|         return "/fileInfo/preview/" + fileInfo.getId(); | ||||
|     } | ||||
|  | ||||
|     public void writeResponseMessage(HttpServletResponse response, String message) { | ||||
|         try { | ||||
|             response.setContentType("text/plain; charset=UTF-8"); | ||||
|             response.getWriter().write(message); | ||||
|             response.getWriter().flush(); | ||||
|         } catch (IOException e) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public String getFullUploadPath() { | ||||
|         // 拼接项目根目录和配置的上传路径 | ||||
|         return DatabaseManager.getRecommendedCacheDirectory() + File.separator + DEFAULT_UPLOAD_PATH; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 提取图片的EXIF元数据 | ||||
|      */ | ||||
|     public Map<String, Double> extractImageMetadata(InputStream inputStream) { | ||||
|         Map<String, Double> result = new HashMap<>(3); | ||||
|         try { | ||||
|             Metadata metadata = ImageMetadataReader.readMetadata(inputStream); | ||||
|             // 获取GPS相关元数据目录(图片的经纬度和高度通常存储在GPS目录中) | ||||
|             GpsDirectory gpsDirectory = metadata.getFirstDirectoryOfType(GpsDirectory.class); | ||||
|             if (gpsDirectory == null) { | ||||
|                 return result; | ||||
|             } | ||||
|  | ||||
|             // 解析纬度(度分秒转十进制) | ||||
|             Double latitude = parseLatitudeOrLongitude( | ||||
|                     gpsDirectory.getRationalArray(GpsDirectory.TAG_LATITUDE), | ||||
|                     gpsDirectory.getString(GpsDirectory.TAG_LATITUDE_REF) | ||||
|             ); | ||||
|  | ||||
|             // 解析经度(度分秒转十进制) | ||||
|             Double longitude = parseLatitudeOrLongitude( | ||||
|                     gpsDirectory.getRationalArray(GpsDirectory.TAG_LONGITUDE), | ||||
|                     gpsDirectory.getString(GpsDirectory.TAG_LONGITUDE_REF) | ||||
|             ); | ||||
|  | ||||
|             // 解析高度(考虑海拔参考、0表示海平面以上、1表示以下) | ||||
|             Double altitude = null; | ||||
|             if (gpsDirectory.containsTag(GpsDirectory.TAG_ALTITUDE)) { | ||||
|                 Rational altitudeRational = gpsDirectory.getRational(GpsDirectory.TAG_ALTITUDE); | ||||
|                 if (altitudeRational != null) { | ||||
|                     altitude = altitudeRational.doubleValue(); | ||||
|                     // 处理海拔参考(是否在海平面以下) | ||||
|                     if (gpsDirectory.containsTag(GpsDirectory.TAG_ALTITUDE_REF) && | ||||
|                             gpsDirectory.getInt(GpsDirectory.TAG_ALTITUDE_REF) == 1) { | ||||
|                         altitude = -altitude; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // 存入结果 Map | ||||
|             result.put("lat", latitude); | ||||
|             result.put("lon", longitude); | ||||
|             result.put("alt", altitude); | ||||
|         } catch (Exception e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将GPS的度分秒格式转换为十进制坐标 | ||||
|      */ | ||||
|     public Double parseLatitudeOrLongitude(Rational[] degreesMinutesSeconds, String ref) { | ||||
|         if (degreesMinutesSeconds == null || degreesMinutesSeconds.length != 3 || ref == null) { | ||||
|             return null; | ||||
|         } | ||||
|         // 度分秒转十进制:度 + 分/60 + 秒/3600 | ||||
|         double degrees = degreesMinutesSeconds[0].doubleValue(); | ||||
|         double minutes = degreesMinutesSeconds[1].doubleValue(); | ||||
|         double seconds = degreesMinutesSeconds[2].doubleValue(); | ||||
|         double value = degrees + (minutes / 60) + (seconds / 3600); | ||||
|         // 根据方向参考调整正负(南纬/西经为负) | ||||
|         if (ref.equalsIgnoreCase("S") || ref.equalsIgnoreCase("W")) { | ||||
|             value = -value; | ||||
|         } | ||||
|  | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 计算文件的 MD5 | ||||
|      */ | ||||
|     public String calculateFileMd5(File file) throws IOException { | ||||
|         try (InputStream is = new FileInputStream(file)) { | ||||
|             return DigestUtils.md5DigestAsHex(is); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import com.yj.earth.business.mapper.ModelMapper; | ||||
| import com.yj.earth.business.service.ModelService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务实现类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Service | ||||
| public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements ModelService { | ||||
|  | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import com.yj.earth.business.mapper.ModelTypeMapper; | ||||
| import com.yj.earth.business.service.ModelTypeService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务实现类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Service | ||||
| public class ModelTypeServiceImpl extends ServiceImpl<ModelTypeMapper, ModelType> implements ModelTypeService { | ||||
|  | ||||
| } | ||||
| @ -11,9 +11,6 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; | ||||
| public class GlobalExceptionHandler { | ||||
|     @ExceptionHandler(Exception.class) | ||||
|     public ApiResponse handleException(Exception e) { | ||||
|         if (!e.getMessage().contains("No static resource")) { | ||||
|             log.error("全局异常处理:{}", e.getMessage()); | ||||
|         } | ||||
|         return ApiResponse.failure(e.getMessage()); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -23,10 +23,10 @@ public class SaTokenConfig implements WebMvcConfigurer { | ||||
|         excludePathPatterns.add("/v3/api-docs/**"); | ||||
|         excludePathPatterns.add("/fileInfo/download/**"); | ||||
|         excludePathPatterns.add("/fileInfo/preview/**"); | ||||
|         excludePathPatterns.add("/fileInfo/previewLocal/**"); | ||||
|         excludePathPatterns.add("/data/clt/**"); | ||||
|         excludePathPatterns.add("/data/mbtiles/**"); | ||||
|         excludePathPatterns.add("/data/pak/**"); | ||||
|         excludePathPatterns.add("/**"); | ||||
|  | ||||
|         // 注册 Sa-Token 拦截器 | ||||
|         registry.addInterceptor(new SaInterceptor(handle -> { | ||||
|  | ||||
| @ -44,7 +44,6 @@ public class ServerInitService { | ||||
|  | ||||
|     public void checkDefaultData() { | ||||
|         checkDefaultUserAndRole(); | ||||
|         checkDefaultSource(); | ||||
|     } | ||||
|  | ||||
|     public void checkDefaultUserAndRole() { | ||||
| @ -73,98 +72,4 @@ public class ServerInitService { | ||||
|             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()); | ||||
|  | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -34,7 +34,7 @@ public class CodeUtil { | ||||
|         } | ||||
|  | ||||
|         // 传入需要生成代码的表名 | ||||
|         Generation("model"); | ||||
|         Generation("business_config"); | ||||
|     } | ||||
|  | ||||
|     public static void Generation(String... tableName) { | ||||
|  | ||||
							
								
								
									
										28
									
								
								src/main/java/com/yj/earth/common/util/FileUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/main/java/com/yj/earth/common/util/FileUtil.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| package com.yj.earth.common.util; | ||||
|  | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.mock.web.MockMultipartFile; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
|  | ||||
| public class FileUtil { | ||||
|  | ||||
|     /** | ||||
|      * 将本地文件转换为 MultipartFile | ||||
|      */ | ||||
|     public static MultipartFile convertToMultipartFile(File file) { | ||||
|         try (FileInputStream inputStream = new FileInputStream(file)) { | ||||
|             return new MockMultipartFile( | ||||
|                     "files", | ||||
|                     file.getName(), | ||||
|                     MediaType.APPLICATION_OCTET_STREAM_VALUE, | ||||
|                     inputStream | ||||
|             ); | ||||
|         } catch (IOException e) { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -47,4 +47,43 @@ public class JsonUtil { | ||||
|             return new HashMap<>(0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将 JSON 字符串转换为指定类型的对象 | ||||
|      */ | ||||
|     public static <T> T jsonToObject(String json, Class<T> clazz) { | ||||
|         if (json == null || json.trim().isEmpty()) { | ||||
|             return null; | ||||
|         } | ||||
|         return objectMapper.convertValue(json, clazz); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将 Map 转换为指定类型的对象 | ||||
|      */ | ||||
|     public static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) { | ||||
|         if (map == null || clazz == null) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             // 使用ObjectMapper将Map转换为指定类型对象 | ||||
|             return objectMapper.convertValue(map, clazz); | ||||
|         } catch (IllegalArgumentException e) { | ||||
|             log.error("Map转对象失败、目标类型: {}, Map内容: {}", clazz.getName(), map, e); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将任意 Object 转化为 JSON | ||||
|      */ | ||||
|     public static String toJson(Object obj) { | ||||
|         try { | ||||
|             return objectMapper.writeValueAsString(obj); | ||||
|         } catch (JsonProcessingException e) { | ||||
|             log.error("对象转JSON失败", e); | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										334
									
								
								src/main/java/com/yj/earth/common/util/SQLiteUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										334
									
								
								src/main/java/com/yj/earth/common/util/SQLiteUtil.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,334 @@ | ||||
| package com.yj.earth.common.util; | ||||
|  | ||||
| import java.sql.*; | ||||
| import java.util.*; | ||||
| import java.lang.reflect.*; | ||||
| import java.util.Date; | ||||
| import java.time.LocalDateTime; // 新增导入 | ||||
| import java.time.format.DateTimeFormatter; // 新增导入 | ||||
| import java.time.format.DateTimeParseException; // 新增导入 | ||||
|  | ||||
| public class SQLiteUtil { | ||||
|  | ||||
|     // 加载 SQLite JDBC 驱动 | ||||
|     static { | ||||
|         try { | ||||
|             Class.forName("org.sqlite.JDBC"); | ||||
|         } catch (ClassNotFoundException e) { | ||||
|             throw new RuntimeException("无法加载SQLite JDBC驱动", e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 统一日期格式(匹配数据库TEXT字段存储的格式:yyyy-MM-dd'T'HH:mm:ss.SSS) | ||||
|     private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; | ||||
|  | ||||
|     /** | ||||
|      * 根据数据库文件绝对路径获取连接 | ||||
|      */ | ||||
|     public static Connection getConnection(String dbFilePath) throws SQLException { | ||||
|         String url = "jdbc:sqlite:" + dbFilePath; | ||||
|         return DriverManager.getConnection(url); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行查询并返回单个对象 | ||||
|      */ | ||||
|     public static <T> T queryForObject(String dbFilePath, String sql, List<Object> params, Class<T> clazz) throws SQLException, IllegalAccessException, InstantiationException { | ||||
|         List<T> results = queryForList(dbFilePath, sql, params, clazz); | ||||
|         return results.isEmpty() ? null : results.get(0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行查询并返回对象列表 | ||||
|      */ | ||||
|     public static <T> List<T> queryForList(String dbFilePath, String sql, List<Object> params, Class<T> clazz) | ||||
|             throws SQLException, IllegalAccessException, InstantiationException { | ||||
|         List<T> resultList = new ArrayList<>(); | ||||
|  | ||||
|         // 使用try-with-resources确保资源自动关闭 | ||||
|         try (Connection conn = getConnection(dbFilePath); | ||||
|              PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { | ||||
|  | ||||
|             // 执行查询 | ||||
|             try (ResultSet rs = pstmt.executeQuery()) { | ||||
|                 ResultSetMetaData metaData = rs.getMetaData(); | ||||
|                 int columnCount = metaData.getColumnCount(); | ||||
|  | ||||
|                 // 处理结果集 | ||||
|                 while (rs.next()) { | ||||
|                     T obj = clazz.newInstance(); | ||||
|                     for (int i = 1; i <= columnCount; i++) { | ||||
|                         String columnName = metaData.getColumnName(i); | ||||
|                         Object value = rs.getObject(i); | ||||
|  | ||||
|                         // 设置对象属性(自动处理类型转换) | ||||
|                         setFieldValue(obj, columnName, value); | ||||
|                     } | ||||
|                     resultList.add(obj); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return resultList; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行增删改SQL语句 | ||||
|      */ | ||||
|     public static int executeUpdate(String dbFilePath, String sql, List<Object> params) throws SQLException { | ||||
|         try (Connection conn = getConnection(dbFilePath); | ||||
|              PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { | ||||
|             return pstmt.executeUpdate(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建并设置参数化的PreparedStatement(关键:处理LocalDateTime转String) | ||||
|      */ | ||||
|     private static PreparedStatement createPreparedStatement(Connection conn, String sql, List<Object> params) | ||||
|             throws SQLException { | ||||
|         PreparedStatement pstmt = conn.prepareStatement(sql); | ||||
|  | ||||
|         if (params != null && !params.isEmpty()) { | ||||
|             int index = 1; | ||||
|             for (Object value : params) { | ||||
|                 // 新增:LocalDateTime类型转为String、适配SQLite的TEXT字段 | ||||
|                 if (value instanceof LocalDateTime) { | ||||
|                     String dateStr = ((LocalDateTime) value).format(LOCAL_DATE_TIME_FORMATTER); | ||||
|                     pstmt.setObject(index++, dateStr); | ||||
|                 } | ||||
|                 // 新增:byte[]类型显式指定为BLOB(避免SQLite自动转换异常) | ||||
|                 else if (value instanceof byte[]) { | ||||
|                     pstmt.setBytes(index++, (byte[]) value); | ||||
|                 } else { | ||||
|                     pstmt.setObject(index++, value); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return pstmt; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 通过反射设置对象的字段值 | ||||
|      */ | ||||
|     private static void setFieldValue(Object obj, String columnName, Object value) { | ||||
|         try { | ||||
|             Field field = findField(obj.getClass(), columnName); | ||||
|             if (field != null) { | ||||
|                 field.setAccessible(true); | ||||
|                 Object convertedValue = convertValue(field.getType(), value); | ||||
|                 field.set(obj, convertedValue); | ||||
|             } | ||||
|         } catch (IllegalAccessException e) { | ||||
|             System.err.println("警告: 无法设置字段 " + columnName + " 的值 - " + e.getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 查找字段、支持下划线命名转驼峰命名(如created_at → createdAt) | ||||
|      */ | ||||
|     private static Field findField(Class<?> clazz, String columnName) { | ||||
|         // 1. 直接匹配字段名(如数据库列名与字段名一致) | ||||
|         try { | ||||
|             return clazz.getDeclaredField(columnName); | ||||
|         } catch (NoSuchFieldException e) { | ||||
|             // 2. 下划线转驼峰后匹配(如created_at → createdAt) | ||||
|             String camelCaseName = underscoreToCamelCase(columnName); | ||||
|             try { | ||||
|                 return clazz.getDeclaredField(camelCaseName); | ||||
|             } catch (NoSuchFieldException e1) { | ||||
|                 // 3. 递归查找父类(支持继承场景) | ||||
|                 if (clazz.getSuperclass() != null) { | ||||
|                     return findField(clazz.getSuperclass(), columnName); | ||||
|                 } | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 下划线命名转驼峰命名(工具方法) | ||||
|      */ | ||||
|     private static String underscoreToCamelCase(String str) { | ||||
|         if (str == null || str.isEmpty()) { | ||||
|             return str; | ||||
|         } | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         boolean nextUpperCase = false; | ||||
|         for (char c : str.toCharArray()) { | ||||
|             if (c == '_') { | ||||
|                 nextUpperCase = true; | ||||
|             } else { | ||||
|                 if (nextUpperCase) { | ||||
|                     sb.append(Character.toUpperCase(c)); | ||||
|                     nextUpperCase = false; | ||||
|                 } else { | ||||
|                     // 修正:首字母小写(如CREATED_AT → createdAt、而非CreatedAt) | ||||
|                     sb.append(Character.toLowerCase(c)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 核心:类型转换(新增LocalDateTime解析、优化BLOB适配) | ||||
|      */ | ||||
|     private static Object convertValue(Class<?> targetType, Object value) { | ||||
|         if (value == null) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // 类型已匹配、直接返回 | ||||
|         if (targetType.isInstance(value)) { | ||||
|             return value; | ||||
|         } | ||||
|  | ||||
|         // 1. 数字类型转换(int/long/double等) | ||||
|         if (targetType == Integer.class || targetType == int.class) { | ||||
|             return ((Number) value).intValue(); | ||||
|         } else if (targetType == Long.class || targetType == long.class) { | ||||
|             return ((Number) value).longValue(); | ||||
|         } else if (targetType == Double.class || targetType == double.class) { | ||||
|             return ((Number) value).doubleValue(); | ||||
|         } else if (targetType == Float.class || targetType == float.class) { | ||||
|             return ((Number) value).floatValue(); | ||||
|         } | ||||
|  | ||||
|         // 2. 布尔类型转换(支持数字/字符串转布尔) | ||||
|         else if (targetType == Boolean.class || targetType == boolean.class) { | ||||
|             if (value instanceof Number) { | ||||
|                 return ((Number) value).intValue() != 0; | ||||
|             } else if (value instanceof String) { | ||||
|                 return "true".equalsIgnoreCase((String) value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 3. 字符串类型转换(所有类型转String) | ||||
|         else if (targetType == String.class) { | ||||
|             return value.toString(); | ||||
|         } | ||||
|  | ||||
|         // 4. 日期类型转换(java.util.Date) | ||||
|         else if (targetType == Date.class) { | ||||
|             if (value instanceof Timestamp) { | ||||
|                 return new Date(((Timestamp) value).getTime()); | ||||
|             } else if (value instanceof LocalDateTime) { | ||||
|                 // 支持LocalDateTime转Date(如需) | ||||
|                 return Date.from(((LocalDateTime) value).atZone(java.time.ZoneId.systemDefault()).toInstant()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 5. 新增:LocalDateTime类型转换(SQLite TEXT → Java LocalDateTime) | ||||
|         else if (targetType == LocalDateTime.class) { | ||||
|             if (value instanceof String) { | ||||
|                 try { | ||||
|                     // 解析数据库存储的ISO格式字符串(如"2025-09-18T17:30:27.143") | ||||
|                     return LocalDateTime.parse((String) value, LOCAL_DATE_TIME_FORMATTER); | ||||
|                 } catch (DateTimeParseException e) { | ||||
|                     System.err.println("警告: 日期解析失败、字符串=" + value + "、格式应为yyyy-MM-dd'T'HH:mm:ss.SSS - " + e.getMessage()); | ||||
|                     return null; | ||||
|                 } | ||||
|             } else if (value instanceof Timestamp) { | ||||
|                 // 兼容Timestamp类型(如其他数据库迁移场景) | ||||
|                 return ((Timestamp) value).toLocalDateTime(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 6. 新增:byte[]类型转换(SQLite BLOB → Java byte[]) | ||||
|         else if (targetType == byte[].class && value instanceof Blob) { | ||||
|             Blob blob = (Blob) value; | ||||
|             try { | ||||
|                 return blob.getBytes(1, (int) blob.length()); | ||||
|             } catch (SQLException e) { | ||||
|                 System.err.println("警告: BLOB转byte[]失败 - " + e.getMessage()); | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 无法转换时返回原始值(避免崩溃、打印警告) | ||||
|         System.err.println("警告: 不支持的类型转换、目标类型=" + targetType.getName() + "、原始值类型=" + value.getClass().getName()); | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行DDL语句(CREATE, ALTER, DROP等) | ||||
|      */ | ||||
|     private static void executeDDL(String dbFilePath, String sql, List<Object> params) throws SQLException { | ||||
|         try (Connection conn = getConnection(dbFilePath); | ||||
|              PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { | ||||
|             pstmt.execute(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行无参数的DDL语句 | ||||
|      */ | ||||
|     public static void executeDDL(String dbFilePath, String sql) { | ||||
|         try { | ||||
|             executeDDL(dbFilePath, sql, null); | ||||
|         } catch (SQLException e) { | ||||
|             throw new RuntimeException("执行DDL语句失败、SQL=" + sql, e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行查询并返回count结果 | ||||
|      */ | ||||
|     public static int queryForCount(String dbFilePath, String sql, List<Object> params) throws SQLException { | ||||
|         try (Connection conn = getConnection(dbFilePath); | ||||
|              PreparedStatement pstmt = createPreparedStatement(conn, sql, params); | ||||
|              ResultSet rs = pstmt.executeQuery()) { | ||||
|  | ||||
|             if (rs.next()) { | ||||
|                 return rs.getInt(1); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行无参数的查询并返回count结果 | ||||
|      */ | ||||
|     public static int queryForCount(String dbFilePath, String sql) throws SQLException { | ||||
|         return queryForCount(dbFilePath, sql, null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 初始化数据库表(model_type + model) | ||||
|      */ | ||||
|     public static void initialization(String modelPath) { | ||||
|         // 创建模型类型表 | ||||
|         String sql = """ | ||||
|                     CREATE TABLE "model_type" ( | ||||
|                       "id" TEXT, | ||||
|                       "name" TEXT, | ||||
|                       "parent_id" TEXT, | ||||
|                       "created_at" TEXT, | ||||
|                       "updated_at" TEXT, | ||||
|                       PRIMARY KEY ("id") | ||||
|                     ); | ||||
|                 """; | ||||
|         executeDDL(modelPath, sql); | ||||
|  | ||||
|         // 创建模型表 | ||||
|         sql = """ | ||||
|                   CREATE TABLE "model" ( | ||||
|                     "id" TEXT, | ||||
|                     "model_type_id" TEXT, | ||||
|                     "model_name" TEXT, | ||||
|                     "model_type" TEXT, | ||||
|                     "poster_type" TEXT, | ||||
|                     "poster" TEXT, | ||||
|                     "data" TEXT, | ||||
|                     "view" TEXT, | ||||
|                     "created_at" TEXT, | ||||
|                     "updated_at" TEXT, | ||||
|                     PRIMARY KEY ("id") | ||||
|                   ); | ||||
|                 """; | ||||
|         executeDDL(modelPath, sql); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -47,8 +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); | ||||
|         classes.add(ModelLibrary.class); | ||||
|         classes.add(BusinessConfig.class); | ||||
|         ENTITY_CLASSES = Collections.unmodifiableList(classes); | ||||
|     } | ||||
|  | ||||
| @ -292,7 +292,7 @@ public class DatabaseManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static Path getRecommendedCacheDirectory() { | ||||
|     public static Path getRecommendedCacheDirectory() { | ||||
|         String os = System.getProperty("os.name").toLowerCase(); | ||||
|         if (os.contains("win")) { | ||||
|             String appData = System.getenv("APPDATA"); | ||||
|  | ||||
							
								
								
									
										24
									
								
								src/main/java/com/yj/earth/design/BusinessConfig.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/main/java/com/yj/earth/design/BusinessConfig.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| package com.yj.earth.design; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| @Data | ||||
| public class BusinessConfig { | ||||
|     @Schema(description = "主键") | ||||
|     private String id; | ||||
|  | ||||
|     @Schema(description = "键") | ||||
|     private String key; | ||||
|  | ||||
|     @Schema(description = "值") | ||||
|     private String value; | ||||
|  | ||||
|     @Schema(description = "创建时间") | ||||
|     private LocalDateTime createdAt; | ||||
|  | ||||
|     @Schema(description = "更新时间") | ||||
|     private LocalDateTime updatedAt; | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/main/java/com/yj/earth/design/ModelLibrary.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/main/java/com/yj/earth/design/ModelLibrary.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| package com.yj.earth.design; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| @Data | ||||
| public class ModelLibrary { | ||||
|     @Schema(description = "主键") | ||||
|     private String id; | ||||
|     @Schema(description = "模型库路径") | ||||
|     private String path; | ||||
|     @Schema(description = "模型库名称") | ||||
|     private String name; | ||||
|     @Schema(description = "是否启用") | ||||
|     private Integer isEnable; | ||||
|     @Schema(description = "创建时间") | ||||
|     private LocalDateTime createdAt; | ||||
|     @Schema(description = "更新时间") | ||||
|     private LocalDateTime updatedAt; | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| package com.yj.earth.dto.businessConfig; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| @Data | ||||
| public class AddBusinessConfigDto { | ||||
|     @Schema(description = "键") | ||||
|     private String key; | ||||
|  | ||||
|     @Schema(description = "值") | ||||
|     private String value; | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/main/java/com/yj/earth/dto/model/AddModelTypeDto.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/main/java/com/yj/earth/dto/model/AddModelTypeDto.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| package com.yj.earth.dto.model; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| @Data | ||||
| public class AddModelTypeDto { | ||||
|     @Schema(description = "模型类型名称") | ||||
|     private String name; | ||||
|     @Schema(description = "父级节点ID") | ||||
|     private String parentId; | ||||
| } | ||||
| @ -4,9 +4,9 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| 
 | ||||
| @Data | ||||
| public class CreateModelFileDto { | ||||
| public class CreateModelLibraryDto { | ||||
|     @Schema(description = "模型库文件名称") | ||||
|     private String modelFileName; | ||||
|     private String name; | ||||
|     @Schema(description = "生成文件夹路径") | ||||
|     private String folderPath; | ||||
|     private String path; | ||||
| } | ||||
							
								
								
									
										134
									
								
								src/main/java/com/yj/earth/params/LinkImage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/main/java/com/yj/earth/params/LinkImage.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| package com.yj.earth.params; | ||||
|  | ||||
| import com.yj.earth.annotation.SourceType; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @SourceType("linkImage") | ||||
| public class LinkImage { | ||||
|     private String id; | ||||
|     private boolean show; | ||||
|     private String name; | ||||
|     private Position position; | ||||
|     private int heightMode; | ||||
|     private boolean scaleByDistance; | ||||
|     private int near; | ||||
|     private int far; | ||||
|     private Billboard billboard; | ||||
|     private Label label; | ||||
|     private Attribute attribute; | ||||
|     private String richTextContent; | ||||
|     private CustomView customView; | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Position { | ||||
|         private double lng; | ||||
|         private double lat; | ||||
|         private double alt; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Billboard { | ||||
|         private boolean show; | ||||
|         private String image; | ||||
|         private String defaultImage; | ||||
|         private int scale; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Label { | ||||
|         private String text; | ||||
|         private boolean show; | ||||
|         private int fontFamily; | ||||
|         private int fontSize; | ||||
|         private String color; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Attribute { | ||||
|         private Link link; | ||||
|         private Vr vr; | ||||
|         private Camera camera; | ||||
|         private Isc isc; | ||||
|         private Goods goods; | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Link { | ||||
|             private List<LinkContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class LinkContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Vr { | ||||
|             private List<VrContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class VrContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Camera { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Isc { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Goods { | ||||
|             private List<GoodsContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class GoodsContent { | ||||
|                 private String id; | ||||
|                 private String name; | ||||
|                 private String cnt; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class CustomView { | ||||
|         private Orientation orientation; | ||||
|         private RelativePosition relativePosition; | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Orientation { | ||||
|             private double heading; | ||||
|             private double pitch; | ||||
|             private double roll; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class RelativePosition { | ||||
|             private double lng; | ||||
|             private double lat; | ||||
|             private double alt; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -2,9 +2,12 @@ package com.yj.earth.params; | ||||
|  | ||||
| import com.yj.earth.annotation.SourceType; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @SourceType("point") | ||||
| public class Point { | ||||
|     private String id; | ||||
| @ -22,6 +25,7 @@ public class Point { | ||||
|     private CustomView customView; | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Position { | ||||
|         private double lng; | ||||
|         private double lat; | ||||
| @ -29,6 +33,7 @@ public class Point { | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Billboard { | ||||
|         private boolean show; | ||||
|         private String image; | ||||
| @ -37,6 +42,7 @@ public class Point { | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Label { | ||||
|         private String text; | ||||
|         private boolean show; | ||||
| @ -46,6 +52,7 @@ public class Point { | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Attribute { | ||||
|         private Link link; | ||||
|         private Vr vr; | ||||
| @ -54,9 +61,11 @@ public class Point { | ||||
|         private Goods goods; | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Link { | ||||
|             private List<LinkContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class LinkContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
| @ -64,9 +73,11 @@ public class Point { | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Vr { | ||||
|             private List<VrContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class VrContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
| @ -74,19 +85,23 @@ public class Point { | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Camera { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Isc { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Goods { | ||||
|             private List<GoodsContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class GoodsContent { | ||||
|                 private String id; | ||||
|                 private String name; | ||||
| @ -96,10 +111,12 @@ public class Point { | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class CustomView { | ||||
|         private Orientation orientation; | ||||
|         private RelativePosition relativePosition; | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Orientation { | ||||
|             private double heading; | ||||
|             private double pitch; | ||||
| @ -107,6 +124,7 @@ public class Point { | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class RelativePosition { | ||||
|             private double lng; | ||||
|             private double lat; | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/main/java/com/yj/earth/params/Terrain.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/main/java/com/yj/earth/params/Terrain.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| package com.yj.earth.params; | ||||
|  | ||||
| import com.yj.earth.annotation.SourceType; | ||||
| import lombok.Data; | ||||
|  | ||||
| @Data | ||||
| @SourceType("terrain") | ||||
| public class Terrain { | ||||
|     private String id; | ||||
|     private String name; | ||||
|     private boolean show; | ||||
| } | ||||
							
								
								
									
										134
									
								
								src/main/java/com/yj/earth/params/VrImage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/main/java/com/yj/earth/params/VrImage.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| package com.yj.earth.params; | ||||
|  | ||||
| import com.yj.earth.annotation.SourceType; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @SourceType("vrImage") | ||||
| public class VrImage { | ||||
|     private String id; | ||||
|     private boolean show; | ||||
|     private String name; | ||||
|     private Position position; | ||||
|     private int heightMode; | ||||
|     private boolean scaleByDistance; | ||||
|     private int near; | ||||
|     private int far; | ||||
|     private Billboard billboard; | ||||
|     private Label label; | ||||
|     private Attribute attribute; | ||||
|     private String richTextContent; | ||||
|     private CustomView customView; | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Position { | ||||
|         private double lng; | ||||
|         private double lat; | ||||
|         private double alt; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Billboard { | ||||
|         private boolean show; | ||||
|         private String image; | ||||
|         private String defaultImage; | ||||
|         private int scale; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Label { | ||||
|         private String text; | ||||
|         private boolean show; | ||||
|         private int fontFamily; | ||||
|         private int fontSize; | ||||
|         private String color; | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class Attribute { | ||||
|         private Link link; | ||||
|         private Vr vr; | ||||
|         private Camera camera; | ||||
|         private Isc isc; | ||||
|         private Goods goods; | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Link { | ||||
|             private List<LinkContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class LinkContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Vr { | ||||
|             private List<VrContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class VrContent { | ||||
|                 private String name; | ||||
|                 private String url; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Camera { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Isc { | ||||
|             private List<Object> content; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Goods { | ||||
|             private List<GoodsContent> content; | ||||
|             @Data | ||||
|             @NoArgsConstructor | ||||
|             public static class GoodsContent { | ||||
|                 private String id; | ||||
|                 private String name; | ||||
|                 private String cnt; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     public static class CustomView { | ||||
|         private Orientation orientation; | ||||
|         private RelativePosition relativePosition; | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class Orientation { | ||||
|             private double heading; | ||||
|             private double pitch; | ||||
|             private double roll; | ||||
|         } | ||||
|  | ||||
|         @Data | ||||
|         @NoArgsConstructor | ||||
|         public static class RelativePosition { | ||||
|             private double lng; | ||||
|             private double lat; | ||||
|             private double alt; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/main/java/com/yj/earth/vo/ModelTypeVo.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/main/java/com/yj/earth/vo/ModelTypeVo.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| package com.yj.earth.vo; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @Data | ||||
| @AllArgsConstructor | ||||
| @NoArgsConstructor | ||||
| public class ModelTypeVo { | ||||
|     @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; | ||||
|     @Schema(description = "子节点列表") | ||||
|     private List<ModelTypeVo> children = new ArrayList<>(); | ||||
|  | ||||
|     public ModelTypeVo(ModelType modelType) { | ||||
|         this.id = modelType.getId(); | ||||
|         this.name = modelType.getName(); | ||||
|         this.parentId = modelType.getParentId(); | ||||
|         this.createdAt = modelType.getCreatedAt(); | ||||
|         this.updatedAt = modelType.getUpdatedAt(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/main/java/com/yj/earth/vo/ModelVo.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/main/java/com/yj/earth/vo/ModelVo.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| package com.yj.earth.vo; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import lombok.Data; | ||||
|  | ||||
| @Data | ||||
| public class ModelVo extends Model { | ||||
|     private String modelTypeName; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 ZZX9599
					ZZX9599