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