This commit is contained in:
2025-12-08 14:50:27 +08:00
parent 74bee3e232
commit 6168f4c57d
25 changed files with 492 additions and 194 deletions

View File

@ -77,7 +77,7 @@ public class AuthGenerator {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
// 生成加密的授权字符串 // 生成加密的授权字符串
String authContent = generateAuth("标准版", 1000, 365, "8B1FB12E9F8E80109724989E0B25773B"); String authContent = generateAuth("标准版", 1000, 1, "25F429FDA965007B72BB7A6B2C03535A");
// 定义授权文件路径(当前目录下的 yjearth.YJ // 定义授权文件路径(当前目录下的 yjearth.YJ
Path licPath = Paths.get("yjearth.YJ"); Path licPath = Paths.get("yjearth.YJ");
@ -92,3 +92,4 @@ public class AuthGenerator {
} }
} }
} }

View File

@ -10,18 +10,25 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Tag(name = "系统设置管理") @Tag(name = "系统设置管理")
@RestController @RestController
@RequestMapping("/auth") @RequestMapping("/auth")
public class AuthController { public class AuthController {
// 授权文件存储路径、项目根目录下的 license 目录 // 授权文件存储目录(项目根目录下的 license 目录
private static final String AUTH_FILE_PATH = "license/yjearth.YJ"; private static final String LICENSE_DIR = "license";
// 授权文件后缀(固定为.YJ
private static final String AUTH_FILE_SUFFIX = ".YJ";
@GetMapping("/info") @GetMapping("/info")
@Operation(summary = "获取系统授权码") @Operation(summary = "获取系统授权码")
@ -37,28 +44,34 @@ public class AuthController {
return ApiResponse.failure("请选择授权文件"); return ApiResponse.failure("请选择授权文件");
} }
// 验证文件名是否为 yjearth.YJ
String fileName = file.getOriginalFilename(); String fileName = file.getOriginalFilename();
if (fileName == null || !fileName.equals("yjearth.YJ")) { // 验证文件名不为空且后缀为.YJ
return ApiResponse.failure("请上传 yjearth.YJ"); if (fileName == null || !fileName.endsWith(AUTH_FILE_SUFFIX)) {
return ApiResponse.failure("授权码错误、请联系管理员提供正确的授权文件");
} }
try { try {
// 读取文件内容 // 读取文件内容并验证有效性
String authContent = new String(file.getBytes(), StandardCharsets.UTF_8).trim(); String authContent = new String(file.getBytes(), StandardCharsets.UTF_8).trim();
// 验证授权内容有效性
String serverHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId(); String serverHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId();
boolean isValid = AuthValidator.validateAuth(authContent, serverHardwareMd5); boolean isValid = AuthValidator.validateAuth(authContent, serverHardwareMd5);
if (!isValid) { if (!isValid) {
return ApiResponse.failure("授权文件无效或已过期"); return ApiResponse.failure("授权码错误、请联系管理员提供正确的授权文件");
} }
// 创建目录(如果不存在)
Path path = Paths.get(AUTH_FILE_PATH); // 处理license目录不存在则创建
if (!Files.exists(path.getParent())) { Path licenseDirPath = Paths.get(LICENSE_DIR);
Files.createDirectories(path.getParent()); if (!Files.exists(licenseDirPath)) {
Files.createDirectories(licenseDirPath);
} }
// 保存授权文件
Files.write(path, authContent.getBytes(StandardCharsets.UTF_8)); // 删除目录下已存在的所有.YJ文件确保仅保留一个
deleteAllYjFilesInLicenseDir();
// 保存新的授权文件(使用原文件名)
Path newAuthFilePath = licenseDirPath.resolve(fileName);
Files.write(newAuthFilePath, authContent.getBytes(StandardCharsets.UTF_8));
return ApiResponse.success(null); return ApiResponse.success(null);
} catch (Exception e) { } catch (Exception e) {
return ApiResponse.failure("导入授权文件失败: " + e.getMessage()); return ApiResponse.failure("导入授权文件失败: " + e.getMessage());
@ -69,18 +82,60 @@ public class AuthController {
@Operation(summary = "查看授权信息") @Operation(summary = "查看授权信息")
public ApiResponse showAuth() { public ApiResponse showAuth() {
try { try {
// 检查授权文件是否存在 // 查找license目录下的所有.YJ文件
Path path = Paths.get(AUTH_FILE_PATH); List<Path> yjFileList = findAllYjFilesInLicenseDir();
if (!Files.exists(path)) {
return ApiResponse.failure("请先导入授权"); // 处理文件数量异常
if (yjFileList.isEmpty()) {
return ApiResponse.successWithMessage("未找到授权文件");
} }
// 读取授权文件内容 if (yjFileList.size() > 1) {
String authContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); return ApiResponse.successWithMessage("授权目录下存在多个" + AUTH_FILE_SUFFIX + "文件,请删除多余文件后重试");
// 获取授权详情 }
// 读取唯一的.YJ文件内容
Path authFilePath = yjFileList.get(0);
String authContent = new String(Files.readAllBytes(authFilePath), StandardCharsets.UTF_8);
// 解析并返回授权信息
AuthInfo authInfo = AuthValidator.getAuthInfo(authContent); AuthInfo authInfo = AuthValidator.getAuthInfo(authContent);
return ApiResponse.success(authInfo); return ApiResponse.success(authInfo);
} catch (Exception e) { } catch (Exception e) {
return ApiResponse.failure("获取授权信息失败: " + e.getMessage()); return ApiResponse.successWithMessage("授权码错误、请联系管理员提供正确的授权文件");
}
}
/**
* 查找 license 目录下所有后缀为 .YJ 的文件
*/
private List<Path> findAllYjFilesInLicenseDir() throws IOException {
Path licenseDirPath = Paths.get(LICENSE_DIR);
if (!Files.exists(licenseDirPath)) {
return Collections.emptyList();
}
if (!Files.isDirectory(licenseDirPath)) {
throw new IOException("license路径不是合法目录");
}
// 遍历目录、筛选后缀为 .YJ的文件不区分大小写
try (var directoryStream = Files.list(licenseDirPath)) {
return directoryStream
.filter(Files::isRegularFile)
.filter(path -> {
String fileName = path.getFileName().toString().toLowerCase();
return fileName.endsWith(AUTH_FILE_SUFFIX.toLowerCase());
})
.collect(Collectors.toList());
}
}
/**
* 删除 license 目录下所有后缀为 .YJ 的文件
*/
private void deleteAllYjFilesInLicenseDir() throws IOException {
List<Path> yjFileList = findAllYjFilesInLicenseDir();
for (Path yjFile : yjFileList) {
Files.delete(yjFile);
} }
} }
} }

View File

@ -193,7 +193,7 @@ public class FileInfoController {
// 标准化路径 // 标准化路径
targetFilePath = Paths.get(fileAbsolutePath).toRealPath(); targetFilePath = Paths.get(fileAbsolutePath).toRealPath();
// 校验文件合法性: 是否存在、是否为普通文件 // 是否存在、是否为普通文件
BasicFileAttributes fileAttr = Files.readAttributes(targetFilePath, BasicFileAttributes.class); BasicFileAttributes fileAttr = Files.readAttributes(targetFilePath, BasicFileAttributes.class);
if (!fileAttr.isRegularFile()) { if (!fileAttr.isRegularFile()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
@ -204,21 +204,27 @@ public class FileInfoController {
// 设置预览响应头 // 设置预览响应头
String fileName = targetFilePath.getFileName().toString(); String fileName = targetFilePath.getFileName().toString();
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
// 手动映射常见图片格式的 MIME 类型
String contentType = Files.probeContentType(targetFilePath); String contentType = Files.probeContentType(targetFilePath);
String fileExtension = getFileExtension(fileName).toLowerCase();
// 覆盖默认探测结果
contentType = mapImageContentType(fileExtension, contentType);
// 对于文本类型文件、指定字符编码 // 对于文本类型文件、指定字符编码
if (contentType != null && contentType.startsWith("text/")) { if (contentType != null && contentType.startsWith("text/")) {
response.setContentType(contentType + "; charset=UTF-8"); response.setContentType(contentType + "; charset=UTF-8");
} else { } else {
response.setContentType(contentType != null ? contentType : "application/octet-stream"); // 确保 Content-Type 不为空
response.setContentType(contentType != null ? contentType : "image/png");
} }
response.setContentLengthLong(fileAttr.size()); response.setContentLengthLong(fileAttr.size());
// 关键修改: 将attachment改为inline实现预览 // inline 已正确设置、无需修改
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); "inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
// 写入文件流 // 写入文件流(无需修改)
try (InputStream inputStream = Files.newInputStream(targetFilePath); try (InputStream inputStream = Files.newInputStream(targetFilePath);
OutputStream outputStream = response.getOutputStream()) { OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[1024 * 8]; byte[] buffer = new byte[1024 * 8];
@ -240,4 +246,32 @@ public class FileInfoController {
fileInfoService.writeResponseMessage(response, "预览失败: " + e.getMessage()); fileInfoService.writeResponseMessage(response, "预览失败: " + e.getMessage());
} }
} }
/**
* 获取文件后缀
*/
private String getFileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
return lastDotIndex == -1 ? "" : fileName.substring(lastDotIndex + 1);
}
/**
* 映射图片格式到标准 MIME 类型
*/
private String mapImageContentType(String fileExtension, String probeContentType) {
Map<String, String> imageMimeMap = new HashMap<>();
imageMimeMap.put("webp", "image/webp");
imageMimeMap.put("png", "image/png");
imageMimeMap.put("jpg", "image/jpeg");
imageMimeMap.put("jpeg", "image/jpeg");
imageMimeMap.put("gif", "image/gif");
imageMimeMap.put("bmp", "image/bmp");
imageMimeMap.put("svg", "image/svg+xml");
// 如果探针结果已存在且是图片类型
if (probeContentType != null && probeContentType.startsWith("image/")) {
return probeContentType;
}
return imageMimeMap.getOrDefault(fileExtension, probeContentType);
}
} }

