最新产品
This commit is contained in:
@ -0,0 +1,86 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.auth.AuthInfo;
|
||||
import com.yj.earth.auth.AuthValidator;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Tag(name = "系统设置管理")
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
// 授权文件存储路径、项目根目录下的license目录
|
||||
private static final String AUTH_FILE_PATH = "license/yjearth.lic";
|
||||
|
||||
@GetMapping("/info")
|
||||
@Operation(summary = "获取系统授权码")
|
||||
public ApiResponse info() {
|
||||
return ApiResponse.success(ServerUniqueIdUtil.getServerUniqueId());
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入授权信息")
|
||||
public ApiResponse importAuth(@Parameter(description = "授权文件", required = true) @RequestParam("file") MultipartFile file) {
|
||||
// 验证文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
return ApiResponse.failure("请选择授权文件");
|
||||
}
|
||||
|
||||
// 验证文件名是否为 yjearth.lic
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.equals("yjearth.lic")) {
|
||||
return ApiResponse.failure("请上传 yjearth.lic");
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取文件内容
|
||||
String authContent = new String(file.getBytes(), StandardCharsets.UTF_8).trim();
|
||||
// 验证授权内容有效性
|
||||
String serverHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId();
|
||||
boolean isValid = AuthValidator.validateAuth(authContent, serverHardwareMd5);
|
||||
if (!isValid) {
|
||||
return ApiResponse.failure("授权文件无效或已过期");
|
||||
}
|
||||
// 创建目录(如果不存在)
|
||||
Path path = Paths.get(AUTH_FILE_PATH);
|
||||
if (!Files.exists(path.getParent())) {
|
||||
Files.createDirectories(path.getParent());
|
||||
}
|
||||
// 保存授权文件
|
||||
Files.write(path, authContent.getBytes(StandardCharsets.UTF_8));
|
||||
return ApiResponse.success(null);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("导入授权文件失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/show")
|
||||
@Operation(summary = "查看授权信息")
|
||||
public ApiResponse showAuth() {
|
||||
try {
|
||||
// 检查授权文件是否存在
|
||||
Path path = Paths.get(AUTH_FILE_PATH);
|
||||
if (!Files.exists(path)) {
|
||||
return ApiResponse.failure("请先导入授权");
|
||||
}
|
||||
// 读取授权文件内容
|
||||
String authContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||
// 获取授权详情
|
||||
AuthInfo authInfo = AuthValidator.getAuthInfo(authContent);
|
||||
return ApiResponse.success(authInfo);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("获取授权信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
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.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.vo.FileInfoVo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "文件数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/fileInfo")
|
||||
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();
|
||||
|
||||
List<FileInfoVo> fileInfoVoList = new ArrayList<>();
|
||||
|
||||
// 遍历处理每个文件
|
||||
for (MultipartFile file : files) {
|
||||
// 跳过空文件
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取原始文件名和后缀
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileSuffix = FileUtil.extName(originalFilename);
|
||||
String contentType = file.getContentType();
|
||||
|
||||
// 生成唯一文件名、避免重复
|
||||
String uniqueFileName = IdUtil.simpleUUID() + "." + fileSuffix;
|
||||
|
||||
// 创建文件存储目录(如果不存在则自动创建)
|
||||
File uploadDir = new File(fullUploadPath);
|
||||
FileUtil.mkdir(uploadDir);
|
||||
|
||||
// 构建完整文件路径
|
||||
String filePath = fullUploadPath + File.separator + uniqueFileName;
|
||||
File destFile = new File(filePath);
|
||||
|
||||
// 使用Hutool工具类保存文件
|
||||
FileUtil.writeBytes(file.getBytes(), destFile);
|
||||
|
||||
// 计算文件MD5(用于校验文件完整性)
|
||||
String fileMd5 = DigestUtil.md5Hex(destFile);
|
||||
|
||||
// 查询有没有文件名一样并且 MD5 也一样的数据
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileName, originalFilename).eq(FileInfo::getFileMd5, fileMd5);
|
||||
if (fileInfoService.count(queryWrapper) > 0) { // 修复了此处的bug、添加了queryWrapper参数
|
||||
return ApiResponse.failure("已存在文件名相同且内容完全一致的文件");
|
||||
}
|
||||
|
||||
// 保存文件信息到数据库
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
fileInfo.setFileName(originalFilename);
|
||||
fileInfo.setFileSuffix(fileSuffix);
|
||||
fileInfo.setContentType(contentType);
|
||||
fileInfo.setFileSize(file.getSize());
|
||||
fileInfo.setFilePath(uniqueFileName); // 只保存相对文件名、不保存完整路径
|
||||
fileInfo.setFileMd5(fileMd5);
|
||||
|
||||
// 保存文件信息并获取ID
|
||||
fileInfoService.save(fileInfo);
|
||||
|
||||
// 构建并设置预览URL和下载URL
|
||||
String previewUrl = "/fileInfo/preview/" + fileInfo.getId();
|
||||
String downloadUrl = "/fileInfo/download/" + fileInfo.getId();
|
||||
|
||||
FileInfoVo fileInfoVo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, fileInfoVo);
|
||||
fileInfoVo.setPreviewUrl(previewUrl);
|
||||
fileInfoVo.setDownloadUrl(downloadUrl);
|
||||
|
||||
fileInfoVoList.add(fileInfoVo);
|
||||
}
|
||||
|
||||
if (fileInfoVoList.isEmpty()) {
|
||||
return ApiResponse.failure("未成功上传任何文件");
|
||||
}
|
||||
|
||||
return ApiResponse.success(fileInfoVoList);
|
||||
}
|
||||
|
||||
@Operation(summary = "文件下载")
|
||||
@GetMapping("/download/{id}")
|
||||
public void downloadFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException {
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
String encodedFileName = URLEncoder.encode(fileInfo.getFileName(), StandardCharsets.UTF_8.name());
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件预览")
|
||||
@GetMapping("/preview/{id}")
|
||||
public void previewFile(
|
||||
@Parameter(description = "文件ID", required = true)
|
||||
@PathVariable String id,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头(不设置 attachment、让浏览器直接显示)
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse getFileList(
|
||||
@Parameter(description = "页码", required = true) Integer pageNum,
|
||||
@Parameter(description = "每页条数", required = true) Integer pageSize,
|
||||
@Parameter(description = "文件名称") String fileName) {
|
||||
|
||||
// 创建分页对象
|
||||
Page<FileInfo> page = new Page<>(pageNum, pageSize);
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (fileName != null && !fileName.isEmpty()) {
|
||||
queryWrapper.like(FileInfo::getFileName, fileName);
|
||||
}
|
||||
// 按创建时间倒序排列、最新上传的文件在前
|
||||
queryWrapper.orderByDesc(FileInfo::getCreatedAt);
|
||||
|
||||
// 执行分页查询
|
||||
IPage<FileInfo> fileInfoPage = fileInfoService.page(page, queryWrapper);
|
||||
|
||||
// 转换为VO对象并设置URL
|
||||
List<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> {
|
||||
FileInfoVo vo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, vo);
|
||||
vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId());
|
||||
vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId());
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 构建分页结果
|
||||
Page<FileInfoVo> resultPage = new Page<>();
|
||||
resultPage.setRecords(records);
|
||||
resultPage.setTotal(fileInfoPage.getTotal());
|
||||
resultPage.setSize(fileInfoPage.getSize());
|
||||
resultPage.setCurrent(fileInfoPage.getCurrent());
|
||||
resultPage.setPages(fileInfoPage.getPages());
|
||||
return ApiResponse.success(resultPage);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.graphhopper.GHRequest;
|
||||
import com.graphhopper.GHResponse;
|
||||
import com.graphhopper.GraphHopper;
|
||||
import com.graphhopper.ResponsePath;
|
||||
import com.graphhopper.config.Profile;
|
||||
import com.graphhopper.util.shapes.GHPoint;
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.yj.earth.common.config.GraphHopperProperties;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.model.Point;
|
||||
import com.yj.earth.model.RouteRequest;
|
||||
import com.yj.earth.model.RouteResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@Tag(name = "路径规划管理")
|
||||
@RestController
|
||||
@RequestMapping("/graphhopper")
|
||||
public class GraphHopperController {
|
||||
@Resource
|
||||
private FileInfoService fileInfoService;
|
||||
@Resource
|
||||
private GraphHopperProperties graphHopperProperties;
|
||||
|
||||
// 存储当前可用的 GraphHopper 实例
|
||||
private volatile GraphHopper currentHopper;
|
||||
|
||||
// 状态控制: 线程安全
|
||||
private final AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||
private final AtomicBoolean isLoaded = new AtomicBoolean(false);
|
||||
|
||||
@Operation(summary = "获取地图列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list() {
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileSuffix, "pbf");
|
||||
return ApiResponse.success(fileInfoService.list(queryWrapper));
|
||||
}
|
||||
|
||||
@Operation(summary = "加载地图数据")
|
||||
@PostMapping("/loadMap")
|
||||
public ApiResponse loadMap(@Parameter(description = "文件ID") @RequestParam String fileId) {
|
||||
// 参数校验
|
||||
if (fileId == null) {
|
||||
return ApiResponse.failure("文件ID不能为空");
|
||||
}
|
||||
|
||||
// 获取并校验OSM文件
|
||||
String osmFilePath = fileInfoService.getFileAbsolutePath(fileId);
|
||||
File osmFile = new File(osmFilePath);
|
||||
if (!osmFile.exists()) {
|
||||
return ApiResponse.failure("地图文件不存在: " + osmFilePath);
|
||||
}
|
||||
if (!osmFile.isFile() || !osmFile.getName().endsWith(".pbf")) {
|
||||
return ApiResponse.failure("仅支持有效的.pbf格式OSM文件");
|
||||
}
|
||||
// 防止并发加载
|
||||
if (isLoading.get()) {
|
||||
return ApiResponse.failure("地图正在加载中、请稍后查询状态");
|
||||
}
|
||||
|
||||
// 标记加载状态
|
||||
isLoading.set(true);
|
||||
isLoaded.set(false);
|
||||
|
||||
// 异步执行: 删除旧数据 → 创建新实例 → 加载新地图
|
||||
new Thread(() -> {
|
||||
GraphHopper newHopper = null;
|
||||
try {
|
||||
// 关键步骤1: 彻底删除旧地图数据目录
|
||||
deleteOldGraphDir();
|
||||
// 关键步骤2: 创建全新的GraphHopper实例
|
||||
newHopper = createNewGraphHopperInstance(osmFilePath);
|
||||
// 关键步骤3: 加载新地图
|
||||
newHopper.importOrLoad();
|
||||
// 关键步骤4: 加载成功 → 替换当前实例 + 更新状态
|
||||
currentHopper = newHopper;
|
||||
isLoaded.set(true);
|
||||
log.info("地图加载成功");
|
||||
} catch (Exception e) {
|
||||
// 加载失败 → 清理新实例资源
|
||||
if (newHopper != null) {
|
||||
newHopper.close();
|
||||
}
|
||||
isLoaded.set(false);
|
||||
e.printStackTrace();
|
||||
log.error("地图加载失败: " + e.getMessage());
|
||||
|
||||
} finally {
|
||||
// 无论成功/失败、释放加载锁
|
||||
isLoading.set(false);
|
||||
}
|
||||
}).start();
|
||||
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "路径规划")
|
||||
@PostMapping("/route")
|
||||
public ApiResponse calculateRoute(@RequestBody RouteRequest request) {
|
||||
// 校验地图是否加载完成 + 实例是否可用
|
||||
if (!isLoaded.get() || currentHopper == null) {
|
||||
return ApiResponse.failure("地图未加载完成");
|
||||
}
|
||||
try {
|
||||
// 构建路径点列表
|
||||
List<GHPoint> ghPoints = new ArrayList<>();
|
||||
ghPoints.add(new GHPoint(request.getStartLat(), request.getStartLng())); // 起点
|
||||
// 添加途经点
|
||||
if (request.getWaypoints() != null && !request.getWaypoints().isEmpty()) {
|
||||
for (Point waypoint : request.getWaypoints()) {
|
||||
ghPoints.add(new GHPoint(waypoint.getLat(), waypoint.getLng()));
|
||||
}
|
||||
}
|
||||
ghPoints.add(new GHPoint(request.getEndLat(), request.getEndLng())); // 终点
|
||||
|
||||
// 构建请求(仅指定Profile、无setWeighting)
|
||||
String targetProfile = request.getProfile() != null ? request.getProfile() : "car";
|
||||
GHRequest ghRequest = new GHRequest(ghPoints)
|
||||
.setProfile(targetProfile);
|
||||
|
||||
// 用新实例计算路径
|
||||
GHResponse response = currentHopper.route(ghRequest);
|
||||
|
||||
// 处理错误
|
||||
if (response.hasErrors()) {
|
||||
return ApiResponse.failure("路径计算失败: " + response.getErrors().toString());
|
||||
}
|
||||
|
||||
// 解析结果
|
||||
ResponsePath bestPath = response.getBest();
|
||||
List<Point> pathPoints = new ArrayList<>();
|
||||
bestPath.getPoints().forEach(ghPoint ->
|
||||
pathPoints.add(new Point(ghPoint.getLat(), ghPoint.getLon()))
|
||||
);
|
||||
|
||||
// 封装返回
|
||||
RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints);
|
||||
return ApiResponse.success(routeResponse);
|
||||
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("路径计算异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取交通方式")
|
||||
@PostMapping("/profiles")
|
||||
public ApiResponse profiles() {
|
||||
return ApiResponse.success(graphHopperProperties.getProfiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建全新的 GraphHopper 实例
|
||||
*/
|
||||
private GraphHopper createNewGraphHopperInstance(String osmFilePath) {
|
||||
GraphHopper hopper = new GraphHopper();
|
||||
// 配置基础参数
|
||||
hopper.setOSMFile(osmFilePath);
|
||||
hopper.setGraphHopperLocation(graphHopperProperties.getGraphLocation());
|
||||
|
||||
// 配置交通方式 + 权重策略
|
||||
List<Profile> profileList = new ArrayList<>();
|
||||
for (String profileName : graphHopperProperties.getProfiles()) {
|
||||
Profile profile = new Profile(profileName);
|
||||
profile.setVehicle(profileName);
|
||||
profile.setWeighting("fastest");
|
||||
profileList.add(profile);
|
||||
}
|
||||
hopper.setProfiles(profileList);
|
||||
|
||||
return hopper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除旧地图数据目录
|
||||
*/
|
||||
private void deleteOldGraphDir() {
|
||||
File graphDir = new File(graphHopperProperties.getGraphLocation());
|
||||
if (!graphDir.exists()) {
|
||||
log.info("旧地图目录不存在、无需删除: " + graphHopperProperties.getGraphLocation());
|
||||
return;
|
||||
}
|
||||
|
||||
// 递归删除所有文件和子目录
|
||||
File[] files = graphDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteOldGraphDir(file);
|
||||
} else {
|
||||
boolean deleted = file.delete();
|
||||
log.info("删除旧地图文件: " + file.getAbsolutePath() + " → " + (deleted ? "成功" : "失败"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除空目录
|
||||
boolean dirDeleted = graphDir.delete();
|
||||
System.out.println("删除旧地图目录: " + graphDir.getAbsolutePath() + " → " + (dirDeleted ? "成功" : "失败"));
|
||||
}
|
||||
|
||||
// 重载: 递归删除子目录
|
||||
private void deleteOldGraphDir(File subDir) {
|
||||
File[] files = subDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteOldGraphDir(file);
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
subDir.delete();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.dto.role.AddRoleDto;
|
||||
import com.yj.earth.dto.role.UpdateRoleDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Tag(name = "角色数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/role")
|
||||
public class RoleController {
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@Operation(summary = "新增角色")
|
||||
@PostMapping("/add")
|
||||
public ApiResponse save(@RequestBody AddRoleDto addRoleDto) {
|
||||
Role role = new Role();
|
||||
BeanUtils.copyProperties(addRoleDto, role);
|
||||
roleService.save(role);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色")
|
||||
@PostMapping("/delete")
|
||||
public ApiResponse delete(@Parameter(description = "角色ID") String id) {
|
||||
roleService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse update(@RequestBody UpdateRoleDto updateRoleDto) {
|
||||
Role role = new Role();
|
||||
BeanUtils.copyProperties(updateRoleDto, role);
|
||||
roleService.updateById(role);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "角色详情")
|
||||
@GetMapping("/getById")
|
||||
public ApiResponse get(@Parameter(description = "角色ID") String id) {
|
||||
Role role = roleService.getById(id);
|
||||
return ApiResponse.success(role);
|
||||
}
|
||||
|
||||
@Operation(summary = "角色列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize) {
|
||||
Page<Role> rolePage = roleService.page(new Page<>(pageNum, pageSize));
|
||||
return ApiResponse.success(rolePage);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.dto.relation.RoleBindOrUnBindSourceDto;
|
||||
import com.yj.earth.dto.relation.SourceBindOrUnBindRoleDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "角色资源管理")
|
||||
@RestController
|
||||
@RequestMapping("/roleSource")
|
||||
public class RoleSourceController {
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
|
||||
@Operation(summary = "角色绑定资源")
|
||||
@PostMapping("/roleBindSource")
|
||||
public ApiResponse roleBindSource(@RequestBody RoleBindOrUnBindSourceDto roleBindOrUnBindSourceDto) {
|
||||
// 先删除该角色下的所有资源
|
||||
roleSourceService.remove(new LambdaQueryWrapper<RoleSource>().eq(RoleSource::getRoleId, roleBindOrUnBindSourceDto.getRoleId()));
|
||||
// 再设置新的资源
|
||||
for (String sourceId : roleBindOrUnBindSourceDto.getSourceIdList()) {
|
||||
RoleSource roleSource = new RoleSource();
|
||||
roleSource.setRoleId(roleBindOrUnBindSourceDto.getRoleId());
|
||||
roleSource.setSourceId(sourceId);
|
||||
roleSourceService.save(roleSource);
|
||||
}
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,168 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.service.SourceParamsValidator;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.MapUtil;
|
||||
import com.yj.earth.dto.source.AddDirectoryDto;
|
||||
import com.yj.earth.dto.source.AddModelSourceDto;
|
||||
import com.yj.earth.dto.source.AddOtherSourceDto;
|
||||
import com.yj.earth.dto.source.UpdateSourceDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.yj.earth.common.constant.GlobalConstant.DIRECTORY;
|
||||
import static com.yj.earth.common.constant.GlobalConstant.SHOW;
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "树形结构管理")
|
||||
@RestController
|
||||
@RequestMapping("/source")
|
||||
public class SourceController {
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
@Resource
|
||||
private SourceParamsValidator sourceParamsValidator;
|
||||
|
||||
@PostMapping("/addDirectory")
|
||||
@Operation(summary = "新增目录资源")
|
||||
public ApiResponse addDirectory(@RequestBody AddDirectoryDto addDirectoryDto) {
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addDirectoryDto.getParentId(), addDirectoryDto.getSourceName());
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 通过之后保存资源
|
||||
Source source = new Source();
|
||||
BeanUtils.copyProperties(addDirectoryDto, source);
|
||||
source.setSourceType(DIRECTORY);
|
||||
source.setIsShow(SHOW);
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
@Operation(summary = "新增模型资源")
|
||||
@PostMapping("/addModelSource")
|
||||
public ApiResponse addModelSource(@RequestBody AddModelSourceDto addModelSourceDto) {
|
||||
// 获取资源绝对路径
|
||||
String sourcePath = addModelSourceDto.getSourcePath();
|
||||
// 获取资源名称
|
||||
String sourceName = FileUtil.mainName(sourcePath);
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addModelSourceDto.getParentId(), sourceName);
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 调用SDK加载资源
|
||||
String sourceId = sourceService.addAndGetSourceId(sourcePath);
|
||||
// 获取文件路径并处理详情
|
||||
String detail = sourceService.getDetail(sourcePath, sourceId);
|
||||
// 构建并保存资源对象
|
||||
Source source = new Source();
|
||||
source.setSourcePath(sourcePath);
|
||||
source.setSourceName(sourceName);
|
||||
source.setParentId(addModelSourceDto.getParentId());
|
||||
source.setTreeIndex(addModelSourceDto.getTreeIndex());
|
||||
source.setDetail(detail);
|
||||
source.setSourceType(MapUtil.getString(MapUtil.jsonToMap(detail), "fileType"));
|
||||
source.setIsShow(SHOW);
|
||||
source.setParams(addModelSourceDto.getParams());
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "新增其他资源")
|
||||
@PostMapping("/addOtherSource")
|
||||
public ApiResponse addOtherSource(@RequestBody AddOtherSourceDto addOtherSourceDto) throws JsonProcessingException {
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addOtherSourceDto.getParentId(), addOtherSourceDto.getSourceName());
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 验证并转换参数
|
||||
Object validatedParams = sourceParamsValidator.validateAndConvert(
|
||||
addOtherSourceDto.getSourceType(),
|
||||
addOtherSourceDto.getParams()
|
||||
);
|
||||
System.out.println(validatedParams);
|
||||
Source source = new Source();
|
||||
BeanUtils.copyProperties(addOtherSourceDto, source);
|
||||
source.setIsShow(SHOW);
|
||||
source.setParams(MapUtil.objectToJson(validatedParams));
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "更新资源信息及参数")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse updateSource(@RequestBody UpdateSourceDto updateSourceDto) {
|
||||
// 查询资源
|
||||
Source source = sourceService.getById(updateSourceDto.getId());
|
||||
if (source == null) {
|
||||
return ApiResponse.failure("资源不存在");
|
||||
}
|
||||
|
||||
// 更新基本信息
|
||||
BeanUtils.copyProperties(updateSourceDto, source);
|
||||
|
||||
// 处理参数更新
|
||||
if (updateSourceDto.getParams() != null && !updateSourceDto.getParams().isEmpty()) {
|
||||
// 获取类型
|
||||
String sourceType = source.getSourceType();
|
||||
// 验证参数
|
||||
Object validatedParams = sourceParamsValidator.validateAndConvert(
|
||||
sourceType,
|
||||
updateSourceDto.getParams()
|
||||
);
|
||||
|
||||
// 获取原始数据的 Map 并合并新参数
|
||||
Map<String, Object> dataMap = MapUtil.jsonToMap(source.getParams());
|
||||
MapUtil.mergeMaps(dataMap, updateSourceDto.getParams());
|
||||
source.setParams(MapUtil.mapToString(dataMap));
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
sourceService.updateById(source);
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
@GetMapping("/type")
|
||||
@Operation(summary = "获取支持的资源类型")
|
||||
public ApiResponse getSupportedSourceTypes() {
|
||||
Set<String> supportedTypes = sourceParamsValidator.getSupportedSourceTypes();
|
||||
return ApiResponse.success(supportedTypes);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "获取资源列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list() {
|
||||
return ApiResponse.success(sourceService.getSourceListByUserId(StpUtil.getLoginIdAsString()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.annotation.EncryptResponse;
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import com.yj.earth.annotation.RoleAccess;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.dto.relation.UserBindOrUnBindRoleDto;
|
||||
import com.yj.earth.dto.user.AddUserDto;
|
||||
import com.yj.earth.dto.user.UpdatePasswordDto;
|
||||
import com.yj.earth.dto.user.UpdateUserDto;
|
||||
import com.yj.earth.dto.user.UserLoginDto;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "用户数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@Operation(summary = "新增用户")
|
||||
@PostMapping("/add")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse save(@RequestBody AddUserDto addUserDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(addUserDto, user);
|
||||
if (userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, user.getUsername())) != null) {
|
||||
return ApiResponse.failure("用户已存在");
|
||||
}
|
||||
String password = user.getPassword();
|
||||
user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
if (addUserDto.getRoleId() == null) {
|
||||
// 查询系统名字为默认角色的角色ID
|
||||
user.setRoleId(roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getRoleName, "默认角色")).getId());
|
||||
}
|
||||
userService.save(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户")
|
||||
@PostMapping("/delete")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse delete(@Parameter(description = "用户ID") String id) {
|
||||
userService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新信息")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse update(@RequestBody UpdateUserDto updateUserDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(updateUserDto, user);
|
||||
userService.updateById(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新密码")
|
||||
@PostMapping("/updatePassword")
|
||||
public ApiResponse updatePassword(@RequestBody UpdatePasswordDto updatePasswordDto) {
|
||||
User user = userService.getById(updatePasswordDto.getId());
|
||||
if (user == null) {
|
||||
return ApiResponse.failure("用户不存在");
|
||||
}
|
||||
if (!BCrypt.checkpw(updatePasswordDto.getOldPassword(), user.getPassword())) {
|
||||
return ApiResponse.failure("旧密码错误");
|
||||
}
|
||||
user.setPassword(BCrypt.hashpw(updatePasswordDto.getNewPassword(), BCrypt.gensalt()));
|
||||
userService.updateById(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户详情")
|
||||
@GetMapping("/getById")
|
||||
public ApiResponse get(@Parameter(description = "用户ID") String id) {
|
||||
return ApiResponse.success(userService.getById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "用户列表")
|
||||
@GetMapping("/list")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize) {
|
||||
Page<User> userPage = userService.page(new Page<>(pageNum, pageSize));
|
||||
return ApiResponse.success(userPage);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录")
|
||||
@PostMapping("/login")
|
||||
public ApiResponse login(@RequestBody UserLoginDto userLoginDto) {
|
||||
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userLoginDto.getUsername()));
|
||||
if (user == null) {
|
||||
return ApiResponse.failure("用户名不存在");
|
||||
}
|
||||
if (!BCrypt.checkpw(userLoginDto.getPassword(), user.getPassword())) {
|
||||
return ApiResponse.failure("密码错误");
|
||||
}
|
||||
StpUtil.login(user.getId());
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
return ApiResponse.success(Map.of("header", tokenInfo.getTokenName(), "token", tokenInfo.getTokenValue()));
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登出")
|
||||
@PostMapping("/logout")
|
||||
public ApiResponse logout() {
|
||||
StpUtil.logout();
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "设置角色")
|
||||
@PostMapping("/userBindOrUnBindRole")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse userBindOrUnBindRole(@RequestBody UserBindOrUnBindRoleDto userBindOrUnBindRoleDto) {
|
||||
userService.lambdaUpdate().set(User::getRoleId, userBindOrUnBindRoleDto.getRoleId()).eq(User::getId, userBindOrUnBindRoleDto.getUserId()).update();
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取当前用户信息")
|
||||
@GetMapping("/getCurrentUserInfo")
|
||||
public ApiResponse getCurrentUserInfo() {
|
||||
return ApiResponse.success(userService.getById(StpUtil.getLoginIdAsString()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user