提交代码

This commit is contained in:
lilin 2025-05-23 17:46:23 +08:00
parent c228d181b0
commit dd2c65eabb
25 changed files with 2133 additions and 278 deletions

View File

@ -19,6 +19,22 @@ public class TaskStatusHolder {
return dataset + ":" ;
}
// 生成专项扫描唯一Keyproject_id
public String specialGenerateKey(String id) {
return id + ":";
}
// 生成专项文档上传唯一Keyproject_id+上传:
public String documentUploadKey(String id) {
return id + "上传:";
}
// 生成试验任务扫描唯一Keytask_id
public String testDatascanKey(String id) {
return id + ":";
}
// 原子性检查并标记任务开始

View File

@ -193,18 +193,19 @@ public class TsFilesController {
* 参数说明 compressedPath 压缩文件路径
* 参数说明 covered 是否覆盖 0 覆盖更新updateTime时间 1提示文件存在
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
@Log(module = "实验数据管理", value = "压缩文件夹接口!")
@PostMapping("/compress")
@ApiOperation("压缩文件夹接口")
public ResponseResult compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId) {
public ResponseResult compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId,String path) {
try {
if (StrUtil.isBlank(ids) && StrUtil.isBlank(compressedFormat) && StrUtil.isBlank(compressedName) && StrUtil.isBlank(compressedPath)) {
if (StrUtil.isBlank(ids) && StrUtil.isBlank(compressedFormat) && StrUtil.isBlank(compressedName) && StrUtil.isBlank(compressedPath)&& StrUtil.isBlank(path)) {
return ResponseResult.error("参数为空");
}
return ResponseResult.success(tsFilesService.compressFolder(ids, compressedFormat, compressedName, compressedPath, covered, parentId));
return ResponseResult.success(tsFilesService.compressFolder(ids, compressedFormat, compressedName, compressedPath, covered, parentId, path));
} catch (Exception e) {
System.out.print("压缩异常原因" + e);
return ResponseResult.error("压缩失败");
@ -216,18 +217,19 @@ public class TsFilesController {
* 参数说明 id 要解压的文件id
* 参数说明 decompressionPath 解压缩路径
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
@Log(module = "实验数据管理", value = "解压缩接口!")
@PostMapping("/decompression")
@ApiOperation("解压缩接口")
public ResponseResult decompressionFolder(String id, String decompressionPath, String parentId) {
public ResponseResult decompressionFolder(String id, String decompressionPath, String parentId,String path) {
try {
if (StrUtil.isBlank(id)) {
return ResponseResult.error("参数为空");
}
return ResponseResult.success(tsFilesService.decompressionFolder(id, decompressionPath, parentId));
return ResponseResult.success(tsFilesService.decompressionFolder(id, decompressionPath, parentId,path));
} catch (Exception e) {
System.out.print("解压缩异常原因" + e);
return ResponseResult.error("解压缩失败");
@ -369,8 +371,6 @@ public class TsFilesController {
Page<TsFiles> tsfilesPage = tsFilesService.compareLocal(dataset, nodeId, taskId, page);
// List<TsFilesDTO> dtos = tsFilesService.compareLocal(dataset, nodeId, taskId);
// DualTreeResponse response = tsFilesService.compareLocal(dataset, nodeId, taskId);
return ResponseResult.successData(tsfilesPage);
} catch (Exception e) {
return ResponseResult.error("对比失败");
@ -600,6 +600,37 @@ public class TsFilesController {
}
}
/**********************************
* 用途说明: 获取异步信息
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "获取异步信息", value = "获取异步信息!")
@PostMapping("/automaticFileBackupData")
@ApiOperation("获取异步信息")
public ResponseResult automaticFileBackupData(String id) throws Exception {
if (StrUtil.isEmpty(id) ) {
return ResponseResult.error("参数为空");
}
List<String> dataset = StrUtil.split(id, ",");
// 生成唯一Key
String asyncKey = taskStatusHolder.generateKeybyId(dataset);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("TASK_NOT_FOUND".equals(existingStatus)) {
return ResponseResult.success("1");
}
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("0");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("自动备份任务已完成!");
}
return null;
}
/**********************************
* 用途说明: 实时获取轨迹数据
* 参数说明 id 文件的ID

View File

@ -4,12 +4,14 @@ package com.yfd.platform.modules.experimentalData.controller;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.yfd.platform.annotation.Log;
import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.TsNodes;
import com.yfd.platform.modules.experimentalData.service.ITsNodesService;
import com.yfd.platform.modules.experimentalData.service.ITsTaskService;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@ -35,6 +37,10 @@ public class TsNodesController {
private ITsNodesService tsNodesService;
@Autowired
private TaskStatusHolder taskStatusHolder;
/***********************************
* 用途说明获取试验任务节点 树形结构
* 参数说明
@ -48,7 +54,7 @@ public class TsNodesController {
@ResponseBody
@PreAuthorize("@el.check('select:tsnodes')")
public ResponseResult getTsNodesTree(String nodeName, String taskId) {
List<Map<String, Object>> list = tsNodesService.getTsNodesTree(nodeName,taskId);
List<Map<String, Object>> list = tsNodesService.getTsNodesTree(nodeName, taskId);
return ResponseResult.successData(list);
}
@ -63,7 +69,7 @@ public class TsNodesController {
@PreAuthorize("@el.check('add:tsnodes')")
public ResponseResult addTsNodes(@RequestBody TsNodes tsnodes) {
//参数校验 对象 节点名称 所属任务ID
if (ObjUtil.isEmpty(tsnodes) && StrUtil.isBlank(tsnodes.getNodeName()) && StrUtil.isBlank(tsnodes.getTaskId()) ) {
if (ObjUtil.isEmpty(tsnodes) && StrUtil.isBlank(tsnodes.getNodeName()) && StrUtil.isBlank(tsnodes.getTaskId())) {
return ResponseResult.error("参数为空");
}
return tsNodesService.addTsNodes(tsnodes);
@ -90,17 +96,18 @@ public class TsNodesController {
/**********************************
* 用途说明: 根据ID删除试验任务节点
* 参数说明 id 试验任务节点ID
* 参数说明 path 路径
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
@Log(module = "试验数据管理", value = "根据ID删除试验任务节点!")
@PostMapping("/deleteTsNodesById")
@ApiOperation("根据ID删除试验任务节点")
@PreAuthorize("@el.check('del:tsnodes')")
public ResponseResult deleteTsNodesById(@RequestParam String id) {
public ResponseResult deleteTsNodesById(@RequestParam String id,String path) {
if (StrUtil.isBlank(id)) {
return ResponseResult.error("参数为空");
}
boolean isOk = tsNodesService.deleteTsNodesById(id);
boolean isOk = tsNodesService.deleteTsNodesById(id,path);
if (isOk) {
return ResponseResult.success();
} else {
@ -108,4 +115,67 @@ public class TsNodesController {
}
}
/**********************************
* 用途说明: 试验数据扫描接口
* 参数说明 id 试验任务ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "试验数据扫描接口", value = "试验数据扫描接口!")
@PostMapping("/testDataScanById")
@ApiOperation("试验数据扫描接口通过试验任务ID")
public ResponseResult testDataScanByIdAsync(String id) throws Exception {
if (StrUtil.isEmpty(id)) {
return ResponseResult.error("参数为空");
}
// 生成唯一Key
String asyncKey = taskStatusHolder.testDatascanKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("试验数据扫描任务正在处理中!");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("验数据扫描任务已完成!");
}
// 原子性启动新任务
if (taskStatusHolder.startTaskIfAbsent(asyncKey)) {
// 直接异步执行并推送结果
tsNodesService.testDataScanByIdAsync(id);
return ResponseResult.success("验数据扫描任务开始处理!");
} else {
return ResponseResult.success("验数据扫描任务已由其他请求启动!");
}
}
/**********************************
* 用途说明: 获取异步信息
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "获取异步信息", value = "获取异步信息!")
@PostMapping("/obtaintestData")
@ApiOperation("获取异步信息")
public ResponseResult obtaintestData(String id) throws Exception {
if (StrUtil.isEmpty(id)) {
return ResponseResult.error("id为空");
}
// 生成唯一Key
String asyncKey = taskStatusHolder.testDatascanKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("TASK_NOT_FOUND".equals(existingStatus)) {
return ResponseResult.success("1");
}
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("0");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务已完成!");
}
return null;
}
}

View File

@ -43,8 +43,12 @@ public class TsTaskController {
* endDate 结束日期
* taskPlace 任务地点
* taskPerson 任务人员
* carrierType 载体类型
* deviceCode 设备
* taskCode 任务编号
* taskType 任务类型
* carrierName 任务人员
* deviceCode 任务人员
* testDescribe 任务人员
* sensorDescribe 任务人员
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
@ -52,9 +56,9 @@ public class TsTaskController {
@GetMapping("/page")
@ApiOperation("分页查询试验数据管理试验任务管理")
@PreAuthorize("@el.check('select:tsTask')")
public ResponseResult getTsTaskPage(String taskName, String startDate, String endDate,String taskPlace, String taskPerson, String carrierType, String deviceCode, Page<TsTask> page) {
public ResponseResult getTsTaskPage(String taskName, String startDate, String endDate,String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page<TsTask> page) {
//分页查询
Page<TsTask> sdProjectPage = tsTaskService.getTsTaskPage(taskName, startDate, endDate,taskPlace,taskPerson,carrierType,deviceCode, page);
Page<TsTask> sdProjectPage = tsTaskService.getTsTaskPage(taskName, startDate, endDate,taskPlace,taskPerson,taskCode,taskType,carrierName,deviceCode,testDescribe,sensorDescribe, page);
return ResponseResult.successData(sdProjectPage);
}
@ -163,7 +167,7 @@ public class TsTaskController {
//@PreAuthorize("@el.check('select:devicesignal')")
public ResponseResult listTsTask() {
LambdaQueryWrapper<TsTask> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(TsTask::getTaskDate);
queryWrapper.orderByDesc(TsTask::getTaskStartdate);
List<TsTask> tsTasks = tsTaskService.list(queryWrapper);
return ResponseResult.successData(tsTasks);
}

View File

@ -1,6 +1,7 @@
package com.yfd.platform.modules.experimentalData.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -79,5 +80,9 @@ public class TsNodes implements Serializable {
*/
private String custom3;
/**
* TODO路径用于拼接
*/
@TableField(exist = false)
private String path;
}

View File

@ -64,11 +64,11 @@ public class TsTask implements Serializable {
*/
private String deviceName;
/**
* 任务开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp taskDate;
// /**
// * 任务开始时间
// */
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
// private Timestamp taskDate;
/**
* 任务地点
@ -111,5 +111,34 @@ public class TsTask implements Serializable {
*/
private String custom3;
/**
* 任务类型
*/
private String taskType;
/**
* 试验描述
*/
private String testDescribe;
/**
* 传感器描述
*/
private String sensorDescribe;
/**
* 任务开始时间
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate taskStartdate;
/**
* 任务结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate taskEnddate;
}

View File

@ -69,18 +69,20 @@ public interface ITsFilesService extends IService<TsFiles> {
* 参数说明 compressedPath 压缩文件路径
* 参数说明 covered 是否覆盖 0 覆盖更新updateTime时间 1提示文件存在
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
String compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId) throws FileNotFoundException;
String compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId,String path) throws FileNotFoundException;
/**********************************
* 用途说明: 解压缩接口
* 参数说明 id 要解压的文件id
* 参数说明 decompressionPath 解压缩路径
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
String decompressionFolder(String id, String decompressionPath, String parentId);
String decompressionFolder(String id, String decompressionPath, String parentId, String path);
/**********************************
* 用途说明: 将文件上传到备份空间

View File

@ -43,10 +43,18 @@ public interface ITsNodesService extends IService<TsNodes> {
/**********************************
* 用途说明: 根据ID删除试验任务节点
* 参数说明 id 试验任务节点ID
* 参数说明 path 路径
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
boolean deleteTsNodesById(String id);
boolean deleteTsNodesById(String id,String path);
boolean deleteTsNodesByTaskId(String taskId);
boolean deleteTsNodesByTaskId(String taskId,String path);
/**********************************
* 用途说明: 试验数据扫描接口
* 参数说明 id 试验任务ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
void testDataScanByIdAsync(String id) throws Exception;
}

View File

@ -16,6 +16,7 @@ import java.util.List;
*/
public interface ITsTaskService extends IService<TsTask> {
/**********************************
* 用途说明: 分页查询试验数据管理-试验任务管理
* 参数说明
@ -24,12 +25,16 @@ public interface ITsTaskService extends IService<TsTask> {
* endDate 结束日期
* taskPlace 任务地点
* taskPerson 任务人员
* carrierType 载体类型
* deviceCode 设备
* taskCode 任务编号
* taskType 任务类型
* carrierName 任务人员
* deviceCode 任务人员
* testDescribe 任务人员
* sensorDescribe 任务人员
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String carrierType, String deviceCode, Page<TsTask> page);
Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page<TsTask> page);
/***********************************
* 用途说明新增试验数据管理-试验任务管理

View File

@ -32,6 +32,8 @@ import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.*;
import com.yfd.platform.modules.experimentalData.mapper.TsFilesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsNodesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsTaskMapper;
import com.yfd.platform.modules.experimentalData.service.ITsFilesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
@ -111,6 +113,12 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Resource
private TsTaskMapper tsTaskMapper;
@Resource
private TsNodesMapper tsNodesMapper;
// 从数据库获取的压缩类型列表
private List<String> compressSuffixes;
private final Set<String> addedEntries = new HashSet<>();
@ -239,8 +247,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
if (tsFiles.getUpdateTime() == null) {
tsFiles.setUpdateTime(tsFiles.getUploadTime());
}
String ProcessingPath = processingPath(tsFiles.getWorkPath(), nodeId);
tsFiles.setWorkPath(ProcessingPath);
// String ProcessingPath = processingPath(tsFiles.getWorkPath(), nodeId);
// tsFiles.setWorkPath(ProcessingPath);
}
tsFilesPage.setRecords(records); // 同步到 tsFilesPage
System.out.println("Updated records: " + records);
@ -332,10 +340,10 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
queryWrapper.isNotNull("work_path");
List<TsFiles> tsFiles = tsFilesMapper.selectList(queryWrapper);
for (TsFiles tsFiles1 : tsFiles) {
String ProcessingPath = processingPath(tsFiles1.getWorkPath(), nodeId);
tsFiles1.setWorkPath(ProcessingPath);
}
// for (TsFiles tsFiles1 : tsFiles) {
// String ProcessingPath = processingPath(tsFiles1.getWorkPath(), nodeId);
// tsFiles1.setWorkPath(ProcessingPath);
// }
return tsFiles;
}
@ -475,7 +483,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
public ResponseResult addTsFile(TsFiles tsFiles) throws IOException {
if (tsFiles.getIsFile().equals("FILE")) {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
String basePath = config.getValue() + tsFiles.getWorkPath();
// 拼接完整的文件路径
Path filePath = Paths.get(basePath, tsFiles.getFileName() + ".txt");
@ -1107,14 +1115,13 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
* 参数说明 compressedPath 压缩文件路径
* 参数说明 covered 是否覆盖 0 覆盖更新updateTime时间 1提示文件存在
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
@Override
public String compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId) throws FileNotFoundException {
public String compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId, String path) throws FileNotFoundException {
//获取本地根路径
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
StorageSourceConfig storageSourceConfig = getStorageConfig("filePath", "local");
compressedPath = normalizePath(compressedPath);
@ -1137,16 +1144,16 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 构建完整路径
String sourceDir = storageSourceConfig.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName();
Path path = Paths.get(sourceDir);
Path workpath = Paths.get(sourceDir);
// 检查路径是否存在
if (!Files.exists(path)) {
if (!Files.exists(workpath)) {
throw new FileNotFoundException("路径不存在: " + path);
}
// 添加到压缩列表
sourceDirs.add(path); // 修复使用可变集合
sourceDirs.add(workpath); // 修复使用可变集合
}
try {
String finalParentId = ensureFullPathExists(compressedPath, filesList.get(0).getNodeId(), filesList.get(0).getTaskId());
String finalParentId = ensureFullPathExists(compressedPath, filesList.get(0).getNodeId(), filesList.get(0).getTaskId(), path);
parentId = finalParentId;
LOGGER.info("路径已全部存在最终目录ID: {}", finalParentId);
} catch (RuntimeException e) {
@ -1158,7 +1165,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 规范化压缩包存放路径
// 如果 compressedPath 是根目录使用当前工作目录
Path zipFilePath = Paths.get(storageSourceConfig.getValue() + compressedPath + compressedName + "." + compressedFormat);
Path zipFilePath = Paths.get(storageSourceConfig.getValue() + path + compressedPath + compressedName + "." + compressedFormat);
// 检查目标路径是否可写
if (!Files.isWritable(zipFilePath.getParent())) {
@ -1173,11 +1180,13 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 调用压缩方法
Boolean value = compressToSameDirectory(sourceDirs, compressedFormat, zipFilePath);
if (value) {
String workPath = normalizePath(path + compressedPath);
//表结构增加 nodeId, String TaskId
TsFiles tsFiles = new TsFiles();
tsFiles.setTaskId(filesList.get(0).getTaskId());
tsFiles.setNodeId(filesList.get(0).getNodeId());
tsFiles.setWorkPath(compressedPath);
tsFiles.setWorkPath(workPath);
tsFiles.setIsFile("FILE");
// 获取文件大小字节
@ -1208,7 +1217,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
QueryWrapper<TsFiles> queryWrapperTsFiles = new QueryWrapper<>();
queryWrapperTsFiles.eq("node_id", filesList.get(0).getNodeId());
queryWrapperTsFiles.eq("task_id", filesList.get(0).getTaskId());
queryWrapperTsFiles.eq("work_path", compressedPath);
queryWrapperTsFiles.eq("work_path", workPath);
queryWrapperTsFiles.eq("file_name", zipFileName);
queryWrapperTsFiles.eq("parent_id", filesList.get(0).getParentId());
TsFiles tsFilesdata = tsFilesMapper.selectOne(queryWrapperTsFiles);
@ -1259,36 +1268,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
* @param nodeId 当前节点ID 38240bbb160450e03de33df911937d59
* @return 最终目录的数据库ID即最后一级目录的ID
*/
public String ensureFullPathExists(String compressedPath, String nodeId, String taskId) {
// 1. 解析路径提取有效层级目录名跳过nodeId部分
List<String> pathSegments = Arrays.stream(compressedPath.split("/"))
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
// 校验路径是否以nodeId开头
if (pathSegments.isEmpty() || !pathSegments.get(0).equals(nodeId)) {
throw new RuntimeException("路径必须包含当前节点ID");
}
String nodePath = "/" + nodeId + "/";
if (compressedPath.equals(nodePath)) {
return "00";
}
// 提取实际要处理的目录层级去掉开头的nodeId
List<String> dirSegments = null;
if (pathSegments.size() > 1) {
// 如果 pathSegments 大于 1去掉 nodeId
dirSegments = pathSegments.subList(1, pathSegments.size());
}
if (dirSegments.isEmpty()) {
throw new RuntimeException("路径缺少有效目录层级");
}
// 2. 初始化根目录信息基于nodeId
String parentId = "00"; // 根目录的父ID为0
String baseFsPath = "/" + nodeId + "/"; // 本地存储基础路径
public String ensureFullPathExists(String compressedPath, String nodeId, String taskId, String currentPath) {
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
@ -1298,13 +1278,42 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
// 1. 解析路径提取有效层级目录名跳过nodeId部分
List<String> pathSegments = Arrays.stream(compressedPath.split("/"))
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
// // 校验路径是否以nodeId开头
// if (pathSegments.isEmpty() || !pathSegments.get(0).equals(nodeId)) {
// throw new RuntimeException("路径必须包含当前节点ID");
// }
// String nodePath = "/" + nodeId + "/";
// if (compressedPath.equals(nodePath)) {
// return "00";
// }
// 提取实际要处理的目录层级去掉开头的nodeId
// List<String> dirSegments = null;
// if (pathSegments.size() > 1) {
// // 如果 pathSegments 大于 1去掉 nodeId
// dirSegments = pathSegments.subList(1, pathSegments.size());
// }
// if (dirSegments.isEmpty()) {
// throw new RuntimeException("路径缺少有效目录层级");
// }
// 2. 初始化根目录信息基于nodeId
String parentId = "00"; // 根目录的父ID为0
// 3. 逐层处理目录从第一个有效目录开始
String currentPath = baseFsPath;
for (String dirName : dirSegments) {
// 检查当前层级目录是否存在
for (String dirName : pathSegments) {
//如果为空 查询文件表 // 检查当前层级目录是否存在
TsFiles dir = tsFilesMapper.selectOne(new QueryWrapper<TsFiles>()
.eq("node_id", nodeId)
.eq("task_id", taskId)
.eq("node_id", nodeId)
.eq("parent_id", parentId)
.eq("work_path", currentPath)
.eq("file_name", dirName));
@ -1740,25 +1749,26 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
* 参数说明 id 要解压的文件id
* 参数说明 decompressionPath 解压缩路径
* 参数说明 parentId 父ID
* 参数说明 path 根目录 /项目名/节点名称/
* 返回值说明: com.yfd.platform.config.ResponseResult
***********************************/
@Override
public String decompressionFolder(String id, String decompressionPath, String parentId) {
public String decompressionFolder(String id, String decompressionPath, String parentId, String path) {
// 查询本地文件路径根目录 E:\yun
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
StorageSourceConfig storageSourceConfig = getStorageConfig("filePath", "local");
decompressionPath = normalizePath(decompressionPath);
// 1. 获取压缩包记录
TsFiles zipFileRecord = tsFilesMapper.selectById(id);
try {
String finalParentId = ensureFullPathExists(decompressionPath, zipFileRecord.getNodeId(), zipFileRecord.getTaskId());
String finalParentId = ensureFullPathExists(decompressionPath, zipFileRecord.getNodeId(), zipFileRecord.getTaskId(), path);
parentId = finalParentId;
LOGGER.info("路径已全部存在最终目录ID: {}", finalParentId);
} catch (RuntimeException e) {
LOGGER.error("路径创建失败: {}", e.getMessage());
return "路径创建失败!";
}
//解压以后的路径
String extractPath = normalizePath(path + decompressionPath);
String zipName = getFileNameWithoutExtension(zipFileRecord.getFileName()); // 示例222
try {
@ -1770,10 +1780,10 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
Path destRoot;
if (hasFolder) {
// 如果有文件夹创建子目录 E:/yun/333/222/
destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath, zipName);
destRoot = Paths.get(storageSourceConfig.getValue(), extractPath, zipName);
} else {
// 如果只有文件直接解压到目标目录 E:/yun/333/
destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath);
destRoot = Paths.get(storageSourceConfig.getValue(), extractPath);
}
Files.createDirectories(destRoot);
@ -1789,7 +1799,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
TsFiles rootFolder = createFolderRecord(
zipName, // 文件夹名称222
parentId, // 父ID是333的ID
buildFolderPath(decompressionPath), // 修正为 /333/222/
buildFolderPath(extractPath), // 修正为 /333/222/
zipFileRecord.getTaskId(),
zipFileRecord.getNodeId(),
zipFileRecord.getUploader()
@ -1800,7 +1810,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
processFolderContents(
unzippedRoot,
rootFolderId,
buildFolderPath(decompressionPath + "/" + zipName + "/"), // 修正为 /333/222/
buildFolderPath(extractPath + "/" + zipName + "/"), // 修正为 /333/222/
zipFileRecord.getTaskId(),
zipFileRecord.getNodeId(),
zipFileRecord.getUploader()
@ -2509,17 +2519,17 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 递归查询每个记录的子节点并添加到 records
List<TsFiles> allFiles = new ArrayList<>();
if(StringUtils.isNotBlank(nodeId)&&StringUtils.isNotBlank(taskId)){
if (StringUtils.isNotBlank(nodeId) && StringUtils.isNotBlank(taskId)) {
for (TsFiles tsFiles : parentFiles) {
tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// 如果备份路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getBackupPath())) {
allFiles.add(tsFiles);
}
}
}else {
} else {
for (TsFiles tsFiles : parentFiles) {
tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// 如果备份路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getBackupPath()) && "FILE".equals(tsFiles.getIsFile())) {
allFiles.add(tsFiles);
@ -2568,7 +2578,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 对每个子节点递归查询其子节点
for (TsFiles child : children) {
child.setWorkPath(processingPath(child.getWorkPath(), child.getNodeId())); // 优化点11路径处理内聚
// child.setWorkPath(processingPath(child.getWorkPath(), child.getNodeId())); // 优化点11路径处理内聚
if (StringUtils.isEmpty(child.getBackupPath())) {
allFiles.add(child);
}
@ -2609,7 +2619,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 递归查询每个记录的子节点并添加到 records
List<TsFiles> allFiles = new ArrayList<>();
for (TsFiles tsFiles : records) {
tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// 如果备份路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getBackupPath())) {
allFiles.add(tsFiles);
@ -2661,17 +2671,17 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
List<TsFiles> parentFiles = tsFilesMapper.selectList(queryWrapper);
// 递归查询每个记录的子节点并添加到 records
List<TsFiles> allFiles = new ArrayList<>();
if(StringUtils.isNotBlank(nodeId)&&StringUtils.isNotBlank(taskId)){
if (StringUtils.isNotBlank(nodeId) && StringUtils.isNotBlank(taskId)) {
for (TsFiles tsFiles : parentFiles) {
tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// tsFiles.setWorkPath(processingPath(tsFiles.getWorkPath(), tsFiles.getNodeId())); // 优化点11路径处理内聚
// 如果备份路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getWorkPath())) {
allFiles.add(tsFiles);
}
}
}else {
} else {
for (TsFiles tsFiles : parentFiles) {
tsFiles.setBackupPath(processingPath(tsFiles.getBackupPath(), tsFiles.getNodeId()));
// tsFiles.setBackupPath(processingPath(tsFiles.getBackupPath(), tsFiles.getNodeId()));
// 如果工作路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getWorkPath())) {
allFiles.add(tsFiles);
@ -2684,8 +2694,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
}
// ==================== 3. 内存分页处理 ====================
int total = allFiles.size();
int pageSize = (int) page.getSize();
@ -2718,7 +2726,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
if (children != null && !children.isEmpty()) {
// 对每个子节点递归查询其子节点
for (TsFiles child : children) {
child.setBackupPath(processingPath(child.getBackupPath(), child.getNodeId()));
// child.setBackupPath(processingPath(child.getBackupPath(), child.getNodeId()));
if (StringUtils.isEmpty(child.getWorkPath())) {
allFiles.add(child);
}
@ -2760,7 +2768,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 递归查询每个记录的子节点并添加到 records
List<TsFiles> allFiles = new ArrayList<>();
for (TsFiles tsFiles : records) {
tsFiles.setBackupPath(processingPath(tsFiles.getBackupPath(), tsFiles.getNodeId()));
//tsFiles.setBackupPath(processingPath(tsFiles.getBackupPath(), tsFiles.getNodeId()));
// 如果工作路径为空 增加 将当前节点加入结果列表
if (StringUtils.isEmpty(tsFiles.getWorkPath())) {
allFiles.add(tsFiles);
@ -2785,8 +2793,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Override
public Page<TsFiles> compareMd5(List<String> dataset, String nodeId, String taskId, Page<TsFiles> page) {
// 获取本地文件路径根目录和存储空间名称
StorageSourceConfig filePathConfig = getStorageConfig("filePath");
StorageSourceConfig bucketConfig = getStorageConfig("bucketName");
StorageSourceConfig filePathConfig = getStorageConfig("filePath", "local");
StorageSourceConfig bucketConfig = getStorageConfig("bucketName", "minio");
// ================ 1. 执行原始分页查询 ================
QueryWrapper<TsFiles> queryWrapper = buildQueryWrapper(dataset, nodeId, taskId);
@ -2827,8 +2836,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
String minioMD5 = getMinioMD5Data(bucketConfig.getValue(), tsFile.getBackupPath(), tsFile.getFileName());
// 路径处理
tsFile.setWorkPath(processingPath(tsFile.getWorkPath(), tsFile.getNodeId()));
tsFile.setBackupPath(processingPath(tsFile.getBackupPath(), tsFile.getNodeId()));
//tsFile.setWorkPath(processingPath(tsFile.getWorkPath(), tsFile.getNodeId()));
// tsFile.setBackupPath(processingPath(tsFile.getBackupPath(), tsFile.getNodeId()));
// 设置MD5字段即使不满足条件也保留字段
tsFile.setLocatMd5(localMD5);
@ -2861,8 +2870,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Override
public List<TsFiles> compareMd5List(List<String> dataset, String nodeId, String taskId) {
// 获取本地文件路径根目录和存储空间名称
StorageSourceConfig filePathConfig = getStorageConfig("filePath");
StorageSourceConfig bucketConfig = getStorageConfig("bucketName");
StorageSourceConfig filePathConfig = getStorageConfig("filePath", "local");
StorageSourceConfig bucketConfig = getStorageConfig("bucketName", "minio");
// ================ 1. 执行原始分页查询 ================
QueryWrapper<TsFiles> queryWrapper = buildQueryWrapper(dataset, nodeId, taskId);
@ -2898,8 +2907,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
String minioMD5 = getMinioMD5Data(bucketConfig.getValue(), tsFile.getBackupPath(), tsFile.getFileName());
// 路径处理
tsFile.setWorkPath(processingPath(tsFile.getWorkPath(), tsFile.getNodeId()));
tsFile.setBackupPath(processingPath(tsFile.getBackupPath(), tsFile.getNodeId()));
// tsFile.setWorkPath(processingPath(tsFile.getWorkPath(), tsFile.getNodeId()));
// tsFile.setBackupPath(processingPath(tsFile.getBackupPath(), tsFile.getNodeId()));
// 设置MD5字段即使不满足条件也保留字段
tsFile.setLocatMd5(localMD5);
@ -3006,9 +3015,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
}
// 辅助方法获取存储配置
private StorageSourceConfig getStorageConfig(String name) {
private StorageSourceConfig getStorageConfig(String name, String type) {
return storageSourceConfigMapper.selectOne(
new QueryWrapper<StorageSourceConfig>().eq("name", name)
new QueryWrapper<StorageSourceConfig>().eq("name", name).eq("type", type)
);
}
@ -3051,12 +3060,11 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
QueryWrapper<StorageSourceConfig> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("name", "bucketName");
StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1);
StorageSourceConfig storageSourceConfig = getStorageConfig("filePath", "local");
StorageSourceConfig storageSourceConfig1 = getStorageConfig("bucketName", "minio");
List<ParameterList> parameterLists = parameter.getParameterLists();
@ -3411,14 +3419,11 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "filePath");
StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
StorageSourceConfig storageSourceConfig = getStorageConfig("filePath", "local");
StorageSourceConfig storageSourceConfig1 = getStorageConfig("bucketName", "minio");
QueryWrapper<StorageSourceConfig> queryWrapper1 = new QueryWrapper<>();
queryWrapper1.eq("name", "bucketName");
StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1);
List<ParameterList> parameterLists = parameter.getParameterLists();
for (ParameterList parameterListData : parameterLists) {
//创建文件夹
@ -3631,8 +3636,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Transactional(rollbackFor = Exception.class)
public String moveFileFolder(MoveCopyFileFolderRequest reques) throws IOException {
// 1. 获取存储根路径
StorageSourceConfig config = storageSourceConfigMapper.selectOne(
new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
Path rootPath = Paths.get(config.getValue());
String newPath = reques.getNewPath();
@ -3912,8 +3918,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Transactional(rollbackFor = Exception.class)
public String copyFileFolder(MoveCopyFileFolderRequest reques) throws IOException {
// 1. 获取存储根路径
StorageSourceConfig config = storageSourceConfigMapper.selectOne(
new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
Path rootPath = Paths.get(config.getValue());
String newPath = reques.getNewPath();
@ -4329,8 +4336,15 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
FileItemResult fileItemResult = new FileItemResult();
TsFiles tsFiles = tsFilesMapper.selectById(id);
String fileNameData = tsFiles.getFileName();
String workPath = tsFiles.getWorkPath();
String path = workPath + fileNameData;
String path = "";
if ("local".equals(type)) {
String workPath = tsFiles.getWorkPath();
path = workPath + fileNameData;
} else {
String backupPath = tsFiles.getBackupPath();
path = backupPath + fileNameData;
}
//准备获取文件的信息
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(type);
fileItemResult = fileService.getFileItem(path);
@ -4402,13 +4416,22 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
}
}
// 设置路径字段
// if (isLocal) {
// String ProcessingPath = processingPath(node.getWorkPath(), node.getNodeId());
// dto.setPath(ProcessingPath);
// } else {
// String ProcessingPath = processingPath(node.getBackupPath(), node.getNodeId());
// dto.setPath(ProcessingPath);
// }
// 设置路径字段
if (isLocal) {
String ProcessingPath = processingPath(node.getWorkPath(), node.getNodeId());
String ProcessingPath = node.getWorkPath();
dto.setPath(ProcessingPath);
} else {
String ProcessingPath = processingPath(node.getBackupPath(), node.getNodeId());
String ProcessingPath = node.getBackupPath();
dto.setPath(ProcessingPath);
}
dto.setUrl(null);
dto.setType(null);
@ -4455,8 +4478,9 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
}
currentTaskFuture = executorService.submit(() -> {
TsFiles tsFiles = tsFilesMapper.selectById(id);
StorageSourceConfig config = storageSourceConfigMapper.selectOne(
new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
Path filePath = Paths.get(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
try (BufferedReader reader = new BufferedReader(new FileReader(filePath.toFile()))) {
@ -4573,7 +4597,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Override
public String readFileContent(String id) throws IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
TsFiles tsFiles = tsFilesMapper.selectById(id);
// 1. 路径标准化与安全校验
Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
@ -4596,7 +4621,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
* 验证路径是否合法并转换为标准化路径
*/
private Path validateAndNormalizePath(String filePath) throws SecurityException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
Path targetPath = Paths.get(filePath).normalize();
Path baseDirPath = Paths.get(config.getValue());
@ -4623,7 +4648,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
*/
@Override
public void saveFileContent(String id, String content) throws SecurityException, IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
TsFiles tsFiles = tsFilesMapper.selectById(id);
// 1. 路径标准化与安全校验
Path targetPath = validateAndNormalizePath(config.getValue() + tsFiles.getWorkPath() + tsFiles.getFileName());
@ -4646,7 +4672,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
***********************************/
@Override
public void batchUpdateFile(String id, List<ModifyCommand> modifications) throws IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
StorageSourceConfig config = getStorageConfig("filePath", "local");
TsFiles tsFile = tsFilesMapper.selectById(id);
if (tsFile == null) {
throw new IllegalArgumentException("文件ID不存在: " + id);

View File

@ -2,30 +2,38 @@ package com.yfd.platform.modules.experimentalData.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.exception.file.InvalidStorageSourceException;
import com.yfd.platform.modules.config.model.request.FileListRequest;
import com.yfd.platform.modules.experimentalData.domain.TsFiles;
import com.yfd.platform.modules.experimentalData.domain.TsNodes;
import com.yfd.platform.modules.experimentalData.domain.TsTask;
import com.yfd.platform.modules.experimentalData.mapper.TsFilesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsNodesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsTaskMapper;
import com.yfd.platform.modules.experimentalData.service.ITsFilesService;
import com.yfd.platform.modules.experimentalData.service.ITsNodesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.modules.specialDocument.domain.Files;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import com.yfd.platform.modules.specialDocument.mapper.NodesMapper;
import com.yfd.platform.modules.specialDocument.domain.Project;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
import com.yfd.platform.modules.storage.mapper.StorageSourceMapper;
import com.yfd.platform.modules.storage.model.entity.StorageSource;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.request.BatchDeleteRequest;
import com.yfd.platform.modules.storage.model.request.NewFolderRequest;
import com.yfd.platform.modules.storage.model.request.RenameFolderRequest;
import com.yfd.platform.modules.storage.model.result.FileItemResult;
import com.yfd.platform.modules.storage.service.StorageSourceService;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService;
import com.yfd.platform.system.domain.LoginUser;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@ -68,8 +76,22 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
@Resource
private StorageSourceContext storageSourceContext;
@Autowired
private TaskStatusHolder taskStatusHolder;
//试验任务Mapper
@Resource
private TsTaskMapper tsTaskMapper;
@Resource
private StorageSourceService storageSourceService;
//顶级父节点 Top level parent node
public static final String TOP_LEVEL_PARENT_NODE = "00";
@Override
public List<Map<String, Object>> getTsNodesTree(String nodeName, String taskId) {
TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
// 查询所有节点数据
List<Map<String, Object>> allNodes = getAllNodes(taskId);
@ -81,12 +103,16 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
return new ArrayList<>();
}
// 根节点的基本路径/项目名称/
String basePath = "/" + tsTask.getTaskName() + "/";
// 存储最终结果
List<Map<String, Object>> result = new ArrayList<>();
// 如果 nodeName 为空返回所有根节点的完整树形结构
if (StringUtils.isEmpty(nodeName)) {
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath);
result.addAll(buildFullTree(rootNode, allNodes));
}
return result;
@ -94,6 +120,7 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
// 否则返回从根节点到目标节点的树形结构
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath);
List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
if (!tree.isEmpty()) {
result.addAll(tree);
@ -162,6 +189,13 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
// 查找当前节点的所有子节点
List<Map<String, Object>> children = findChildren(allNodes, currentNode.get("nodeId").toString());
// 为每个子节点设置路径父路径 + 父节点名称 + "/"
for (Map<String, Object> child : children) {
String parentPath = (String) currentNode.get("path");
String parentName = (String) currentNode.get("nodeName");
child.put("path", parentPath + parentName + "/");
}
// 递归构建子树
List<Map<String, Object>> tree = new ArrayList<>();
for (Map<String, Object> child : children) {
@ -213,6 +247,12 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
// 查找当前节点的所有子节点
List<Map<String, Object>> children = findChildren(allNodes, currentNode.get("nodeId").toString());
for (Map<String, Object> child : children) {
String parentPath = (String) currentNode.get("path");
String parentName = (String) currentNode.get("nodeName");
child.put("path", parentPath + parentName + "/");
}
// 递归查找目标节点
for (Map<String, Object> child : children) {
List<Map<String, Object>> childTree = buildTreeToTargetNode(child, allNodes, nodeName);
@ -242,7 +282,15 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
@Transactional(rollbackFor = Exception.class) // 添加事务注解遇到异常时回滚
public ResponseResult addTsNodes(TsNodes tsnodes) {
//查询文件表 如果有一样的名称不能新增
List<TsFiles> tsFiles = tsFilesMapper.selectList(new QueryWrapper<TsFiles>()
.eq("task_id", tsnodes.getTaskId())
.eq("parent_id", "00")
.eq("file_name", tsnodes.getNodeName())
.eq("is_file", "FOLDER"));
if (tsFiles.size() > 0) {
return ResponseResult.error("数据管理中存在该文件夹!");
}
// 校验文件名是否包含非法字符
String nodeName = tsnodes.getNodeName();
if (containsInvalidCharacters(nodeName)) {
@ -282,20 +330,38 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
int valueAdded = tsNodesMapper.insert(tsnodes);
if (valueAdded == 1) {
LOGGER.info("tsnodes表结构增加成功");
//判断文件夹是否创建
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("local");
boolean flag = fileService.isFolderCreated(File.separator + tsnodes.getNodeId());
//如果是false 说明没有创建 那就新建一个文件夹
if (!flag) {
//本地创建文件夹
AbstractBaseFileService<?> fileServiceData = storageSourceContext.getByStorageKey("local");
boolean flagData = fileServiceData.newFolder(File.separator, tsnodes.getNodeId());
if (!flagData) {
LOGGER.error("创建节点文件夹失败!");
return ResponseResult.error("新增节点失败!");
}
//新增节点的时候 创建文件夹
NewFolderRequest newFolderRequest = new NewFolderRequest();
newFolderRequest.setName(tsnodes.getNodeName());//新建的文件夹名称,示例值(/a/b/c)
newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问则支持请求密码,示例值(123456)
newFolderRequest.setPath(tsnodes.getPath());//请求路径,示例值(/)
newFolderRequest.setStorageKey("local");//存储源 key,示例值(local minio sdlocal)
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
if (flag) {
return ResponseResult.success();
} else {
LOGGER.error("节点新增成功但是local创建文件失败");
return ResponseResult.error();
}
return ResponseResult.success();
// //判断文件夹是否创建
// AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("local");
// boolean flag = fileService.isFolderCreated(File.separator + tsnodes.getNodeId());
// //如果是false 说明没有创建 那就新建一个文件夹
// if (!flag) {
// //本地创建文件夹
// AbstractBaseFileService<?> fileServiceData = storageSourceContext.getByStorageKey("local");
// boolean flagData = fileServiceData.newFolder(File.separator, tsnodes.getNodeId());
// if (!flagData) {
// LOGGER.error("创建节点文件夹失败!");
// return ResponseResult.error("新增节点失败!");
// }
// }
// return ResponseResult.success();
} else {
LOGGER.error("tsnodes表结构增加失败");
return ResponseResult.error();
@ -367,10 +433,11 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
/**********************************
* 用途说明: 根据ID删除试验任务节点
* 参数说明 id 试验任务节点ID
* 参数说明 path 路径
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
@Override
public boolean deleteTsNodesById(String id) {
public boolean deleteTsNodesById(String id, String path) {
//根据ID 查询当前数据
TsNodes tsNodes = tsNodesMapper.selectById(id);
//删除之前 先拼路径 然后删除本地和minio的文件夹 最后删除表结构
@ -391,15 +458,56 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
tsFilesService.deleteTsFilesByIds(dataset, "local");
}
// 递归删除子节点
deleteChildren(tsNodes.getNodeId(), tsNodes.getTaskId());
// 删除当前节点
int deleteCount = tsNodesMapper.deleteById(id);
if (deleteCount == 1) {
LOGGER.info("tsnodes表结删除改成功");
return true;
// // 递归删除子节点
// deleteChildren(tsNodes.getNodeId(), tsNodes.getTaskId());
//删除当前节点的文件夹 todo 这个地方改动
// 删除 sdlocal 中的文件夹
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(tsNodes.getNodeName());
deleteItemData.setPassword("");
deleteItemData.setPath(path);
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey("local");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
} else {
deleteFailCount++;
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}
}
if (deleteSuccessCount >= 1) {
// 删除当前节点
int deleteCount = tsNodesMapper.deleteById(id);
if (deleteCount == 1) {
LOGGER.info("tsnodes表结删除改成功");
return true;
} else {
LOGGER.error("tsnodes表结构删除失败");
return false;
}
} else {
LOGGER.error("tsnodes表结构删除失败");
return false;
}
}
@ -446,21 +554,58 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
@Override
public boolean deleteTsNodesByTaskId(String taskId) {
public boolean deleteTsNodesByTaskId(String taskId, String path) {
Boolean value = false;
//根据任务ID 查询所有的集合 不管层级结构全部都删除
QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("task_id", taskId);
List<TsNodes> tsNodesList = tsNodesMapper.selectList(queryWrapper);
if (tsNodesList == null && tsNodesList.isEmpty()) {
if (tsNodesList.size() == 0) {
return true;
}
for (TsNodes tsNodes : tsNodesList) {
//删除文件表
Boolean deleteTsFiles = tsFilesService.deleteTsFilesByNodeId(tsNodes.getNodeId(), tsNodes.getTaskId());
//如果删除成功 接着删除节点表数据
if (deleteTsFiles) {
//删除当前节点的文件夹 todo 这个地方改动
// 删除 sdlocal 中的文件夹
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(tsNodes.getNodeName());
deleteItemData.setPassword("");
deleteItemData.setPath(path);
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey("local");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
} else {
deleteFailCount++;
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}
}
if (deleteSuccessCount >= 1) {
// 删除当前节点
int deleteCount = tsNodesMapper.deleteById(tsNodes.getNodeId());
if (deleteCount == 1) {
@ -470,9 +615,269 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
LOGGER.error("tsnodes表结构删除失败");
value = false;
}
} else {
value = false;
}
}
return value;
}
/**********************************
* 用途说明: 试验数据扫描接口
* 参数说明 id 试验任务ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Override
@Async("asyncExecutor")
public void testDataScanByIdAsync(String id) throws Exception {
try {
// 执行扫描并且插入数据库
this.testDataScanById(id);
} finally {
// 生成唯一Key
String asyncKey = taskStatusHolder.testDatascanKey(id);
// 无论成功失败都标记完成
taskStatusHolder.finishTask(asyncKey);
}
}
private String testDataScanById(String id) throws Exception {
//查询试验任务信息
TsTask tsTask = tsTaskMapper.selectById(id);
//获取文件列表
String absolutePath = "/" + tsTask.getTaskName() + "/";
FileListRequest fileListRequest = buildFileRequest(absolutePath);
String storageKey = fileListRequest.getStorageKey();
Integer storageId = storageSourceService.findIdByKey(storageKey);
if (storageId == null) {
throw new InvalidStorageSourceException("通过存储源 key 未找到存储源, key: " + storageKey);
}
// 处理请求参数默认值
fileListRequest.handleDefaultValue();
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId);
//todo 首先获取两个集合 对比出数据库中没有的文件夹以及文件递归增加
List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath());
//获取数据库父节点为0的数据 任务ID 上级节点时00
List<TsNodes> tsNodes = tsNodesMapper.selectList(new LambdaQueryWrapper<TsNodes>().eq(TsNodes::getParentId, "00").eq(TsNodes::getTaskId, id));
// 步骤 1提取现有的 nodeName
Set<String> existingNodeNames = tsNodes.stream().map(TsNodes::getNodeName).collect(Collectors.toSet());
// 步骤 2筛选新增数据 找到需要新增到数据库的文件夹 这个属于第一层架
List<FileItemResult> fileItemNewList = fileItemList.stream().filter(fileItem -> !existingNodeNames.contains(fileItem.getName())).collect(Collectors.toList());
firstLayerData(fileItemNewList, id);
return "扫描完成";
}
/**
* 第一层下面应该只有文件夹
*
* @param fileItemList 项目下面第一层级的所有数据
* @param taskId 所属任务ID
* @throws Exception
*/
public void firstLayerData(List<FileItemResult> fileItemList, String taskId) throws Exception {
for (FileItemResult item : fileItemList) {
//思路就是 如果是文件夹 就查询一下 没有就新增 新的的时候递归往下走
if (item.getType() == FileTypeEnum.FOLDER) {
//先查询有没有 如果没有就新增
LambdaQueryWrapper<TsNodes> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TsNodes::getTaskId, taskId);
queryWrapper.eq(TsNodes::getParentId, TOP_LEVEL_PARENT_NODE);
queryWrapper.eq(TsNodes::getNodeName, item.getName());
TsNodes nodeData = tsNodesMapper.selectOne(queryWrapper);
//如果没有 新增 并且递归
if (nodeData == null) {
TsNodes node = savetsNodes(taskId, TOP_LEVEL_PARENT_NODE, item.getName());
otherLevelsData(taskId, node.getNodeId(), item.getName(), item.getPath(), TOP_LEVEL_PARENT_NODE);
} else {
otherLevelsData(taskId, nodeData.getNodeId(), item.getName(), item.getPath(), TOP_LEVEL_PARENT_NODE);
}
}
// else {
// //获取节点名称
// String nodeName = getLastPathSegment(item.getPath());
// //获取节点信息 主要用到ID
// QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("node_name", nodeName);
// queryWrapper.eq("parent_id", TOP_LEVEL_PARENT_NODE);
// Nodes node = nodesMapper.selectOne(queryWrapper);
//
// //新增之前先查询
// LambdaQueryWrapper<Files> queryWrapper1 = new LambdaQueryWrapper<>();
// queryWrapper1.eq(Files::getProjectId, projectId);
// queryWrapper1.eq(Files::getNodeId, node.getId());
// queryWrapper1.eq(Files::getFilePath, item.getPath());
// queryWrapper1.eq(Files::getFileName, item.getName());
// Files files = filesMapper.selectOne(queryWrapper1);
// if (files == null) {
// //保存文件信息
// saveFiles(projectId, node.getId(), item.getPath(), item.getName(), String.valueOf(item.getSize()));
// }
// }
}
}
/**
* @param taskId 所属项目ID
* @param nodeId 节点ID
* @param nodeName 节点名称
* @param path 路径
* @param parentId 父级ID
* @return
*/
private void otherLevelsData(String taskId, String nodeId, String nodeName, String path, String parentId) throws Exception {
//通过节点的路径加名称 查询下面的文件及文件夹
String absolutePath = path + nodeName + "/";
//获取文件列表
FileListRequest fileListRequest = buildFileRequest(absolutePath);
String storageKey = fileListRequest.getStorageKey();
Integer storageId = storageSourceService.findIdByKey(storageKey);
if (storageId == null) {
throw new InvalidStorageSourceException("通过存储源 key 未找到存储源, key: " + storageKey);
}
// 处理请求参数默认值
fileListRequest.handleDefaultValue();
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId);
//todo 首先获取两个集合 对比出数据库中没有的文件夹以及文件递归增加
List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath());
for (FileItemResult item : fileItemList) {
//思路就是 如果是文件夹 就查询一下 没有就新增 新的的时候递归往下走
if (item.getType() == FileTypeEnum.FOLDER) {
//先查询有没有 如果没有就新增 条件 节点ID 任务ID 上级ID 工作空间路径 文件名称
LambdaQueryWrapper<TsFiles> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TsFiles::getTaskId, taskId);
queryWrapper.eq(TsFiles::getNodeId, nodeId);
queryWrapper.eq(TsFiles::getParentId, parentId);
queryWrapper.eq(TsFiles::getFileName, item.getName());
queryWrapper.eq(TsFiles::getWorkPath, item.getPath());
TsFiles tsFiles = tsFilesMapper.selectOne(queryWrapper);
//如果没有 新增 并且递归
if (tsFiles == null) {
//保存文件
TsFiles tsFilesData = savetsFiles(taskId, nodeId, item.getName(), item.getPath(), parentId, "FOLDER", String.valueOf(item.getSize()));
otherLevelsData(taskId, nodeId, item.getName(), item.getPath(), tsFilesData.getId());
} else {
otherLevelsData(taskId, nodeId, item.getName(), item.getPath(), tsFiles.getId());
}
} else {
//todo 如果是文件 直接新增就可以了 不需要其他的操作
//先查询有没有 如果没有就新增 条件 节点ID 任务ID 上级ID 工作空间路径 文件名称
LambdaQueryWrapper<TsFiles> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TsFiles::getTaskId, taskId);
queryWrapper.eq(TsFiles::getNodeId, nodeId);
queryWrapper.eq(TsFiles::getParentId, parentId);
queryWrapper.eq(TsFiles::getFileName, item.getName());
queryWrapper.eq(TsFiles::getWorkPath, item.getPath());
TsFiles tsFiles = tsFilesMapper.selectOne(queryWrapper);
if (tsFiles == null) {
// 获取文件大小字节
long fileSizeInBytes = item.getSize();
// 转换为 MB 并保留两位小数
double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0);
String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
//保存文件信息
TsFiles tsFilesData = savetsFiles(taskId, nodeId, item.getName(), item.getPath(), parentId, "FILE", fileSizeFormatted);
LOGGER.info("保存文件信息:{}", item.getPath() + item.getName());
}
}
}
}
/**
* @param taskId 所属项目ID
* @param nodeId 节点ID
* @param fileName 名称
* @param path 路径
* @param parentId 父级ID
* @param isFile 文件还是文件夹
* @param fileSize 文件大小
* @return
*/
private TsFiles savetsFiles(String taskId, String nodeId, String fileName, String path, String parentId, String isFile, String fileSize) {
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
//保存文件信息
TsFiles tsFiles1 = new TsFiles();
//任务
tsFiles1.setTaskId(taskId);
//节点
tsFiles1.setNodeId(nodeId);
//文件 文件夹 区分
tsFiles1.setIsFile(isFile);
//上级ID
tsFiles1.setParentId(parentId);
//文件名称
tsFiles1.setFileName(fileName);
//工作空间路径
tsFiles1.setWorkPath(path);
tsFiles1.setUploadTime(currentTime);
if ("null".equals(fileSize)) {
tsFiles1.setFileSize("0.001");
} else {
//文件大小
if ("0.000".equals(fileSize)) {
tsFiles1.setFileSize("0.001");
} else {
tsFiles1.setFileSize(fileSize);
}
}
tsFilesMapper.insert(tsFiles1);
return tsFiles1;
}
/**
* @param taskId 所属任务ID
* @param parentId 父节点ID
* @param nodeName 节点名称
* @return
*/
private TsNodes savetsNodes(String taskId, String parentId, String nodeName) {
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
//获取当前登录用户
QueryWrapper<TsNodes> queryWrapperNodeOrder = new QueryWrapper<>();
int orderno = this.count(queryWrapperNodeOrder.eq("parent_id", TOP_LEVEL_PARENT_NODE)) + 1;
TsNodes node = new TsNodes();
node.setTaskId(taskId);
node.setParentId(parentId);
node.setNodeName(nodeName);
node.setCreateTime(currentTime);
node.setNodeOrder(orderno);
tsNodesMapper.insert(node);
return node;
}
private FileListRequest buildFileRequest(String path) {
FileListRequest fileListRequest = new FileListRequest();
fileListRequest.setOrderBy("time");
fileListRequest.setOrderDirection("desc");
fileListRequest.setPassword("");
fileListRequest.setPath(path);
fileListRequest.setStorageKey("local");
return fileListRequest;
}
}

View File

@ -1,8 +1,10 @@
package com.yfd.platform.modules.experimentalData.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.modules.experimentalData.domain.TsTask;
import com.yfd.platform.modules.experimentalData.mapper.TsTaskMapper;
@ -10,16 +12,24 @@ import com.yfd.platform.modules.experimentalData.service.ITsFilesService;
import com.yfd.platform.modules.experimentalData.service.ITsNodesService;
import com.yfd.platform.modules.experimentalData.service.ITsTaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.modules.specialDocument.domain.Project;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.request.BatchDeleteRequest;
import com.yfd.platform.modules.storage.model.request.NewFolderRequest;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
@ -44,6 +54,13 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
@Resource
private ITsNodesService tsNodesService;
@Resource
private StorageSourceContext storageSourceContext;
private static final String INITIAL_CODE = "00001";
private static final int MAX_CODE_VALUE = 99999;
/**********************************
* 用途说明: 分页查询试验数据管理-试验任务管理
* 参数说明
@ -52,13 +69,17 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
* endDate 结束日期
* taskPlace 任务地点
* taskPerson 任务人员
* carrierType 载体类型
* deviceCode 设备
* taskCode 任务编号
* taskType 任务类型
* carrierName 载体名称
* deviceCode 设备代号_编号
* testDescribe 试验描述
* sensorDescribe 传感器描述
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
@Override
public Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String carrierType, String deviceCode, Page<TsTask> page) {
public Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType, String carrierName, String deviceCode, String testDescribe, String sensorDescribe, Page<TsTask> page) {
LambdaQueryWrapper<TsTask> queryWrapper = new LambdaQueryWrapper<>();
//如果任务名称 taskName 不为空
@ -76,15 +97,32 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
queryWrapper.like(TsTask::getTaskPerson, taskPerson);
}
//如果操作类型 carrierType 不为空
if (StringUtils.isNotEmpty(carrierType)) {
queryWrapper.like(TsTask::getCarrierType, carrierType);
//如果任务编号 taskCode 不为空
if (StringUtils.isNotEmpty(taskCode)) {
queryWrapper.like(TsTask::getTaskCode, taskCode);
}
//如果设备 deviceCode 不为空
//如果任务类型 taskType 不为空
if (StringUtils.isNotEmpty(taskType)) {
queryWrapper.eq(TsTask::getTaskType, taskType);
}
//如果载体名称 carrierName 不为空
if (StringUtils.isNotEmpty(carrierName)) {
queryWrapper.like(TsTask::getCarrierName, carrierName);
}
//如果设备代号_编号 deviceCode 不为空
if (StringUtils.isNotEmpty(deviceCode)) {
queryWrapper.like(TsTask::getDeviceCode, deviceCode);
}
//如果试验描述 testDescribe 不为空
if (StringUtils.isNotEmpty(testDescribe)) {
queryWrapper.like(TsTask::getTestDescribe, testDescribe);
}
//如果传感器描述 sensorDescribe 不为空
if (StringUtils.isNotEmpty(sensorDescribe)) {
queryWrapper.like(TsTask::getSensorDescribe, sensorDescribe);
}
//开始时间startDate
DateTime parseStartDate = DateUtil.parse(startDate);
@ -92,9 +130,9 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
DateTime parseEndDate = DateUtil.parse(endDate);
//开始时间和结束时间不为空 查询条件>=开始时间 <结束时间
if (parseStartDate != null && parseEndDate != null) {
queryWrapper.ge(TsTask::getTaskDate, parseStartDate).lt(TsTask::getTaskDate, parseEndDate);
queryWrapper.ge(TsTask::getTaskStartdate, parseStartDate).lt(TsTask::getTaskEnddate, parseEndDate);
}
queryWrapper.orderByDesc(TsTask::getTaskDate);
queryWrapper.orderByDesc(TsTask::getTaskStartdate);
//分页查询
Page<TsTask> tsTaskPage = tsTaskMapper.selectPage(page, queryWrapper);
return tsTaskPage;
@ -108,6 +146,15 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
***********************************/
@Override
public Boolean addSdproject(TsTask tsTask) {
//todo 新增实验任务的时候创建一个本地的文件夹
//生成任务编号
String taskCode = generateNextTsTaskCode();
tsTask.setTaskCode(taskCode);
//生成任务名称 任务开始时间_结束时间_地点_载机名称_设备代号_编号
String taskName = buildTaskName(tsTask);
tsTask.setTaskName(taskName);
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
@ -115,12 +162,54 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
tsTask.setCreateTime(currentTime);
int valueAdded = tsTaskMapper.insert(tsTask);
if (valueAdded == 1) {
return true;
// 根节点路径
String path = "/";
//新增节点的时候 创建文件夹
NewFolderRequest newFolderRequest = new NewFolderRequest();
newFolderRequest.setName(tsTask.getTaskName());//新建的文件夹名称,示例值(/a/b/c)
newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问则支持请求密码,示例值(123456)
newFolderRequest.setPath(path);//请求路径,示例值(/)
newFolderRequest.setStorageKey("local");//存储源 key,示例值(local minio)
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
if (flag) {
return true;
} else {
LOGGER.error("试验任务增加成功,但是本地试验任务文件夹创建失败");
return false;
}
} else {
return false;
}
}
public static String buildTaskName(TsTask tsTask) {
List<String> parts = new ArrayList<>();
// 按顺序添加非空字段
addIfNotEmpty(parts, String.valueOf(tsTask.getTaskStartdate()));
addIfNotEmpty(parts, String.valueOf(tsTask.getTaskEnddate()));
addIfNotEmpty(parts, tsTask.getTaskPlace());
addIfNotEmpty(parts, tsTask.getCarrierName());
addIfNotEmpty(parts, tsTask.getDeviceCode());
// 使用下划线连接非空部分
return String.join("_", parts);
}
private static void addIfNotEmpty(List<String> list, String value) {
if (StringUtils.isNotBlank(value)) { // 使用Apache Commons Lang的空判断
list.add(value.trim()); // 去除首尾空格
}
}
// 备用方案不使用第三方库
private static boolean isNotEmpty(String value) {
return value != null && !value.trim().isEmpty();
}
/**********************************
* 用途说明: 修改试验数据管理-试验任务管理
* 参数说明
@ -129,6 +218,10 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
***********************************/
@Override
public boolean updatetsTask(TsTask tsTask) {
//生成任务名称 任务开始时间_结束时间_地点_载机名称_设备代号_编号
String taskName = buildTaskName(tsTask);
tsTask.setTaskName(taskName);
int valueUpdate = tsTaskMapper.updateById(tsTask);
if (valueUpdate == 1) {
return true;
@ -147,28 +240,105 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
Boolean value = false;
//循环所有的ID
for(String taskId : dataset){
for (String taskId : dataset) {
TsTask tsTask = tsTaskMapper.selectById(taskId);
String path = "/" + tsTask.getTaskName() + "/";
//调用删除节点 根据任务ID
Boolean deleteTsnodes = tsNodesService.deleteTsNodesByTaskId(taskId);
Boolean deleteTsnodes = tsNodesService.deleteTsNodesByTaskId(taskId, path);
//如果删除成功 接着删除节点表数据
if(deleteTsnodes){
if (deleteTsnodes) {
LOGGER.info("tsNodes表结删除改成功");
value = true;
}else {
} else {
LOGGER.error("tsNodes表结构删除失败");
value = false;
}
int deleteCount = tsTaskMapper.deleteById(taskId);
if (deleteCount == 1) {
LOGGER.info("tstask表结删除改成功");
value = true;
// 删除 local 中的文件夹 项目文件夹
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(tsTask.getTaskName());
deleteItemData.setPassword("");
deleteItemData.setPath("/");
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey("local");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
} else {
deleteFailCount++;
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}
}
if (deleteSuccessCount >= 1) {
// 删除当前项目
int deleteCount = tsTaskMapper.deleteById(taskId);
if (deleteCount == 1) {
LOGGER.info("tstask表结删除改成功");
value = true;
} else {
LOGGER.error("tstask表结构删除失败");
value = false;
}
} else {
LOGGER.error("tstask表结构删除失败");
value = false;
}
}
return value;
}
@Transactional(rollbackFor = Exception.class)
public synchronized String generateNextTsTaskCode() {
// 1. 使用悲观锁查询最大编号
TsTask maxTsTask = this.getOne(
new QueryWrapper<TsTask>()
.select("MAX(task_code) AS taskCode")
.last("FOR UPDATE")
);
// 2. 处理空表情况
String currentMax = (maxTsTask != null && maxTsTask.getTaskCode() != null)
? maxTsTask.getTaskCode()
: INITIAL_CODE;
// 3. 验证并生成新编号
try {
int numericCode = Integer.parseInt(currentMax);
if (numericCode < 0 || numericCode > MAX_CODE_VALUE) {
throw new IllegalStateException("当前设备编号超出有效范围: " + currentMax);
}
if (numericCode == MAX_CODE_VALUE) {
throw new IllegalStateException("设备编号已达最大值" + MAX_CODE_VALUE);
}
return String.format("%05d", numericCode + 1);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("无效的设备编号格式: " + currentMax);
}
}
}

View File

@ -4,14 +4,17 @@ package com.yfd.platform.modules.specialDocument.controller;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.yfd.platform.annotation.Log;
import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import com.yfd.platform.modules.specialDocument.service.INodesService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -32,6 +35,9 @@ public class NodesController {
@Resource
private INodesService nodesService;
@Autowired
private TaskStatusHolder taskStatusHolder;
/***********************************
* 用途说明获取专项文档节点 树形结构
* 参数说明
@ -44,8 +50,8 @@ public class NodesController {
@ApiOperation("获取专项文档节点树形结构")
@ResponseBody
@PreAuthorize("@el.check('select:nodes')")
public ResponseResult getNodesTree(String nodeName,String projectId) {
List<Map<String, Object>> list = nodesService.getNodesTree(nodeName,projectId);
public ResponseResult getNodesTree(String nodeName, String projectId) {
List<Map<String, Object>> list = nodesService.getNodesTree(nodeName, projectId);
return ResponseResult.successData(list);
}
@ -60,7 +66,7 @@ public class NodesController {
@PreAuthorize("@el.check('add:nodes')")
public ResponseResult addNodes(@RequestBody Nodes nodes) {
//参数校验 对象 节点名称 所属项目ID
if (ObjUtil.isEmpty(nodes) && StrUtil.isBlank(nodes.getNodeName()) && StrUtil.isBlank(nodes.getProjectId()) ) {
if (ObjUtil.isEmpty(nodes) && StrUtil.isBlank(nodes.getNodeName()) && StrUtil.isBlank(nodes.getProjectId())) {
return ResponseResult.error("参数为空");
}
return nodesService.addNodes(nodes);
@ -86,17 +92,18 @@ public class NodesController {
/**********************************
* 用途说明: 根据ID删除专项文档节点
* 参数说明 id 专项文档节点ID
* 参数说明 path 路径
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
@Log(module = "专项文档管理", value = "根据ID删除专项文档节点")
@PostMapping("/deleteNodesById")
@ApiOperation("根据ID删除专项文档节点")
@PreAuthorize("@el.check('del:nodes')")
public ResponseResult deleteNodesById(@RequestParam String id) {
public ResponseResult deleteNodesById(@RequestParam String id, String path) {
if (StrUtil.isBlank(id)) {
return ResponseResult.error("参数为空");
}
boolean isOk = nodesService.deleteNodesById(id);
boolean isOk = nodesService.deleteNodesById(id,path);
if (isOk) {
return ResponseResult.success();
} else {
@ -105,6 +112,131 @@ public class NodesController {
}
/**********************************
* 用途说明: 专项文档管理扫描接口
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "专项文档管理扫描接口", value = "专项文档管理扫描接口!")
@PostMapping("/specialScanById")
@ApiOperation("专项文档管理扫描接口通过所属项目ID")
public ResponseResult specialScanByIdAsync(String id) throws Exception {
if (StrUtil.isEmpty(id)) {
return ResponseResult.error("参数为空");
}
// 生成唯一Key
String asyncKey = taskStatusHolder.specialGenerateKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务正在处理中!");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务已完成!");
}
// 原子性启动新任务
if (taskStatusHolder.startTaskIfAbsent(asyncKey)) {
// 直接异步执行并推送结果
nodesService.specialScanByIdAsync(id);
return ResponseResult.success("专项文档扫描任务开始处理!");
} else {
return ResponseResult.success("专项文档扫描任务已由其他请求启动!");
}
}
/**********************************
* 用途说明: 获取异步信息
* 参数说明 id 所属项目ID
* 参数说明 type 要查询的信息类型 0扫描 1上传扫描
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "获取异步信息", value = "获取异步信息!")
@PostMapping("/obtainInformationAsync")
@ApiOperation("获取异步信息")
public ResponseResult obtainInformationAsync(String id, String type) throws Exception {
if (StrUtil.isEmpty(id)) {
return ResponseResult.error("id为空");
}
if (StrUtil.isEmpty(type)) {
return ResponseResult.error("类型为空");
}
if ("0".equals(type)) {
// 生成唯一Key
String asyncKey = taskStatusHolder.specialGenerateKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("TASK_NOT_FOUND".equals(existingStatus)) {
return ResponseResult.success("1");
}
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("0");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务已完成!");
}
} else {
// 生成唯一Key
String asyncKey = taskStatusHolder.documentUploadKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("TASK_NOT_FOUND".equals(existingStatus)) {
return ResponseResult.success("1");
}
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("0");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务已完成!");
}
}
return null;
}
/**********************************
* 用途说明: 专项文档管理文档上传接口
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
@Log(module = "专项文档管理文档上传", value = "专项文档管理文档上传接口!")
@PostMapping("/documentUploadById")
@ApiOperation("专项文档管理文档上传接口通过所属项目ID")
public ResponseResult documentUploadByIdAsync(String id, String fileName) throws Exception {
if (StrUtil.isEmpty(id)) {
return ResponseResult.error("参数为空");
}
if (StrUtil.isEmpty(fileName)) {
return ResponseResult.error("参数为空");
}
// 生成唯一Key
String asyncKey = taskStatusHolder.documentUploadKey(id);
// 检查任务是否已存在
String existingStatus = taskStatusHolder.getStatus(asyncKey);
if ("IN_PROGRESS".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务正在处理中!");
} else if ("COMPLETED".equals(existingStatus)) {
return ResponseResult.success("专项文档扫描任务已完成!");
}
// 原子性启动新任务
if (taskStatusHolder.startTaskIfAbsent(asyncKey)) {
// 直接异步执行并推送结果
nodesService.documentUploadByIdAsync(id, fileName);
return ResponseResult.success("专项文档扫描任务开始处理!");
} else {
return ResponseResult.success("专项文档扫描任务已由其他请求启动");
}
}
// /**********************************
// * 用途说明: 批量删除专项文档节点
// * 参数说明 ids 专项文档节点id数组

View File

@ -39,7 +39,7 @@ public class ProjectController {
/**********************************
* 用途说明: 分页查询专项文档管理-项目管理
* 参数说明
* projectCode 项目编号
* description 项目描述
* projectType 项目类型
*projectName 项目名称
* pageNum 当前页
@ -48,9 +48,9 @@ public class ProjectController {
@GetMapping("/page")
@ApiOperation("分页查询专项文档管理项目管理")
@PreAuthorize("@el.check('select:project')")
public ResponseResult getSdProjectPage(String projectCode, String projectType, String projectName, Page<Project> page) {
public ResponseResult getSdProjectPage(String description, String projectType, String projectName, Page<Project> page) {
//分页查询
Page<Project> sdProjectPage = projectService.getSdProjectPage(projectCode, projectType, projectName, page);
Page<Project> sdProjectPage = projectService.getSdProjectPage(description, projectType, projectName, page);
return ResponseResult.successData(sdProjectPage);
}

View File

@ -92,4 +92,11 @@ public class Nodes implements Serializable {
private String overallPath;
/**
* TODO路径用于拼接
*/
@TableField(exist = false)
private String path;
}

View File

@ -1,8 +1,6 @@
package com.yfd.platform.modules.specialDocument.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.*;
import java.sql.Timestamp;
import java.time.LocalDate;
@ -63,6 +61,7 @@ public class Project implements Serializable {
* 项目启动时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@TableField(updateStrategy = FieldStrategy.IGNORED)
private Timestamp projectTime;
/**

View File

@ -44,7 +44,22 @@ public interface INodesService extends IService<Nodes> {
/**********************************
* 用途说明: 根据ID删除专项文档节点
* 参数说明 id 专项文档节点ID
* 参数说明 path 路径
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
***********************************/
boolean deleteNodesById(String id);
boolean deleteNodesById(String id, String path);
/**********************************
* 用途说明: 专项文档管理扫描接口
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
void specialScanByIdAsync(String id) throws Exception;
/**********************************
* 用途说明: 专项文档管理文档上传接口
* 参数说明 id 所属项目ID
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
void documentUploadByIdAsync(String id,String fileName);
}

View File

@ -19,13 +19,13 @@ public interface IProjectService extends IService<Project> {
/**********************************
* 用途说明: 分页查询专项文档管理-项目管理
* 参数说明
* projectCode 项目编号
* description 项目描述
* projectType 项目类型
*projectName 项目名称
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
Page<Project> getSdProjectPage(String projectCode, String projectType, String projectName, Page<Project> page);
Page<Project> getSdProjectPage(String description, String projectType, String projectName, Page<Project> page);
/***********************************
* 用途说明新增专项文档管理-项目管理
@ -44,4 +44,6 @@ public interface IProjectService extends IService<Project> {
boolean updateSdproject(Project project);
boolean deleteProjectByIds(List<String> dataset);
String generateNextProjectCode();
}

View File

@ -111,7 +111,7 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
fileListRequest.setOrderDirection("desc");
fileListRequest.setPassword("");
fileListRequest.setPath(filePath);
fileListRequest.setStorageKey("minio");
fileListRequest.setStorageKey("sdlocal");
String storageKey = fileListRequest.getStorageKey();
Integer storageId = storageSourceService.findIdByKey(storageKey);
@ -225,7 +225,7 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
//校验是否真正上传
String pathAndName = files.getFilePath()+"/" + name;
//准备获取文件的信息
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("minio");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("sdlocal");
FileItemResult fileItemResult = fileService.getFileItem(pathAndName);
if (fileItemResult == null || fileItemResult.getName() == null) {
return ResponseResult.error(name + "文件没有上传到空间,请重新选择上传!");
@ -342,11 +342,11 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
deleteItemData.setType(FileTypeEnum.FILE);
deleteItemList.add(deleteItemData);
//首先通过ID集合查询所有的内容 然后放到List<DeleteItem> deleteItems里面 放好以后删除数据库 然后删除minio
//首先通过ID集合查询所有的内容 然后放到List<DeleteItem> deleteItems里面 放好以后删除数据库 然后删除本地sdlocal
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey("minio");
batchDeleteRequest.setStorageKey("sdlocal");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
List<BatchDeleteRequest.DeleteItem> deleteItems = batchDeleteRequest.getDeleteItems();
@ -399,7 +399,7 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
}
// 修改 MinIO 文件名的方法
// 修改 sdlocal 文件名的方法
private boolean updateMinioFileName(Files filesData, Files files) {
try {
RenameFileRequest renameFileRequest = new RenameFileRequest();
@ -407,12 +407,12 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, Files> implements
renameFileRequest.setNewName(files.getFileName());
renameFileRequest.setPassword("");
renameFileRequest.setPath(filesData.getFilePath());
renameFileRequest.setStorageKey("minio");
renameFileRequest.setStorageKey("sdlocal");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(renameFileRequest.getStorageKey());
return fileService.renameFile(renameFileRequest.getPath(), renameFileRequest.getName(), renameFileRequest.getNewName());
} catch (Exception e) {
LOGGER.error("MinIO 修改文件名时发生异常", e);
LOGGER.error("sdlocal 修改文件名时发生异常", e);
return false;
}
}

View File

@ -1,25 +1,33 @@
package com.yfd.platform.modules.specialDocument.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.modules.specialDocument.domain.Files;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import com.yfd.platform.modules.specialDocument.domain.Project;
import com.yfd.platform.modules.specialDocument.mapper.ProjectMapper;
import com.yfd.platform.modules.specialDocument.service.INodesService;
import com.yfd.platform.modules.specialDocument.service.IProjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.system.domain.SysDictionaryItems;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.request.BatchDeleteRequest;
import com.yfd.platform.modules.storage.model.request.NewFolderRequest;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService;
import com.yfd.platform.system.mapper.SysDictionaryItemsMapper;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
@ -35,6 +43,10 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
private static final String INITIAL_CODE = "00001";
private static final int MAX_CODE_VALUE = 99999;
//专项项目表Mapper
@Resource
private ProjectMapper projectMapper;
@ -48,22 +60,25 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
@Resource
private SysDictionaryItemsMapper sysDictionaryItemsMapper;
@Resource
private StorageSourceContext storageSourceContext;
/**********************************
* 用途说明: 分页查询专项文档管理-项目管理
* 参数说明
* projectCode 项目编号
* description 项目描述
* projectType 项目类型
*projectName 项目名称
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
@Override
public Page<Project> getSdProjectPage(String projectCode, String projectType, String projectName, Page<Project> page) {
public Page<Project> getSdProjectPage(String description, String projectType, String projectName, Page<Project> page) {
LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<>();
//如果项目编号 projectCode 不为空
if (StringUtils.isNotEmpty(projectCode)) {
queryWrapper.like(Project::getProjectCode, projectCode);
//如果项目描述 description 不为空
if (StringUtils.isNotEmpty(description)) {
queryWrapper.like(Project::getDescription, description);
}
//如果项目类型 projectType 不为空
if (StringUtils.isNotEmpty(projectType)) {
@ -99,6 +114,41 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
***********************************/
@Override
public Boolean addSdproject(Project project) {
//创建项目的时候要创建一个本地的文件夹 路径的话去找专项的存储路径
//生成项目编号
String projectCode = generateNextProjectCode();
project.setProjectCode(projectCode);
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
project.setCreateTime(currentTime);
int valueAdded = projectMapper.insert(project);
if (valueAdded == 1) {
// 根节点路径
String path = "/";
//新增节点的时候 创建文件夹
NewFolderRequest newFolderRequest = new NewFolderRequest();
newFolderRequest.setName(project.getProjectName());//新建的文件夹名称,示例值(/a/b/c)
newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问则支持请求密码,示例值(123456)
newFolderRequest.setPath(path);//请求路径,示例值(/)
newFolderRequest.setStorageKey("sdlocal");//存储源 key,示例值(local minio)
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
if (flag) {
return true;
} else {
LOGGER.error("节点新增成功,但是本地专项文件夹创建失败");
return false;
}
} else {
return false;
}
// //查询字典表获取项目类型对应数据字典
// QueryWrapper<SysDictionaryItems> queryWrapperSysDictionary = new QueryWrapper<>();
// queryWrapperSysDictionary.eq("parentcode", "compressType");
@ -122,17 +172,38 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
// if(){
//
// }
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
Timestamp currentTime = Timestamp.valueOf(now);
project.setCreateTime(currentTime);
int valueAdded = projectMapper.insert(project);
if (valueAdded == 1) {
return true;
} else {
return false;
}
// 编号生成工具方法
@Transactional(rollbackFor = Exception.class)
public synchronized String generateNextProjectCode() {
// 1. 使用悲观锁查询最大编号
Project maxProject = this.getOne(
new QueryWrapper<Project>()
.select("MAX(project_code) AS projectCode")
.last("FOR UPDATE")
);
// 2. 处理空表情况
String currentMax = (maxProject != null && maxProject.getProjectCode() != null)
? maxProject.getProjectCode()
: INITIAL_CODE;
// 3. 验证并生成新编号
try {
int numericCode = Integer.parseInt(currentMax);
if (numericCode < 0 || numericCode > MAX_CODE_VALUE) {
throw new IllegalStateException("当前项目编号超出有效范围: " + currentMax);
}
if (numericCode == MAX_CODE_VALUE) {
throw new IllegalStateException("项目编号已达最大值" + MAX_CODE_VALUE);
}
return String.format("%05d", numericCode + 1);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("无效的项目编号格式: " + currentMax);
}
}
/**********************************
@ -152,7 +223,9 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
// if(sysDictionaryItems != null){
// project.setProjectType(sysDictionaryItems.getDictName());
// }
if (project.getProjectTime() == null || project.getProjectTime().equals("")) {
project.setProjectTime(null);
}
int valueUpdate = projectMapper.updateById(project);
if (valueUpdate == 1) {
@ -173,6 +246,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
//循环所有的ID
for (String id : dataset) {
Project project = projectMapper.selectById(id);
String path = "/" + project.getProjectName() + "/";
QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("project_id", id);
queryWrapper.eq("parent_id", "00");
@ -181,7 +256,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
if (nodesList != null || !nodesList.isEmpty()) {
for (Nodes nodes : nodesList) {
//调用删除节点 根据任务ID
Boolean deleteTsnodes = nodesService.deleteNodesById(nodes.getId());
Boolean deleteTsnodes = nodesService.deleteNodesById(nodes.getId(), path);
//如果删除成功 接着删除节点表数据
if (deleteTsnodes) {
LOGGER.info("nodes表结删除改成功");
@ -193,15 +268,56 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
}
}
// 删除当前项目
int deleteCount = projectMapper.deleteById(id);
if (deleteCount == 1) {
LOGGER.info("project表结删除改成功");
value = true;
// 删除 sdlocal 中的文件夹 项目文件夹
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(project.getProjectName());
deleteItemData.setPassword("");
deleteItemData.setPath("/");
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey("sdlocal");
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
} else {
deleteFailCount++;
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}
}
if (deleteSuccessCount >= 1) {
// 删除当前项目
int deleteCount = projectMapper.deleteById(id);
if (deleteCount == 1) {
LOGGER.info("project表结删除改成功本地文件夹删除成功");
value = true;
} else {
LOGGER.error("project表结构删除失败,本地文件夹删除失败");
value = false;
}
} else {
LOGGER.error("project表结构删除失败");
value = false;
}
}
return value;
}

View File

@ -178,6 +178,9 @@ public class StorageSourceContext {
*/
private AbstractBaseFileService<IStorageParam> getInitStorageBeanByStorageId(Integer storageId) {
String keyById = storageSourceMapper.findKeyById(storageId);
StorageSource storageSource = storageSourceMapper.findByStorageKey(keyById);
String type = String.valueOf(storageSource.getType());
StorageTypeEnum storageTypeEnum = Optional.ofNullable(storageSourceMapper.findByStorageKey(keyById)).map(StorageSource::getType).orElse(null);
for (AbstractBaseFileService<?> value : storageTypeEnumFileServiceMap.values()) {
if (Objects.equals(value.getStorageTypeEnum(), storageTypeEnum)) {

View File

@ -1 +1 @@
package com.yfd.platform.modules.storage.controller.file; import cn.hutool.core.bean.BeanUtil; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSort; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.exception.file.InvalidStorageSourceException; import com.yfd.platform.modules.config.model.request.FileListRequest; import com.yfd.platform.modules.storage.chain.FileChain; import com.yfd.platform.modules.storage.chain.FileContext; import com.yfd.platform.modules.storage.context.StorageSourceContext; import com.yfd.platform.modules.storage.convert.StorageSourceConvert; import com.yfd.platform.modules.storage.model.entity.StorageSource; import com.yfd.platform.modules.storage.model.result.FileInfoResult; import com.yfd.platform.modules.storage.model.result.FileItemResult; import com.yfd.platform.modules.storage.model.result.StorageSourceResult; import com.yfd.platform.modules.storage.service.StorageSourceService; import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; import java.util.List; import java.util.stream.Collectors; /** * 文件列表相关接口, 如展示存储源列表, 展示文件列表, 搜索文件列表等. * * @author zhengsl */ @Api(tags = "文件列表模块") @ApiSort(2) @Slf4j @RequestMapping("/api/storage") @RestController public class FileController { @Resource private StorageSourceContext storageSourceContext; @Resource private StorageSourceService storageSourceService; @Resource private FileChain fileChain; @Resource private StorageSourceConvert storageSourceConvert; @ApiOperationSupport(order = 1) @ApiOperation(value = "获取存储源列表", notes = "获取所有已启用的存储源, 并且按照后台顺序排序") @GetMapping("/list") public ResponseResult storageList() { List<StorageSource> storageList = storageSourceService.findAllEnableOrderByOrderNum(); List<StorageSourceResult> storageSourceResultList = storageSourceConvert.entityToResultList (storageList); return ResponseResult.successData(storageSourceResultList); } @ApiOperationSupport(order = 2) @ApiOperation(value = "获取文件列表", notes = "获取某个存储源下, 指定路径的文件&文件夹列表") @PostMapping("/files") public ResponseResult list(@Valid @RequestBody FileListRequest fileListRequest) throws Exception { String storageKey = fileListRequest.getStorageKey(); Integer storageId = storageSourceService.findIdByKey(storageKey); if (storageId == null) { throw new InvalidStorageSourceException("通过存储源 key 未找到存储源, key: " + storageKey); } // 处理请求参数默认值 fileListRequest.handleDefaultValue(); // 获取文件列表 AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId); List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath()); // 执行责任链 FileContext fileContext = FileContext.builder() .storageId(storageId) .fileListRequest(fileListRequest) .fileItemList(fileItemList).build(); fileChain.execute(fileContext); return ResponseResult.successData(new FileInfoResult(fileContext.getFileItemList(), fileContext.getPasswordPattern())); } }
package com.yfd.platform.modules.storage.controller.file; import cn.hutool.core.bean.BeanUtil; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSort; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.exception.file.InvalidStorageSourceException; import com.yfd.platform.modules.config.model.request.FileListRequest; import com.yfd.platform.modules.storage.chain.FileChain; import com.yfd.platform.modules.storage.chain.FileContext; import com.yfd.platform.modules.storage.context.StorageSourceContext; import com.yfd.platform.modules.storage.convert.StorageSourceConvert; import com.yfd.platform.modules.storage.model.entity.StorageSource; import com.yfd.platform.modules.storage.model.result.FileInfoResult; import com.yfd.platform.modules.storage.model.result.FileItemResult; import com.yfd.platform.modules.storage.model.result.StorageSourceResult; import com.yfd.platform.modules.storage.service.StorageSourceService; import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; import java.util.List; import java.util.stream.Collectors; /** * 文件列表相关接口, 如展示存储源列表, 展示文件列表, 搜索文件列表等. * * @author zhengsl */ @Api(tags = "文件列表模块") @ApiSort(2) @Slf4j @RequestMapping("/api/storage") @RestController public class FileController { @Resource private StorageSourceContext storageSourceContext; @Resource private StorageSourceService storageSourceService; @Resource private FileChain fileChain; @Resource private StorageSourceConvert storageSourceConvert; @ApiOperationSupport(order = 1) @ApiOperation(value = "获取存储源列表", notes = "获取所有已启用的存储源, 并且按照后台顺序排序") @GetMapping("/list") public ResponseResult storageList() { List<StorageSource> storageList = storageSourceService.findAllEnableOrderByOrderNum(); List<StorageSourceResult> storageSourceResultList = storageSourceConvert.entityToResultList (storageList); return ResponseResult.successData(storageSourceResultList); } @ApiOperationSupport(order = 2) @ApiOperation(value = "获取文件列表", notes = "获取某个存储源下, 指定路径的文件&文件夹列表") @PostMapping("/files") public ResponseResult list(@Valid @RequestBody FileListRequest fileListRequest) throws Exception { String storageKey = fileListRequest.getStorageKey(); Integer storageId = storageSourceService.findIdByKey(storageKey); if (storageId == null) { throw new InvalidStorageSourceException("通过存储源 key 未找到存储源, key: " + storageKey); } // 处理请求参数默认值 fileListRequest.handleDefaultValue(); // 获取文件列表 AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId); List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath()); List<FileItemResult> fileItemListss = fileService.fileListData(fileListRequest.getPath(), "4a547ee5b2b49ce2d9c2b95ee92766c2"); // 执行责任链 FileContext fileContext = FileContext.builder() .storageId(storageId) .fileListRequest(fileListRequest) .fileItemList(fileItemList).build(); fileChain.execute(fileContext); return ResponseResult.successData(new FileInfoResult(fileContext.getFileItemList(), fileContext.getPasswordPattern())); } }

View File

@ -18,9 +18,10 @@ import java.util.Map;
public enum StorageTypeEnum implements IEnum {
/**
* 当前系统支持的所有存储源类型
* 当前系统支持的所有存储源类型 TODO: 增加了专项文档本地文件服务测试
*/
LOCAL("local", "本地存储"),
SDLOCAL("sdlocal", "专项文档本地文件服务测试"),
ALIYUN("aliyun", "阿里云 OSS"),
WEBDAV("webdav", "WebDAV"),
TENCENT("tencent", "腾讯云 COS"),

View File

@ -232,6 +232,7 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
//根据修改
boolean ok = this.updateById(sysUser);
if (ok) {
// roleids 按逗号分割为数组 roles遍历每个角色ID若用户原有角色中不包含该角色调用 addUserRoles 方法向关联表 sys_role_users插入新记录
if (StrUtil.isNotEmpty(roleids)) {
String[] roles = roleids.split(",");
List<String> list = sysUserMapper.getRoleid(sysUser.getId());
@ -244,7 +245,7 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
sysUser.getId());
}
}
//删除不包含的角色
//删除不包含的角色 调用 delInRoleUsersByUserid 方法删除用户原有但不在 roles 中的角色关联
sysUserMapper.delInRoleUsersByUserid(sysUser.getId(), roles);
} else {