View File

@ -29,16 +29,18 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@Data @Data
@Slf4j @Slf4j
@Tag(name = "路径规划管理") @Tag(name = "路径规划管理")
@RestController @RestController
@RequestMapping("/graphhopper") @RequestMapping("/graphhopper")
public class GraphHopperController { public class GraphHopperController {
@ -56,20 +58,44 @@ public class GraphHopperController {
private final AtomicBoolean isLoading = new AtomicBoolean(false); private final AtomicBoolean isLoading = new AtomicBoolean(false);
private final AtomicBoolean isLoaded = new AtomicBoolean(false); private final AtomicBoolean isLoaded = new AtomicBoolean(false);
@Operation(summary = "加载地图数据") @Operation(summary = "加载地图数据(启用路网文件)")
@PostMapping("/loadMap") @PostMapping("/loadMap")
public ApiResponse loadMap(@Parameter(description = "文件路径") @RequestParam String path) { public ApiResponse loadMap(@Parameter(description = "文件路径") @RequestParam String path) {
File osmFile = new File(path); File osmFile = new File(path);
if (!osmFile.exists()) { if (!osmFile.exists()) {
return ApiResponse.failure("地图文件不存在: " + path); return ApiResponse.failure("地图文件不存在: " + path);
} }
if (!osmFile.isFile() || !osmFile.getName().endsWith(".pbf")) { if (!osmFile.isFile()) {
return ApiResponse.failure("仅支持有效的.pbf格式OSM文件"); return ApiResponse.failure("请提供有效的文件路径");
} }
String fileName = osmFile.getName();
File tempPbfFile = null;
// 判断文件后缀
if (fileName.endsWith(".pbf")) {
// 直接使用原文件
} else if (fileName.endsWith(".pbfl")) {
try {
// 创建临时文件,后缀改为 .pbf
String tempFileName = UUID.randomUUID().toString() + ".pbf";
tempPbfFile = new File(osmFile.getParent(), tempFileName);
// 复制文件内容
Files.copy(osmFile.toPath(), tempPbfFile.toPath());
// 将路径指向临时文件
path = tempPbfFile.getAbsolutePath();
} catch (IOException e) {
return ApiResponse.failure("创建临时文件失败: " + e.getMessage());
}
} else {
return ApiResponse.failure("仅支持有效的.pbf或.pbfl格式OSM文件");
}
// 防止并发加载 // 防止并发加载
if (isLoading.get()) { if (isLoading.get()) {
return ApiResponse.failure("地图正在加载中、请稍后查询状态"); return ApiResponse.failure("对应区域的路网文件正在启用中、请稍后查询状态");
} }
// 标记加载状态 // 标记加载状态
@ -77,19 +103,21 @@ public class GraphHopperController {
isLoaded.set(false); isLoaded.set(false);
// 异步执行: 删除旧数据 → 创建新实例 → 加载新地图 // 异步执行: 删除旧数据 → 创建新实例 → 加载新地图
String finalPath = path;
File finalTempPbfFile = tempPbfFile;
new Thread(() -> { new Thread(() -> {
GraphHopper newHopper = null; GraphHopper newHopper = null;
try { try {
// 关键步骤1: 彻底删除旧地图数据目录 // 关键步骤1: 彻底删除旧地图数据目录
deleteOldGraphDir(); deleteOldGraphDir();
// 关键步骤2: 创建全新的GraphHopper实例 // 关键步骤2: 创建全新的GraphHopper实例
newHopper = createNewGraphHopperInstance(path); newHopper = createNewGraphHopperInstance(finalPath);
// 关键步骤3: 加载新地图 // 关键步骤3: 加载新地图
newHopper.importOrLoad(); newHopper.importOrLoad();
// 关键步骤4: 加载成功 → 替换当前实例 + 更新状态 // 关键步骤4: 加载成功 → 替换当前实例 + 更新状态
currentHopper = newHopper; currentHopper = newHopper;
isLoaded.set(true); isLoaded.set(true);
log.info("地图加载成功"); log.info("对应区域的路网文件启用成功");
} catch (Exception e) { } catch (Exception e) {
// 加载失败 → 清理新实例资源 // 加载失败 → 清理新实例资源
if (newHopper != null) { if (newHopper != null) {
@ -97,22 +125,31 @@ public class GraphHopperController {
} }
isLoaded.set(false); isLoaded.set(false);
e.printStackTrace(); e.printStackTrace();
log.error("地图加载失败: " + e.getMessage()); log.error("对应区域的路网文件启用失败: " + e.getMessage());
} finally { } finally {
// 无论成功/失败、释放加载锁 // 无论成功/失败、释放加载锁
isLoading.set(false); isLoading.set(false);
// 删除临时文件
if (finalTempPbfFile != null && finalTempPbfFile.exists()) {
if (finalTempPbfFile.delete()) {
log.info("临时文件已删除: " + finalTempPbfFile.getAbsolutePath());
} else {
log.warn("临时文件删除失败: " + finalTempPbfFile.getAbsolutePath());
}
}
} }
}).start(); }).start();
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@Operation(summary = "清除地图服务") @Operation(summary = "清除地图服务(停用路网文件)")
@PostMapping("/clearMap") @PostMapping("/clearMap")
public ApiResponse clearMap() { public ApiResponse clearMap() {
// 防止并发操作(与加载操作互斥) // 防止并发操作(与加载操作互斥)
if (isLoading.get()) { if (isLoading.get()) {
return ApiResponse.failure("地图正在加载中、无法清除、请稍后再试"); return ApiResponse.failure("对应区域的路网文件正在启用中、无法清除、请稍后再试");
} }
// 标记正在处理清除操作 // 标记正在处理清除操作
@ -120,24 +157,24 @@ public class GraphHopperController {
try { try {
// 1. 关闭当前GraphHopper实例 // 1. 关闭当前GraphHopper实例
if (currentHopper != null) { if (currentHopper != null) {
log.info("开始关闭当前地图服务实例"); log.info("开始关闭当前路网服务实例");
currentHopper.close(); currentHopper.close();
currentHopper = null; currentHopper = null;
log.info("地图服务实例已关闭"); log.info("路网服务实例已关闭");
} }
// 2. 删除地图数据目录 // 2. 删除地图数据目录
log.info("开始删除地图数据目录"); log.info("开始删除路网数据目录");
deleteOldGraphDir(); deleteOldGraphDir();
log.info("地图数据目录已删除"); log.info("路网数据目录已删除");
// 3. 重置状态变量 // 3. 重置状态变量
isLoaded.set(false); isLoaded.set(false);
return ApiResponse.success("地图服务已成功清除"); return ApiResponse.success("对应区域的路网文件已成功停用并清除");
} catch (Exception e) { } catch (Exception e) {
log.error("清除地图服务失败", e); log.error("清除路网服务失败", e);
return ApiResponse.failure("清除地图服务失败: " + e.getMessage()); return ApiResponse.failure("清除路网服务失败: " + e.getMessage());
} finally { } finally {
// 释放操作锁 // 释放操作锁
isLoading.set(false); isLoading.set(false);
@ -146,15 +183,17 @@ public class GraphHopperController {
@Operation(summary = "路径规划") @Operation(summary = "路径规划")
@PostMapping("/route") @PostMapping("/route")
public ApiResponse calculateRoute(@RequestBody RouteRequest request) { public ApiResponse calculateRoute(@RequestBody RouteRequest request) {
// 区分未加载地图和加载中两种状态 // 区分三种核心状态提示
if (isLoading.get()) { if (isLoading.get()) {
return ApiResponse.failure("地图正在加载中、请稍后再试"); // 场景1: 导入了路网文件但未启用(加载中)
return ApiResponse.failure("请启用对应区域的路网文件");
} }
if (!isLoaded.get() || currentHopper == null) { if (!isLoaded.get() || currentHopper == null) {
return ApiResponse.failure("地图未加载、请先加载地图"); // 场景2: 未导入路网文件
return ApiResponse.failure("请导入并启用对应区域的路网文件");
} }
try { try {
// 构建路径点列表 // 构建路径点列表
List<GHPoint> ghPoints = new ArrayList<>(); List<GHPoint> ghPoints = new ArrayList<>();
@ -182,9 +221,10 @@ public class GraphHopperController {
boolean hasPointNotFoundError = response.getErrors().stream() boolean hasPointNotFoundError = response.getErrors().stream()
.anyMatch(e -> e instanceof com.graphhopper.util.exceptions.PointNotFoundException); .anyMatch(e -> e instanceof com.graphhopper.util.exceptions.PointNotFoundException);
if (hasOutOfBoundsError) { if (hasOutOfBoundsError) {
return ApiResponse.failure("路径超出地图范围"); // 场景3: 已启用但路径超出路网范围
return ApiResponse.failure("绘制路径超出有效路网范围");
} else if (hasPointNotFoundError) { } else if (hasPointNotFoundError) {
return ApiResponse.failure("未超地图范围但找不到路径点"); return ApiResponse.failure("未超路网范围但找不到有效路径点");
} else { } else {
return ApiResponse.failure("路径计算异常: " + response.getErrors().get(0).getMessage()); return ApiResponse.failure("路径计算异常: " + response.getErrors().get(0).getMessage());
} }
@ -199,27 +239,26 @@ public class GraphHopperController {
RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints); RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints);
return ApiResponse.success(routeResponse); return ApiResponse.success(routeResponse);
} catch (com.graphhopper.util.exceptions.PointOutOfBoundsException e) { } catch (com.graphhopper.util.exceptions.PointOutOfBoundsException e) {
// 捕获单点超出范围的异常 // 捕获单点超出范围的异常场景3
return ApiResponse.failure("路径超出地图范围"); return ApiResponse.failure("绘制路径超出有效路网范围");
} catch (Exception e) { } catch (Exception e) {
return ApiResponse.failure("路径计算异常: " + e.getMessage()); return ApiResponse.failure("路径计算异常: " + e.getMessage());
} }
} }
@Operation(summary = "获取地图加载状态") @Operation(summary = "获取路网文件加载状态")
@GetMapping("/status") @GetMapping("/status")
public ApiResponse getMapStatus() { public ApiResponse getMapStatus() {
if (isLoading.get()) { if (isLoading.get()) {
return ApiResponse.success("地图正在加载"); return ApiResponse.success("对应区域的路网文件正在启用");
} else if (isLoaded.get() && currentHopper != null) { } else if (isLoaded.get() && currentHopper != null) {
return ApiResponse.success("地图已加载完成"); return ApiResponse.success("对应区域的路网文件已启用");
} else { } else {
return ApiResponse.success("地图未加载"); return ApiResponse.success("未导入对应区域的路网文件");
} }
} }
@Operation(summary = "获取交通方式") @Operation(summary = "获取交通方式")
@PostMapping("/profiles") @PostMapping("/profiles")
public ApiResponse profiles() { public ApiResponse profiles() {
return ApiResponse.success(graphHopperProperties.getProfiles()); return ApiResponse.success(graphHopperProperties.getProfiles());
@ -252,7 +291,7 @@ public class GraphHopperController {
private void deleteOldGraphDir() { private void deleteOldGraphDir() {
File graphDir = new File(graphHopperProperties.getGraphLocation()); File graphDir = new File(graphHopperProperties.getGraphLocation());
if (!graphDir.exists()) { if (!graphDir.exists()) {
log.info("地图目录不存在、无需删除: " + graphHopperProperties.getGraphLocation()); log.info("路网目录不存在、无需删除: " + graphHopperProperties.getGraphLocation());
return; return;
} }
@ -264,14 +303,14 @@ public class GraphHopperController {
deleteOldGraphDir(file); deleteOldGraphDir(file);
} else { } else {
boolean deleted = file.delete(); boolean deleted = file.delete();
log.info("删除旧地图文件: " + file.getAbsolutePath() + "" + (deleted ? "成功" : "失败")); log.info("删除旧路网文件: " + file.getAbsolutePath() + "" + (deleted ? "成功" : "失败"));
} }
} }
} }
// 删除空目录 // 删除空目录
boolean dirDeleted = graphDir.delete(); boolean dirDeleted = graphDir.delete();
System.out.println("删除旧地图目录: " + graphDir.getAbsolutePath() + "" + (dirDeleted ? "成功" : "失败")); log.info("删除旧路网目录: " + graphDir.getAbsolutePath() + "" + (dirDeleted ? "成功" : "失败"));
} }
// 重载:递归删除子目录 // 重载:递归删除子目录

View File

@ -193,6 +193,9 @@ public class IconLibraryController {
@GetMapping("/iconTypeTree") @GetMapping("/iconTypeTree")
public ApiResponse iconTypeTree(@Parameter(description = "图标名称") @RequestParam(value = "iconName", required = false) String iconName) throws SQLException, IllegalAccessException, InstantiationException { public ApiResponse iconTypeTree(@Parameter(description = "图标名称") @RequestParam(value = "iconName", required = false) String iconName) throws SQLException, IllegalAccessException, InstantiationException {
List<IconTypeVo> treeList = iconTypeList(iconName); List<IconTypeVo> treeList = iconTypeList(iconName);
if(treeList == null){
return ApiResponse.successWithMessage("请先创建或导入图标库");
}
return ApiResponse.success(treeList); return ApiResponse.success(treeList);
} }
@ -392,7 +395,7 @@ public class IconLibraryController {
queryWrapper.eq(IconLibrary::getIsEnable, 1); queryWrapper.eq(IconLibrary::getIsEnable, 1);
IconLibrary library = iconLibraryService.getOne(queryWrapper); IconLibrary library = iconLibraryService.getOne(queryWrapper);
if(library == null){ if(library == null){
throw new RuntimeException("请先创建或导入图标库"); return null;
} }
return library.getPath(); return library.getPath();
} }

View File

@ -206,6 +206,9 @@ public class MilitaryLibraryController {
@GetMapping("/militaryTypeTree") @GetMapping("/militaryTypeTree")
public ApiResponse militaryTypeTree(@Parameter(description = "军标名称") @RequestParam(value = "militaryName", required = false) String militaryName) throws SQLException, IllegalAccessException, InstantiationException { public ApiResponse militaryTypeTree(@Parameter(description = "军标名称") @RequestParam(value = "militaryName", required = false) String militaryName) throws SQLException, IllegalAccessException, InstantiationException {
List<MilitaryTypeVo> treeList = militaryTypeList(militaryName); List<MilitaryTypeVo> treeList = militaryTypeList(militaryName);
if (treeList == null) {
return ApiResponse.successWithMessage("请先创建或导入军标库");
}
return ApiResponse.success(treeList); return ApiResponse.success(treeList);
} }
@ -430,7 +433,7 @@ public class MilitaryLibraryController {
queryWrapper.eq(MilitaryLibrary::getIsEnable, 1); // 1=启用、0=未启用 queryWrapper.eq(MilitaryLibrary::getIsEnable, 1); // 1=启用、0=未启用
MilitaryLibrary library = militaryLibraryService.getOne(queryWrapper); MilitaryLibrary library = militaryLibraryService.getOne(queryWrapper);
if (library == null) { if (library == null) {
throw new RuntimeException("请先创建或导入军标库"); return null;
} }
return library.getPath(); return library.getPath();
} }

View File

@ -175,7 +175,11 @@ public class ModelLibraryController {
@Operation(summary = "模型类型列表") @Operation(summary = "模型类型列表")
@GetMapping("/modelTypeList") @GetMapping("/modelTypeList")
public ApiResponse modelTypeTree(@Parameter(description = "模型名称") @RequestParam(value = "modelName", required = false) String modelName) throws SQLException, IllegalAccessException, InstantiationException { public ApiResponse modelTypeTree(@Parameter(description = "模型名称") @RequestParam(value = "modelName", required = false) String modelName) throws SQLException, IllegalAccessException, InstantiationException {
return ApiResponse.success(modelTypeList(modelName)); List<ModelTypeVo> modelTypeVos = modelTypeList(modelName);
if (modelTypeVos == null) {
return ApiResponse.successWithMessage("请先创建或导入模型库");
}
return ApiResponse.success(modelTypeVos);
} }
@Operation(summary = "添加模型文件") @Operation(summary = "添加模型文件")
@ -547,7 +551,7 @@ public class ModelLibraryController {
queryWrapper.eq(ModelLibrary::getIsEnable, 1); queryWrapper.eq(ModelLibrary::getIsEnable, 1);
ModelLibrary modelLibrary = modelLibraryService.getOne(queryWrapper); ModelLibrary modelLibrary = modelLibraryService.getOne(queryWrapper);
if (modelLibrary == null) { if (modelLibrary == null) {
throw new RuntimeException("请先创建或导入模型库"); return null;
} }
return modelLibrary.getPath(); return modelLibrary.getPath();
} }

View File

@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Tag(name = "地图文件管理") @Tag(name = "地图文件管理")
@RestController @RestController
@ -27,12 +28,22 @@ public class PbfInfoController {
@Operation(summary = "添加地图文件") @Operation(summary = "添加地图文件")
@PostMapping("/add") @PostMapping("/add")
public ApiResponse add(@Parameter(description = "地图文件路径") @RequestParam(required = true) String path) { public ApiResponse add(@Parameter(description = "地图文件路径列表") @RequestParam(required = true) List<String> paths) {
// 批量转换为PbfInfo对象
List<PbfInfo> pbfInfoList = paths.stream()
.map(path -> {
PbfInfo pbfInfo = new PbfInfo(); PbfInfo pbfInfo = new PbfInfo();
pbfInfo.setPath(path); pbfInfo.setPath(path);
pbfInfo.setName(FileUtil.mainName(path)); pbfInfo.setName(FileUtil.mainName(path));
pbfInfo.setIsEnable(0); pbfInfo.setIsEnable(0);
pbfInfoService.save(pbfInfo); return pbfInfo;
})
.collect(Collectors.toList());
// 批量保存
pbfInfoService.saveBatch(pbfInfoList);
// 返回成功结果
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@ -83,6 +94,4 @@ public class PbfInfoController {
graphHopperController.clearMap(); graphHopperController.clearMap();
return ApiResponse.success(null); return ApiResponse.success(null);
} }
} }

View File

@ -19,6 +19,7 @@ import java.io.File;
import java.sql.*; import java.sql.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@RestController @RestController
@Tag(name = "POI文件管理") @Tag(name = "POI文件管理")
@ -27,14 +28,22 @@ public class PoiInfoController {
@Resource @Resource
private PoiInfoService poiInfoService; private PoiInfoService poiInfoService;
@Operation(summary = "添加POI文件") @Operation(summary = "批量添加POI文件")
@PostMapping("/add") @PostMapping("/add")
public ApiResponse add(@Parameter(description = "POI文件路径") @RequestParam(required = true) String path) { public ApiResponse add(@Parameter(description = "POI文件路径列表") @RequestParam(required = true) List<String> paths) {
// 批量转换为PoiInfo对象
List<PoiInfo> poiInfoList = paths.stream()
.map(path -> {
PoiInfo poiInfo = new PoiInfo(); PoiInfo poiInfo = new PoiInfo();
poiInfo.setPath(path); poiInfo.setPath(path);
poiInfo.setName(FileUtil.mainName(path)); poiInfo.setName(FileUtil.mainName(path));
poiInfo.setIsEnable(0); poiInfo.setIsEnable(0);
poiInfoService.save(poiInfo); return poiInfo;
})
.collect(Collectors.toList());
// 批量保存
poiInfoService.saveBatch(poiInfoList);
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@ -57,7 +66,7 @@ public class PoiInfoController {
@Operation(summary = "启用POI文件") @Operation(summary = "启用POI文件")
@PostMapping("/enable") @PostMapping("/enable")
public ApiResponse enable(@Parameter(description = "地图文件ID") @RequestParam(required = true) String id) { public ApiResponse enable(@Parameter(description = "POI文件ID") @RequestParam(required = true) String id) {
PoiInfo poiInfo = poiInfoService.getById(id); PoiInfo poiInfo = poiInfoService.getById(id);
poiInfo.setIsEnable(1); poiInfo.setIsEnable(1);
poiInfoService.updateById(poiInfo); poiInfoService.updateById(poiInfo);
@ -74,7 +83,7 @@ public class PoiInfoController {
@Operation(summary = "禁用POI文件") @Operation(summary = "禁用POI文件")
@PostMapping("/disable") @PostMapping("/disable")
public ApiResponse disable(@Parameter(description = "地图文件ID") @RequestParam(required = true) String id) { public ApiResponse disable(@Parameter(description = "POI文件ID") @RequestParam(required = true) String id) {
PoiInfo poiInfo = new PoiInfo(); PoiInfo poiInfo = new PoiInfo();
poiInfo.setId(id); poiInfo.setId(id);
poiInfo.setIsEnable(0); poiInfo.setIsEnable(0);
@ -84,10 +93,7 @@ public class PoiInfoController {
@Operation(summary = "POI搜索") @Operation(summary = "POI搜索")
@GetMapping("/search") @GetMapping("/search")
public ApiResponse search( public ApiResponse search(@Parameter(description = "分页页码") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize, @Parameter(description = "名称搜索") String name) {
@Parameter(description = "分页页码") Integer pageNum,
@Parameter(description = "分页大小") Integer pageSize,
@Parameter(description = "名称搜索") String name) {
int offset = (pageNum - 1) * pageSize; int offset = (pageNum - 1) * pageSize;
// 查询启用的POI文件 // 查询启用的POI文件
PoiInfo poiInfo = poiInfoService.getOne(new QueryWrapper<PoiInfo>().lambda().eq(PoiInfo::getIsEnable, 1)); PoiInfo poiInfo = poiInfoService.getOne(new QueryWrapper<PoiInfo>().lambda().eq(PoiInfo::getIsEnable, 1));

View File

@ -26,6 +26,8 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File;
import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
@ -186,11 +188,21 @@ public class SourceController {
queryWrapper.eq(Source::getId, updateLevelDto.getId()); queryWrapper.eq(Source::getId, updateLevelDto.getId());
Source source = sourceService.getOne(queryWrapper); Source source = sourceService.getOne(queryWrapper);
String params = source.getParams(); String params = source.getParams();
if (params != null) {
// 修改这个 JSON 的值 // 修改这个 JSON 的值
params = JsonUtil.modifyJsonValue(params, "layerIndex", updateLevelDto.getLayerIndex()); params = JsonUtil.modifyJsonValue(params, "layerIndex", updateLevelDto.getLayerIndex());
source.setParams(params); source.setParams(params);
sourceService.updateById(source); sourceService.updateById(source);
} }
// 否则是新增
if (params == null) {
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("layerIndex", updateLevelDto.getLayerIndex());
paramsMap.put("id", updateLevelDto.getId());
source.setParams(MapUtil.mapToString(paramsMap));
sourceService.updateById(source);
}
}
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@ -250,53 +262,67 @@ public class SourceController {
@PostMapping("/uploadLocationImage") @PostMapping("/uploadLocationImage")
@Operation(summary = "新增定位图片") @Operation(summary = "新增定位图片")
public ApiResponse uploadLocationImage(@RequestParam("ids") @Parameter(description = "上传定位图片ID列表") List<String> ids, public ApiResponse uploadLocationImage(
@RequestParam("ids") @Parameter(description = "上传定位图片ID列表") List<String> ids,
@RequestParam(value = "parentId", required = false) @Parameter(description = "父节点ID") String parentId, @RequestParam(value = "parentId", required = false) @Parameter(description = "父节点ID") String parentId,
@RequestParam(value = "treeIndex", required = false) @Parameter(description = "树状索引") Integer treeIndex, @RequestParam(value = "treeIndex", required = false) @Parameter(description = "树状索引") Integer treeIndex,
@RequestParam(value = "params", required = false) @Parameter(description = "参数") String params, @RequestParam(value = "params", required = false) @Parameter(description = "参数") String params,
@RequestParam(value = "sourceType", required = false) @Parameter(description = "资源类型") String sourceType, @RequestParam(value = "sourceType", required = false) @Parameter(description = "资源类型") String sourceType,
@RequestParam("files") @Parameter(description = "带有定位的图片文件", required = true) MultipartFile[] files) { @RequestParam("filePaths") @Parameter(description = "图片文件绝对路径列表", required = true) List<String> filePaths) {
// 验证并转换参数
sourceParamsValidator.validateAndConvert( sourceParamsValidator.validateAndConvert(
sourceType, sourceType,
JsonUtil.jsonToMap(params) JsonUtil.jsonToMap(params)
); );
// 检查是否有重复的filePath
for (String filePath : filePaths) {
// 检查数据库中是否已存在该文件路径
boolean exists = sourceService.lambdaQuery()
.eq(Source::getSourcePath, filePath)
.eq(Source::getIsShow, SHOW)
.exists();
if (exists) {
return ApiResponse.failure("数据库存在参数中部分文件路径、不允许重复上传:" + filePath);
}
}
List<Source> sources = new ArrayList<>(); List<Source> sources = new ArrayList<>();
for (int i = 0; i < files.length; i++) { for (int i = 0; i < filePaths.size(); i++) {
Map<String, Object> dataMap = fileInfoService.handleLocationImageUpload(files[i]); String filePath = filePaths.get(i);
// 构建并保存资源对象 File file = new File(filePath);
try {
Map<String, Object> dataMap = fileInfoService.handleLocationImageUpload(file);
Source source = new Source(); Source source = new Source();
source.setSourcePath(filePath);
source.setId(ids.get(i)); source.setId(ids.get(i));
source.setSourceName(files[i].getOriginalFilename()); source.setSourceName(Paths.get(filePath).getFileName().toString());
source.setParentId(parentId); source.setParentId(parentId);
source.setSourceType(sourceType); source.setSourceType(sourceType);
source.setTreeIndex(treeIndex); source.setTreeIndex(treeIndex);
// 转换为对象
Point point = JsonUtil.mapToObject(JsonUtil.jsonToMap(params), Point.class); Point point = JsonUtil.mapToObject(JsonUtil.jsonToMap(params), Point.class);
point.setId(ids.get(i)); point.setId(ids.get(i));
Point.Position position = new Point.Position(); Point.Position position = new Point.Position();
point.setName(files[i].getOriginalFilename()); point.setName(Paths.get(filePath).getFileName().toString());
point.getLabel().setText(files[i].getOriginalFilename()); point.getLabel().setText(Paths.get(filePath).getFileName().toString());
// 解析经纬度、海拔
Object lonObj = dataMap.get("lon"); Object lonObj = dataMap.get("lon");
if (lonObj != null && lonObj instanceof Double) { if (lonObj != null && lonObj instanceof Double) {
position.setLng((Double) lonObj); position.setLng((Double) lonObj);
} }
Object latObj = dataMap.get("lat"); Object latObj = dataMap.get("lat");
if (latObj != null && latObj instanceof Double) { if (latObj != null && latObj instanceof Double) {
position.setLat((Double) latObj); position.setLat((Double) latObj);
} }
Object altObj = dataMap.get("alt"); Object altObj = dataMap.get("alt");
if (altObj != null && altObj instanceof Double) { if (altObj != null && altObj instanceof Double) {
position.setAlt((Double) altObj); position.setAlt((Double) altObj);
} }
point.setPosition(position); point.setPosition(position);
// 根据资源类型设置链接/VR内容
if ("linkImage".equals(sourceType)) { if ("linkImage".equals(sourceType)) {
// 设置地址
List<Point.Attribute.Link.LinkContent> list = new ArrayList<>(); List<Point.Attribute.Link.LinkContent> list = new ArrayList<>();
Point.Attribute.Link.LinkContent linkContent = new Point.Attribute.Link.LinkContent(); Point.Attribute.Link.LinkContent linkContent = new Point.Attribute.Link.LinkContent();
linkContent.setName("带定位照片"); linkContent.setName("带定位照片");
@ -311,15 +337,17 @@ public class SourceController {
list.add(vrContent); list.add(vrContent);
point.getAttribute().getVr().setContent(list); point.getAttribute().getVr().setContent(list);
} }
// 保存资源参数
// 将 vrImage 转化为 JSON
source.setParams(JsonUtil.toJson(point)); source.setParams(JsonUtil.toJson(point));
source.setIsShow(SHOW); source.setIsShow(SHOW);
sourceService.save(source); sourceService.save(source);
// 添加资源到该用户的角色下 // 添加角色关联
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId()); roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
sources.add(source); sources.add(source);
} catch (Exception e) {
return ApiResponse.failure("处理文件失败:" + filePath + ",原因:" + e.getMessage());
}
} }
return ApiResponse.success(sources); return ApiResponse.success(sources);
} }

View File

@ -71,10 +71,11 @@ public class TsPlanController {
} }
private TsSource createSourceIfNotExists(String sourceName, String sourceType, String parentId, int treeIndex, int isShow, String tsPlanId) { private TsSource createSourceIfNotExists(String sourceName, String sourceType, String parentId, int treeIndex, int isShow, String tsPlanId) {
// 检查资源是否已存在通过名称和父ID组合判断唯一性 // 检查资源是否已存在通过名称和父ID和计划ID组合判断唯一性)
TsSource existingSource = tsSourceService.getOne(new LambdaQueryWrapper<TsSource>() TsSource existingSource = tsSourceService.getOne(new LambdaQueryWrapper<TsSource>()
.eq(TsSource::getSourceName, sourceName) .eq(TsSource::getSourceName, sourceName)
.eq(parentId != null, TsSource::getParentId, parentId) .eq(parentId != null, TsSource::getParentId, parentId)
.eq(TsSource::getPlanId, tsPlanId)
.isNull(parentId == null, TsSource::getParentId)); .isNull(parentId == null, TsSource::getParentId));
if (existingSource != null) { if (existingSource != null) {
return existingSource; return existingSource;
@ -128,21 +129,24 @@ public class TsPlanController {
@Parameter(description = "开始时间") @RequestParam(required = false) String startTime, @Parameter(description = "开始时间") @RequestParam(required = false) String startTime,
@Parameter(description = "结束时间") @RequestParam(required = false) String endTime) { @Parameter(description = "结束时间") @RequestParam(required = false) String endTime) {
LambdaQueryWrapper<TsPlan> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<TsPlan> queryWrapper = new LambdaQueryWrapper<>();
// 创建人昵称模糊查询
if (StringUtils.isNotBlank(nickname)) {
LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<User>()
.select(User::getId)
.like(User::getNickname, nickname);
List<String> userIds = userService.list(userQueryWrapper).stream()
.map(User::getId)
.collect(Collectors.toList());
if (userIds.isEmpty()) {
// 返回空数据
return ApiResponse.success(new Page<TsPlan>());
}
}
// 方案名称模糊查询 // 方案名称模糊查询
if (StringUtils.isNotBlank(name)) { if (StringUtils.isNotBlank(name)) {
queryWrapper.like(TsPlan::getName, name); queryWrapper.like(TsPlan::getName, name);
} }
// 创建人昵称模糊查询
if (StringUtils.isNotBlank(nickname)) {
queryWrapper.in(TsPlan::getCreatedBy,
new LambdaQueryWrapper<User>()
.select(User::getId)
.like(User::getNickname, nickname)
);
}
// 时间范围查询 // 时间范围查询
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
try { try {
@ -154,36 +158,17 @@ public class TsPlanController {
LocalDateTime end = LocalDateTime.parse(endTime, formatter); LocalDateTime end = LocalDateTime.parse(endTime, formatter);
queryWrapper.le(TsPlan::getCreatedAt, end); queryWrapper.le(TsPlan::getCreatedAt, end);
} }
} catch (DateTimeParseException e) { } catch (
DateTimeParseException e) {
return ApiResponse.failure("时间格式错误、需为 yyyy-MM-dd HH:mm:ss"); return ApiResponse.failure("时间格式错误、需为 yyyy-MM-dd HH:mm:ss");
} }
// 执行分页查询 // 执行分页查询
Page<TsPlan> page = tsPlanService.page(new Page<>(pageNum, pageSize), queryWrapper); Page<TsPlan> page = tsPlanService.page(new Page<>(pageNum, pageSize), queryWrapper);
List<TsPlan> records = page.getRecords();
// 关联查询用户信息并封装结果 for (TsPlan record : records) {
if (!page.getRecords().isEmpty()) { record.setCreatedBy(userService.getById(record.getCreatedBy()).getNickname());
List<String> creatorIds = page.getRecords().stream()
.map(TsPlan::getCreatedBy)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!creatorIds.isEmpty()) {
List<User> creators = userService.listByIds(creatorIds);
Map<String, String> creatorNicknameMap = creators.stream()
.collect(Collectors.toMap(
User::getId,
user -> user.getNickname() != null ? user.getNickname() : "未知昵称",
(k1, k2) -> k1
));
page.getRecords().forEach(plan -> {
plan.setCreatedBy(creatorNicknameMap.getOrDefault(plan.getCreatedBy(), "未知用户"));
});
} }
}
return ApiResponse.success(page); return ApiResponse.success(page);
} }

View File

@ -2,14 +2,17 @@ package com.yj.earth.business.controller;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yj.earth.business.domain.Source;
import com.yj.earth.business.domain.TsSource; import com.yj.earth.business.domain.TsSource;
import com.yj.earth.business.service.TsSourceService; import com.yj.earth.business.service.TsSourceService;
import com.yj.earth.common.util.ApiResponse; import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.common.util.JsonUtil; import com.yj.earth.common.util.JsonUtil;
import com.yj.earth.common.util.MapUtil; import com.yj.earth.common.util.MapUtil;
import com.yj.earth.dto.source.DragSourceDto;
import com.yj.earth.dto.tsPlan.BatchUpdateShowStatusDto; import com.yj.earth.dto.tsPlan.BatchUpdateShowStatusDto;
import com.yj.earth.dto.tsSource.AddTsModelSourceDto; import com.yj.earth.dto.tsSource.AddTsModelSourceDto;
import com.yj.earth.dto.tsSource.AddTsSourceDto; import com.yj.earth.dto.tsSource.AddTsSourceDto;
import com.yj.earth.dto.tsSource.DragTsSourceDto;
import com.yj.earth.dto.tsSource.UpdateTsSourceDto; import com.yj.earth.dto.tsSource.UpdateTsSourceDto;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@ -68,6 +71,20 @@ public class TsSourceController {
return ApiResponse.success(null); return ApiResponse.success(null);
} }
@Operation(summary = "拖动资源")
@PostMapping("/dragSource")
public ApiResponse drag(@RequestBody List<DragTsSourceDto> dragTsSourceDtoList) {
for (DragTsSourceDto dragTsSourceDto : dragTsSourceDtoList) {
LambdaQueryWrapper<TsSource> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TsSource::getId, dragTsSourceDto.getId());
TsSource tsSource = tsSourceService.getOne(queryWrapper);
tsSource.setTreeIndex(dragTsSourceDto.getTreeIndex());
tsSource.setParentId(dragTsSourceDto.getParentId());
tsSourceService.updateById(tsSource);
}
return ApiResponse.success(null);
}
@Operation(summary = "查询某个方案下的态势资源") @Operation(summary = "查询某个方案下的态势资源")
@PostMapping("/query") @PostMapping("/query")
public ApiResponse query(@Parameter(description = "态势方案ID") @RequestParam(required = true) String id) { public ApiResponse query(@Parameter(description = "态势方案ID") @RequestParam(required = true) String id) {

View File

@ -32,6 +32,9 @@ public class TsPlan implements Serializable {
@Schema(description = "创建人") @Schema(description = "创建人")
private String createdBy; private String createdBy;
@Schema(description = "滑轮")
private Integer wheel;
@Schema(description = "仿真开始时间") @Schema(description = "仿真开始时间")
private LocalDateTime simulationStartTime; private LocalDateTime simulationStartTime;

View File

@ -5,12 +5,14 @@ import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.Map; import java.util.Map;
public interface FileInfoService extends IService<FileInfo> { public interface FileInfoService extends IService<FileInfo> {
// 根据文件ID获取文件绝对路径 // 根据文件ID获取文件绝对路径
String getFileAbsolutePath(String id); String getFileAbsolutePath(String id);
Map<String, Object> handleLocationImageUpload(MultipartFile file); Map<String, Object> handleLocationImageUpload(MultipartFile file);
Map<String, Object> handleLocationImageUpload(File file);
String uploadWithPreview(MultipartFile file); String uploadWithPreview(MultipartFile file);
void writeResponseMessage(HttpServletResponse response, String message); void writeResponseMessage(HttpServletResponse response, String message);
String getFullUploadPath(); String getFullUploadPath();

View File

@ -104,6 +104,62 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i
return result; return result;
} }
public Map<String, Object> handleLocationImageUpload(File file) {
// 构建并返回结果
Map<String, Object> result = new HashMap<>();
try {
if (file == null || !file.exists()) {
throw new IllegalArgumentException("文件不存在");
}
if (file.length() == 0) {
throw new IllegalArgumentException("上传文件不能为空(空文件)");
}
// 原始文件名
String originalFilename = file.getName();
// 文件后缀
String fileSuffix = FileUtil.extName(originalFilename);
// 文件MIME类型
String contentType = Files.probeContentType(file.toPath());
if (contentType == null || !contentType.startsWith("image/")) {
throw new IllegalArgumentException("请上传图片文件,当前文件类型:" + contentType);
}
// 获取完整的上传目录路径
Path fullUploadPath = Paths.get(getFullUploadPath());
// 生成唯一文件名
String uniqueFileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileSuffix;
// 创建文件存储目录(不存在则创建)
Files.createDirectories(fullUploadPath);
// 构建目标文件路径
Path destFilePath = fullUploadPath.resolve(uniqueFileName);
// 覆盖已存在的文件
Files.copy(file.toPath(), destFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
String fileMd5 = calculateFileMd5(destFilePath.toFile());
Map<String, Double> metadata;
try (InputStream is = Files.newInputStream(destFilePath)) {
metadata = extractImageMetadata(is);
}
FileInfo fileInfo = new FileInfo();
fileInfo.setFileName(originalFilename);
fileInfo.setFileSuffix(fileSuffix);
fileInfo.setContentType(contentType);
fileInfo.setFileSize(file.length());
fileInfo.setFilePath(uniqueFileName);
fileInfo.setFileMd5(fileMd5);
this.save(fileInfo);
result.put("url", "/fileInfo/preview/" + fileInfo.getId());
result.put("lon", metadata.get("lon"));
result.put("lat", metadata.get("lat"));
result.put("alt", metadata.get("alt"));
} catch (IOException e) {
throw new RuntimeException("文件处理失败: " + e.getMessage(), e);
}
return result;
}
public String uploadWithPreview(MultipartFile file) { public String uploadWithPreview(MultipartFile file) {
FileInfo fileInfo = new FileInfo(); FileInfo fileInfo = new FileInfo();
try { try {

View File

@ -95,12 +95,19 @@ public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> impleme
public Source addModelSource(AddModelSourceDto addModelSourceDto) { public Source addModelSource(AddModelSourceDto addModelSourceDto) {
// 获取资源绝对路径 // 获取资源绝对路径
String sourcePath = addModelSourceDto.getSourcePath(); String sourcePath = addModelSourceDto.getSourcePath();
// 判断数据是否在数据库存在
LambdaQueryWrapper<Source> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Source::getSourcePath, sourcePath);
if (sourceService.count(queryWrapper) > 0) {
throw new RuntimeException("资源已存在");
}
// 获取资源名称 // 获取资源名称
String sourceName = FileUtil.mainName(sourcePath); String sourceName = FileUtil.mainName(sourcePath);
// 校验是否通过 // 校验是否通过
String message = sourceService.checkIsPass(addModelSourceDto.getParentId(), sourceName); String message = sourceService.checkIsPass(addModelSourceDto.getParentId(), sourceName);
if (message != null) { if (message != null) {
return null; throw new RuntimeException("父节点不存在");
} }
// 调用SDK加载资源 // 调用SDK加载资源
String sourceId = sourceService.addAndGetSourceId(sourcePath); String sourceId = sourceService.addAndGetSourceId(sourcePath);

View File

@ -31,7 +31,9 @@ public class SaTokenConfig implements WebMvcConfigurer {
excludePathPatterns.add("/iconLibrary/data/icon/**"); excludePathPatterns.add("/iconLibrary/data/icon/**");
excludePathPatterns.add("/militaryLibrary/data/military/**"); excludePathPatterns.add("/militaryLibrary/data/military/**");
excludePathPatterns.add("/modelLibrary/data/**"); excludePathPatterns.add("/modelLibrary/data/**");
excludePathPatterns.add("/**"); excludePathPatterns.add("/auth/show");
excludePathPatterns.add("/auth/info");
excludePathPatterns.add("/auth/import");
// 注册 Sa-Token 拦截器 // 注册 Sa-Token 拦截器
registry.addInterceptor(new SaInterceptor(handle -> { registry.addInterceptor(new SaInterceptor(handle -> {

View File

@ -39,7 +39,7 @@ public class ServerInitService {
for (Source source : list) { for (Source source : list) {
// 同步资源 // 同步资源
sourceService.getDetail(source.getSourcePath(), sourceService.addAndGetSourceId(source.getSourcePath())); sourceService.getDetail(source.getSourcePath(), sourceService.addAndGetSourceId(source.getSourcePath()));
log.info("初始化资源<--{}-->完成", source.getSourceName()); log.info("初始化资源 <--{}--> 完成", source.getSourceName());
} }
} }

View File

@ -13,6 +13,8 @@ public class TsPlan {
private String name; private String name;
@Schema(description = "方案描述") @Schema(description = "方案描述")
private String desc; private String desc;
@Schema(description = "滑轮")
private Integer wheel;
@Schema(description = "创建人") @Schema(description = "创建人")
private String createdBy; private String createdBy;
@Schema(description = "仿真开始时间") @Schema(description = "仿真开始时间")

View File

@ -11,6 +11,8 @@ public class AddTsPlanDto {
private String name; private String name;
@Schema(description = "方案描述") @Schema(description = "方案描述")
private String desc; private String desc;
@Schema(description = "滑轮")
private Integer wheel;
@Schema(description = "仿真开始时间") @Schema(description = "仿真开始时间")
private LocalDateTime simulationStartTime; private LocalDateTime simulationStartTime;
} }

View File

@ -13,6 +13,8 @@ public class UpdateTsPlanDto {
private String name; private String name;
@Schema(description = "方案描述") @Schema(description = "方案描述")
private String desc; private String desc;
@Schema(description = "滑轮")
private Integer wheel;
@Schema(description = "仿真开始时间") @Schema(description = "仿真开始时间")
private LocalDateTime simulationStartTime; private LocalDateTime simulationStartTime;
} }

View File

@ -0,0 +1,16 @@
package com.yj.earth.dto.tsSource;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class DragTsSourceDto {
@Schema(description = "主键")
private String id;
@Schema(description = "父级ID")
private String parentId;
@Schema(description = "树形索引")
private Integer treeIndex;
}

View File

@ -2,6 +2,7 @@ package com.yj.earth.params;
import com.yj.earth.annotation.SourceType; import com.yj.earth.annotation.SourceType;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List; import java.util.List;
@ -22,6 +23,7 @@ public class MilitaryParam {
private Attribute attribute; private Attribute attribute;
private String attributeType; private String attributeType;
private Text text; private Text text;
private CustomView customView;
@Data @Data
public static class Position { public static class Position {
@ -63,4 +65,26 @@ public class MilitaryParam {
private int far; private int far;
private Position position; private Position position;
} }
@Data
@NoArgsConstructor
public static class CustomView {
private LinkImage.CustomView.Orientation orientation;
private LinkImage.CustomView.RelativePosition relativePosition;
@Data
@NoArgsConstructor
public static class Orientation {
private double heading;
private double pitch;
private double roll;
}
@Data
@NoArgsConstructor
public static class RelativePosition {
private double lng;
private double lat;
private double alt;
}
}
} }

View File

@ -17,6 +17,6 @@ public class Vector {
private List<Object> headTables; private List<Object> headTables;
private boolean show; private boolean show;
private String color; private String color;
private double opacity; private Double opacity;
private Object customView; private Object customView;
} }

View File

@ -1,7 +1,5 @@
package com.yj.earth.vo; package com.yj.earth.vo;
import com.yj.earth.business.domain.Icon;
import com.yj.earth.business.domain.Military;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;