资源相关
This commit is contained in:
7
pom.xml
7
pom.xml
@ -149,6 +149,13 @@
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 图片元数据信息 -->
|
||||
<dependency>
|
||||
<groupId>com.drewnoakes</groupId>
|
||||
<artifactId>metadata-extractor</artifactId>
|
||||
<version>2.18.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- HttpClient5 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
|
||||
@ -49,6 +49,7 @@ public class ServerApp implements CommandLineRunner {
|
||||
serverInitService.init();
|
||||
// 检查默认数据
|
||||
serverInitService.checkDefaultData();
|
||||
// 打印项目文档地址
|
||||
log.info("项目文档地址: {}", "http://" + serverConfig.getHost() + ":" + serverConfig.getPort() + "/doc.html");
|
||||
}
|
||||
}
|
||||
|
||||
12
src/main/java/com/yj/earth/annotation/CheckAuth.java
Normal file
12
src/main/java/com/yj/earth/annotation/CheckAuth.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.yj.earth.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 授权验证注解
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CheckAuth {
|
||||
}
|
||||
99
src/main/java/com/yj/earth/aspect/AuthAspect.java
Normal file
99
src/main/java/com/yj/earth/aspect/AuthAspect.java
Normal file
@ -0,0 +1,99 @@
|
||||
package com.yj.earth.aspect;
|
||||
|
||||
import com.yj.earth.auth.AuthInfo;
|
||||
import com.yj.earth.auth.AuthValidator;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 授权验证切面
|
||||
* 实现授权验证注解的功能
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class AuthAspect {
|
||||
|
||||
// 授权文件路径
|
||||
private static final String LICENSE_FILE_PATH = "license/yjearth.lic";
|
||||
|
||||
@Pointcut("@annotation(com.yj.earth.annotation.CheckAuth)")
|
||||
public void authPointCut() {
|
||||
}
|
||||
|
||||
@Around("authPointCut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
// 读取授权文件内容
|
||||
String authString = readLicenseFile();
|
||||
|
||||
// 检查授权文件是否存在
|
||||
if (authString == null || authString.trim().isEmpty()) {
|
||||
return ApiResponse.failure("未导入授权");
|
||||
}
|
||||
|
||||
// 获取当前服务器硬件信息
|
||||
String currentServerHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId();
|
||||
|
||||
// 验证授权是否有效
|
||||
boolean isValid = AuthValidator.validateAuth(authString, currentServerHardwareMd5);
|
||||
if (!isValid) {
|
||||
// 进一步判断是未授权还是已过期
|
||||
try {
|
||||
AuthInfo authInfo = AuthValidator.getAuthInfo(authString);
|
||||
// 检查是否过期
|
||||
if (new java.util.Date().after(authInfo.getExpireTime())) {
|
||||
return ApiResponse.failure("授权已过期");
|
||||
} else {
|
||||
return ApiResponse.failure("未授权");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("未授权");
|
||||
}
|
||||
}
|
||||
|
||||
// 授权有效,继续执行原方法
|
||||
return point.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取授权文件内容
|
||||
* @return 授权文件内容,如果文件不存在或读取失败则返回null
|
||||
*/
|
||||
private String readLicenseFile() {
|
||||
try {
|
||||
// 尝试从类路径下读取
|
||||
ClassPathResource resource = new ClassPathResource(LICENSE_FILE_PATH);
|
||||
if (resource.exists()) {
|
||||
try (FileInputStream inputStream = new FileInputStream(resource.getFile())) {
|
||||
byte[] bytes = new byte[inputStream.available()];
|
||||
inputStream.read(bytes);
|
||||
return new String(bytes, StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从文件系统读取
|
||||
File file = new File(LICENSE_FILE_PATH);
|
||||
if (file.exists() && file.isFile()) {
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
byte[] bytes = new byte[inputStream.available()];
|
||||
inputStream.read(bytes);
|
||||
return new String(bytes, StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ package com.yj.earth.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<FileInfo> page = new Page<>(pageNum, pageSize);
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<FileInfo> 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<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileName, originalFilename)
|
||||
.eq(FileInfo::getFileMd5, fileMd5);
|
||||
if (fileInfoService.count(queryWrapper) > 0) {
|
||||
throw new IllegalStateException("已存在相同的图片文件");
|
||||
}
|
||||
// 提取图片元数据(使用已保存的文件,避免使用临时文件)
|
||||
Map<String, Object> metadata;
|
||||
try (InputStream is = Files.newInputStream(destFilePath)) {
|
||||
metadata = extractImageMetadata(is);
|
||||
}
|
||||
// 保存文件信息到数据库
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
fileInfo.setFileName(originalFilename);
|
||||
fileInfo.setFileSuffix(fileSuffix);
|
||||
fileInfo.setContentType(contentType);
|
||||
fileInfo.setFileSize(file.getSize());
|
||||
fileInfo.setFilePath(uniqueFileName);
|
||||
fileInfo.setFileMd5(fileMd5);
|
||||
fileInfoService.save(fileInfo);
|
||||
// 构建并返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("previewUrl", "/fileInfo/preview/" + fileInfo.getId());
|
||||
result.put("downloadUrl", "/fileInfo/download/" + fileInfo.getId());
|
||||
result.put("metadata", metadata);
|
||||
return JsonMapConverter.mapToJson(result);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
|
||||
}
|
||||
// 按创建时间倒序排列、最新上传的文件在前
|
||||
queryWrapper.orderByDesc(FileInfo::getCreatedAt);
|
||||
}
|
||||
|
||||
// 执行分页查询
|
||||
IPage<FileInfo> fileInfoPage = fileInfoService.page(page, queryWrapper);
|
||||
/**
|
||||
* 计算文件的 MD5
|
||||
*/
|
||||
private String calculateFileMd5(File file) throws IOException {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
return DigestUtils.md5DigestAsHex(is);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为VO对象并设置URL
|
||||
List<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> {
|
||||
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<String, Object> extractImageMetadata(InputStream inputStream) {
|
||||
try {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
|
||||
|
||||
// 构建分页结果
|
||||
Page<FileInfoVo> 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<String, String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<FileInfo> 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<Point> 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());
|
||||
|
||||
@ -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<String> ids,
|
||||
@RequestParam(value = "parentId", required = false) @Parameter(description = "父节点ID") String parentId,
|
||||
@RequestParam(value = "treeIndex", required = false) @Parameter(description = "树状索引") Integer treeIndex,
|
||||
@RequestParam("files") @Parameter(description = "带有定位的图片文件", required = true) MultipartFile[] files) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
String detail = fileInfoControllerl.handleLocationImageUpload(files[i]);
|
||||
// 构建并保存资源对象
|
||||
Source source = new Source();
|
||||
source.setId(ids.get(i));
|
||||
source.setSourceName(files[i].getOriginalFilename());
|
||||
source.setParentId(parentId);
|
||||
source.setSourceType("locationImage");
|
||||
source.setTreeIndex(treeIndex);
|
||||
source.setDetail(detail);
|
||||
source.setIsShow(SHOW);
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
}
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/type")
|
||||
@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<Source>()
|
||||
.eq(Source::getSourceName, sourceName)
|
||||
.eq(parentId != null, Source::getParentId, parentId)
|
||||
.isNull(parentId == null, Source::getParentId));
|
||||
if (existingSource != null) {
|
||||
return existingSource;
|
||||
}
|
||||
// 不存在则创建新资源
|
||||
try {
|
||||
Source newSource = new Source();
|
||||
newSource.setId(cn.hutool.core.lang.UUID.fastUUID().toString(true));
|
||||
newSource.setSourceName(sourceName);
|
||||
newSource.setSourceType(sourceType);
|
||||
newSource.setParentId(parentId);
|
||||
newSource.setTreeIndex(treeIndex);
|
||||
newSource.setIsShow(isShow);
|
||||
sourceService.save(newSource);
|
||||
// 关联角色资源
|
||||
roleSourceService.addRoleSource(roleId, newSource.getId());
|
||||
return newSource;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,436 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Tag(name = "战斗计算相关")
|
||||
@RestController
|
||||
@RequestMapping("/api/tactical")
|
||||
public class TacticalCalculationController {
|
||||
|
||||
// 日期时间格式化器
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@PostMapping("/meet")
|
||||
@Operation(summary = "计算相遇结果")
|
||||
public ApiResponse calculateMeet(@Valid @RequestBody MeetInputDTO input) {
|
||||
MeetResultDTO result = new MeetResultDTO();
|
||||
|
||||
// 计算相遇时间(小时)
|
||||
double meetTimeHours;
|
||||
if (input.isSameDirection()) {
|
||||
// 同向而行:时间 = 距离 / (速度差)
|
||||
double speedDiff = Math.abs(input.getSpeed1() - input.getSpeed2());
|
||||
if (speedDiff <= 0) {
|
||||
throw new IllegalArgumentException("同向而行时,速度不能相等或后方速度小于前方");
|
||||
}
|
||||
meetTimeHours = input.getInitialDistance() / speedDiff;
|
||||
} else {
|
||||
// 相向而行:时间 = 距离 / (速度和)
|
||||
meetTimeHours = input.getInitialDistance() / (input.getSpeed1() + input.getSpeed2());
|
||||
}
|
||||
|
||||
// 格式化时间结果
|
||||
result.setMeetTime(formatDuration(Duration.ofHours((long) meetTimeHours)
|
||||
.plusMinutes((long) ((meetTimeHours % 1) * 60))));
|
||||
|
||||
// 计算相遇点距离
|
||||
result.setMeetDistanceFrom1(input.getSpeed1() * meetTimeHours);
|
||||
result.setMeetDistanceFrom2(input.getSpeed2() * meetTimeHours);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/pursuit")
|
||||
@Operation(summary = "计算追击结果")
|
||||
public ApiResponse calculatePursuit(@Valid @RequestBody PursuitInputDTO input) {
|
||||
PursuitResultDTO result = new PursuitResultDTO();
|
||||
|
||||
// 检查速度合理性
|
||||
if (input.getPursuerSpeed() <= input.getTargetSpeed()) {
|
||||
throw new IllegalArgumentException("追击速度必须大于目标速度");
|
||||
}
|
||||
|
||||
// 计算追击时间(小时)
|
||||
double pursuitTimeHours = input.getDistance() / (input.getPursuerSpeed() - input.getTargetSpeed());
|
||||
result.setPursuitTime(formatDuration(Duration.ofHours((long) pursuitTimeHours)
|
||||
.plusMinutes((long) ((pursuitTimeHours % 1) * 60))));
|
||||
|
||||
// 计算追击距离
|
||||
result.setPursuitDistance(input.getPursuerSpeed() * pursuitTimeHours);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/formation/person")
|
||||
@Operation(summary = "计算人员队列长度")
|
||||
public ApiResponse calculatePersonFormation(@Valid @RequestBody PersonFormationInputDTO input) {
|
||||
FormationLengthResultDTO result = new FormationLengthResultDTO();
|
||||
|
||||
// 人员队列长度 = (人数 - 1) * 间距
|
||||
if (input.getPersonCount() <= 1) {
|
||||
result.setTotalLength(0.0);
|
||||
} else {
|
||||
result.setTotalLength((input.getPersonCount() - 1) * input.getDistanceBetween());
|
||||
}
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/formation/vehicle")
|
||||
@Operation(summary = "计算车辆队列长度")
|
||||
public ApiResponse calculateVehicleFormation(@Valid @RequestBody VehicleFormationInputDTO input) {
|
||||
FormationLengthResultDTO result = new FormationLengthResultDTO();
|
||||
|
||||
// 车辆队列长度 = (单辆车长 * 数量) + (间距 * (数量 - 1))
|
||||
if (input.getVehicleCount() <= 0) {
|
||||
result.setTotalLength(0.0);
|
||||
} else if (input.getVehicleCount() == 1) {
|
||||
result.setTotalLength(input.getVehicleLength());
|
||||
} else {
|
||||
result.setTotalLength((input.getVehicleLength() * input.getVehicleCount()) +
|
||||
(input.getDistanceBetween() * (input.getVehicleCount() - 1)));
|
||||
}
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/formation/gun")
|
||||
@Operation(summary = "计算火炮队列长度")
|
||||
public ApiResponse calculateGunFormation(@Valid @RequestBody GunFormationInputDTO input) {
|
||||
FormationLengthResultDTO result = new FormationLengthResultDTO();
|
||||
|
||||
// 火炮本身长度
|
||||
double gunTotalLength = input.getGunLength() * input.getGunCount();
|
||||
// 牵引车辆长度
|
||||
double vehicleTotalLength = input.getVehicleLength() * input.getVehicleCount();
|
||||
// 车辆间距
|
||||
double vehicleDistance = input.getVehicleDistance() * (input.getVehicleCount() - 1);
|
||||
|
||||
// 总长度
|
||||
result.setTotalLength(gunTotalLength + vehicleTotalLength + vehicleDistance);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/march/time")
|
||||
@Operation(summary = "计算行军时间")
|
||||
public ApiResponse calculateMarchTime(@Valid @RequestBody MarchTimeInputDTO input) {
|
||||
MarchTimeResultDTO result = new MarchTimeResultDTO();
|
||||
|
||||
// 总行军距离 = 行军距离 + 部队长度(需要完全通过)
|
||||
double totalDistance = input.getMarchDistance() + input.getFormationLength();
|
||||
|
||||
// 行军时间(小时)= 总距离 / 速度 + 等待时间
|
||||
double marchTimeHours = (totalDistance / input.getSpeed()) + input.getWaitHours();
|
||||
|
||||
// 格式化行军时间
|
||||
result.setTotalMarchTime(formatDuration(Duration.ofHours((long) marchTimeHours)
|
||||
.plusMinutes((long) ((marchTimeHours % 1) * 60))));
|
||||
|
||||
// 计算出发时间(如果提供了要求抵达时间)
|
||||
if (input.getRequiredArriveTime() != null) {
|
||||
long hours = (long) marchTimeHours;
|
||||
long minutes = (long) ((marchTimeHours - hours) * 60);
|
||||
LocalDateTime departTime = input.getRequiredArriveTime()
|
||||
.minusHours(hours)
|
||||
.minusMinutes(minutes);
|
||||
result.setDepartureTime(departTime.format(DATE_TIME_FORMATTER));
|
||||
}
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/material/consumption")
|
||||
@Operation(summary = "计算物资消耗")
|
||||
public ApiResponse calculateMaterialConsumption(@Valid @RequestBody MaterialConsumptionInputDTO input) {
|
||||
MaterialConsumptionResultDTO result = new MaterialConsumptionResultDTO();
|
||||
|
||||
switch (input.getMaterialType()) {
|
||||
case AMMUNITION:
|
||||
// 弹药消耗 = 单位基数 * 装备数量 * 时间
|
||||
result.setTotalConsumption(input.getBaseConsumption() * input.getEquipmentCount() * input.getHours());
|
||||
break;
|
||||
case OIL:
|
||||
// 油料消耗 = 单位时间耗油量 * 装备数量 * 时间
|
||||
result.setTotalConsumption(input.getBaseConsumption() * input.getEquipmentCount() * input.getHours());
|
||||
break;
|
||||
case WEAPON:
|
||||
// 武器需求 = 总需求 / (单装备配备数)
|
||||
result.setTotalConsumption(Math.ceil(input.getTotalRequirement() / input.getBaseConsumption()));
|
||||
break;
|
||||
}
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/unit/formation")
|
||||
@Operation(summary = "计算部队编成")
|
||||
public ApiResponse calculateUnitFormation(@Valid @RequestBody UnitFormationInputDTO input) {
|
||||
UnitFormationResultDTO result = new UnitFormationResultDTO();
|
||||
|
||||
// 计算编成数量
|
||||
result.setFormationCount(Math.ceil(input.getTotalStrength() / input.getUnitStrength()));
|
||||
|
||||
// 计算最后一个编成的兵力
|
||||
double lastFormationStrength = input.getTotalStrength() % input.getUnitStrength();
|
||||
if (lastFormationStrength <= 0) {
|
||||
lastFormationStrength = input.getUnitStrength();
|
||||
}
|
||||
result.setLastFormationStrength(lastFormationStrength);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/special/crossRiver")
|
||||
@Operation(summary = "计算渡河时间")
|
||||
public ApiResponse calculateCrossRiverTime(@Valid @RequestBody CrossRiverInputDTO input) {
|
||||
SpecialTimeResultDTO result = new SpecialTimeResultDTO();
|
||||
|
||||
// 计算批次数量
|
||||
double batchCount = Math.ceil(input.getPersonCount() / input.getBatchCapacity());
|
||||
|
||||
// 总时间 = 批次 * 单批次时间 + 准备时间
|
||||
double totalHours = (batchCount * input.getBatchHours()) + input.getPrepareHours();
|
||||
|
||||
// 格式化结果
|
||||
result.setTotalTime(formatDuration(Duration.ofHours((long) totalHours)
|
||||
.plusMinutes((long) ((totalHours % 1) * 60))));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/loss/rate")
|
||||
@Operation(summary = "计算损失率")
|
||||
public ApiResponse calculateLossRate(@Valid @RequestBody LossRateInputDTO input) {
|
||||
LossRateResultDTO result = new LossRateResultDTO();
|
||||
|
||||
if (input.getTotalStrength() <= 0) {
|
||||
throw new IllegalArgumentException("总兵力必须大于0");
|
||||
}
|
||||
|
||||
// 计算损失率
|
||||
result.setLossRate((input.getLostStrength() / input.getTotalStrength()) * 100);
|
||||
|
||||
// 计算剩余兵力
|
||||
result.setRemainingStrength(input.getTotalStrength() - input.getLostStrength());
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
// 格式化Duration为"X天X时X分X秒"
|
||||
private String formatDuration(Duration duration) {
|
||||
long days = duration.toDays();
|
||||
long hours = duration.toHours() % 24;
|
||||
long minutes = duration.toMinutes() % 60;
|
||||
long seconds = duration.getSeconds() % 60;
|
||||
|
||||
return String.format("%d天%d时%d分%d秒", days, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
// 解析时间字符串为Duration
|
||||
private Duration parseTimeString(String timeStr) {
|
||||
Pattern pattern = Pattern.compile("(?<d>\\d+)天(?<h>\\d+)时(?<m>\\d+)分(?<s>\\d+)秒");
|
||||
Matcher matcher = pattern.matcher(timeStr);
|
||||
|
||||
if (matcher.matches()) {
|
||||
int days = Integer.parseInt(matcher.group("d"));
|
||||
int hours = Integer.parseInt(matcher.group("h"));
|
||||
int minutes = Integer.parseInt(matcher.group("m"));
|
||||
int seconds = Integer.parseInt(matcher.group("s"));
|
||||
|
||||
return Duration.ofDays(days)
|
||||
.plusHours(hours)
|
||||
.plusMinutes(minutes)
|
||||
.plusSeconds(seconds);
|
||||
}
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
public static class MeetInputDTO {
|
||||
@Schema(description = "初始距离")
|
||||
private double initialDistance;
|
||||
@Schema(description = "速度1")
|
||||
private double speed1;
|
||||
@Schema(description = "速度2")
|
||||
private double speed2;
|
||||
@Schema(description = "是否同向")
|
||||
private boolean isSameDirection;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MeetResultDTO {
|
||||
@Schema(description = "相遇时间")
|
||||
private String meetTime;
|
||||
@Schema(description = "距离1的距离")
|
||||
private double meetDistanceFrom1;
|
||||
@Schema(description = "距离2的距离")
|
||||
private double meetDistanceFrom2;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class PursuitInputDTO {
|
||||
@Schema(description = "初始距离")
|
||||
private double distance;
|
||||
@Schema(description = "追击者速度")
|
||||
private double pursuerSpeed;
|
||||
@Schema(description = "目标速度")
|
||||
private double targetSpeed;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class PursuitResultDTO {
|
||||
@Schema(description = "追击时间")
|
||||
private String pursuitTime;
|
||||
@Schema(description = "追击距离")
|
||||
private double pursuitDistance;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class PersonFormationInputDTO {
|
||||
@Schema(description = "人数")
|
||||
private int personCount;
|
||||
@Schema(description = "人间距")
|
||||
private double distanceBetween;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class VehicleFormationInputDTO {
|
||||
@Schema(description = "车辆数量")
|
||||
private int vehicleCount;
|
||||
@Schema(description = "车辆长")
|
||||
private double vehicleLength;
|
||||
@Schema(description = "车辆距")
|
||||
private double distanceBetween;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class GunFormationInputDTO {
|
||||
@Schema(description = "火炮数量")
|
||||
private int gunCount;
|
||||
@Schema(description = "单门炮长")
|
||||
private double gunLength;
|
||||
@Schema(description = "牵引车辆数量")
|
||||
private int vehicleCount;
|
||||
@Schema(description = "单辆车长")
|
||||
private double vehicleLength;
|
||||
@Schema(description = "车距")
|
||||
private double vehicleDistance;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class FormationLengthResultDTO {
|
||||
@Schema(description = "总长度")
|
||||
private double totalLength;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MarchTimeInputDTO {
|
||||
@Schema(description = "行军距离")
|
||||
private double marchDistance;
|
||||
@Schema(description = "部队长度")
|
||||
private double formationLength;
|
||||
@Schema(description = "速度")
|
||||
private double speed;
|
||||
@Schema(description = "等待时间(小时)")
|
||||
private double waitHours;
|
||||
@Schema(description = "要求抵达时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime requiredArriveTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MarchTimeResultDTO {
|
||||
@Schema(description = "总行军时间")
|
||||
private String totalMarchTime;
|
||||
@Schema(description = "出发时间")
|
||||
private String departureTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MaterialConsumptionInputDTO {
|
||||
@Schema(description = "物资类型")
|
||||
private MaterialType materialType;
|
||||
@Schema(description = "单位消耗/配备数")
|
||||
private double baseConsumption;
|
||||
@Schema(description = "装备数量")
|
||||
private int equipmentCount;
|
||||
@Schema(description = "时间(小时)")
|
||||
private double hours;
|
||||
@Schema(description = "总需求")
|
||||
private double totalRequirement;
|
||||
|
||||
public enum MaterialType {
|
||||
AMMUNITION, OIL, WEAPON
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MaterialConsumptionResultDTO {
|
||||
@Schema(description = "总消耗量/需求量")
|
||||
private double totalConsumption;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UnitFormationInputDTO {
|
||||
@Schema(description = "总兵力")
|
||||
private double totalStrength;
|
||||
@Schema(description = "单位兵力")
|
||||
private double unitStrength;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UnitFormationResultDTO {
|
||||
@Schema(description = "编成数量")
|
||||
private double formationCount;
|
||||
@Schema(description = "最后一个编成的兵力")
|
||||
private double lastFormationStrength;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CrossRiverInputDTO {
|
||||
@Schema(description = "总人数")
|
||||
private int personCount;
|
||||
@Schema(description = "单批次容量")
|
||||
private int batchCapacity;
|
||||
@Schema(description = "单批次时间(小时)")
|
||||
private double batchHours;
|
||||
@Schema(description = "准备时间(小时)")
|
||||
private double prepareHours;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SpecialTimeResultDTO {
|
||||
@Schema(description = "总时间")
|
||||
private String totalTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LossRateInputDTO {
|
||||
@Schema(description = "总兵力")
|
||||
private double totalStrength;
|
||||
@Schema(description = "损失兵力")
|
||||
private double lostStrength;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LossRateResultDTO {
|
||||
@Schema(description = "损失率(百分比)")
|
||||
private double lossRate;
|
||||
@Schema(description = "剩余兵力")
|
||||
private double remainingStrength;
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
package com.yj.earth.common.service;
|
||||
|
||||
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<Source> list =sourceService.list(new LambdaQueryWrapper<Source>()
|
||||
@ -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<Role>().eq(Role::getRoleName, "管理员"));
|
||||
log.info("初始化默认资源数据");
|
||||
Source source1 = new Source();
|
||||
source1.setId(UUID.fastUUID().toString(true));
|
||||
source1.setSourceName("倾斜模型");
|
||||
source1.setSourceType("directory");
|
||||
source1.setTreeIndex(0);
|
||||
source1.setIsShow(1);
|
||||
sourceService.save(source1);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source1.getId());
|
||||
|
||||
Source source2 = new Source();
|
||||
source2.setId(UUID.fastUUID().toString(true));
|
||||
source2.setSourceName("人工模型");
|
||||
source2.setSourceType("directory");
|
||||
source2.setTreeIndex(0);
|
||||
source2.setIsShow(1);
|
||||
sourceService.save(source2);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source2.getId());
|
||||
|
||||
Source source3 = new Source();
|
||||
source3.setId(UUID.fastUUID().toString(true));
|
||||
source3.setSourceName("卫星底图");
|
||||
source3.setSourceType("directory");
|
||||
source3.setTreeIndex(0);
|
||||
source3.setIsShow(1);
|
||||
sourceService.save(source3);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source3.getId());
|
||||
|
||||
Source source4 = new Source();
|
||||
source4.setId(UUID.fastUUID().toString(true));
|
||||
source4.setSourceName("地形");
|
||||
source4.setSourceType("directory");
|
||||
source4.setTreeIndex(0);
|
||||
source4.setIsShow(1);
|
||||
sourceService.save(source4);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source4.getId());
|
||||
|
||||
Source source5 = new Source();
|
||||
source5.setId(UUID.fastUUID().toString(true));
|
||||
source5.setSourceName("在线图源");
|
||||
source5.setSourceType("directory");
|
||||
source5.setTreeIndex(0);
|
||||
source5.setIsShow(1);
|
||||
sourceService.save(source5);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source5.getId());
|
||||
|
||||
Source source6 = new Source();
|
||||
source6.setId(UUID.fastUUID().toString(true));
|
||||
source6.setSourceName("卫星图");
|
||||
source6.setSourceType("arcgisWximagery");
|
||||
source6.setParentId(source5.getId());
|
||||
source6.setTreeIndex(0);
|
||||
source6.setIsShow(1);
|
||||
sourceService.save(source6);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source6.getId());
|
||||
|
||||
Source source7 = new Source();
|
||||
source7.setId(UUID.fastUUID().toString(true));
|
||||
source7.setSourceName("暗黑地图");
|
||||
source7.setSourceType("arcgisBlueImagery");
|
||||
source7.setParentId(source5.getId());
|
||||
source7.setTreeIndex(0);
|
||||
source7.setIsShow(1);
|
||||
sourceService.save(source7);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source7.getId());
|
||||
|
||||
Source source8 = new Source();
|
||||
source8.setId(UUID.fastUUID().toString(true));
|
||||
source8.setSourceName("路网图");
|
||||
source8.setSourceType("gdlwImagery");
|
||||
source8.setParentId(source5.getId());
|
||||
source8.setTreeIndex(0);
|
||||
source8.setIsShow(1);
|
||||
sourceService.save(source8);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source8.getId());
|
||||
|
||||
Source source9 = new Source();
|
||||
source9.setId(UUID.fastUUID().toString(true));
|
||||
source9.setSourceName("矢量图");
|
||||
source9.setSourceType("gdslImagery");
|
||||
source9.setParentId(source5.getId());
|
||||
source9.setTreeIndex(0);
|
||||
source9.setIsShow(1);
|
||||
sourceService.save(source9);
|
||||
roleSourceService.addRoleSource(adminRole.getId(), source9.getId());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +206,7 @@ public class SourceDataGenerator {
|
||||
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
|
||||
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)) {
|
||||
|
||||
@ -34,7 +34,7 @@ public class CodeUtil {
|
||||
}
|
||||
|
||||
// 传入需要生成代码的表名
|
||||
Generation("file_info");
|
||||
Generation("model");
|
||||
}
|
||||
|
||||
public static void Generation(String... tableName) {
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 服务器唯一标识工具类
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
33
src/main/java/com/yj/earth/design/Model.java
Normal file
33
src/main/java/com/yj/earth/design/Model.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Model {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
@Schema(description = "模型类型ID")
|
||||
private String modelTypeId;
|
||||
@Schema(description = "模型名称")
|
||||
private String modelName;
|
||||
@Schema(description = "模型类型")
|
||||
private String modelType;
|
||||
@Schema(description = "海报类型")
|
||||
private String posterType;
|
||||
@Schema(description = "海报数据")
|
||||
private byte[] poster;
|
||||
@Schema(description = "模型数据")
|
||||
private byte[] data;
|
||||
@Schema(description = "模型视图")
|
||||
private String view;
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
20
src/main/java/com/yj/earth/design/ModelType.java
Normal file
20
src/main/java/com/yj/earth/design/ModelType.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ModelType {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
@Schema(description = "模型类型名称")
|
||||
private String name;
|
||||
@Schema(description = "父级节点ID")
|
||||
private String parentId;
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class UploadLocationImageDto {
|
||||
@Schema(description = "资源ID列表")
|
||||
private List<String> ids;
|
||||
@Schema(description = "父节点ID")
|
||||
private String parentId;
|
||||
@Schema(description = "树状索引")
|
||||
private Integer treeIndex;
|
||||
}
|
||||
@ -31,7 +31,7 @@ public class DiffuseScan {
|
||||
|
||||
@Data
|
||||
public static class CustomView {
|
||||
// 空对象,暂无需字段
|
||||
// 空对象、暂无需字段
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public class RadarScan {
|
||||
|
||||
@Data
|
||||
public static class CustomView {
|
||||
// 空对象,暂无需字段
|
||||
// 空对象、暂无需字段
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
35
src/main/java/com/yj/earth/params/Roam.java
Normal file
35
src/main/java/com/yj/earth/params/Roam.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@SourceType("roam")
|
||||
public class Roam {
|
||||
private String name;
|
||||
private List<Point> points;
|
||||
private String repeat;
|
||||
|
||||
@Data
|
||||
public static class Point {
|
||||
private int duration;
|
||||
private Position position;
|
||||
private Orientation orientation;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Position {
|
||||
private double lng;
|
||||
private double lat;
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Orientation {
|
||||
private double heading;
|
||||
private double pitch;
|
||||
private double roll;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user