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