diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java index 3fbb501..2a6037c 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java @@ -6,10 +6,7 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.annotation.Log; import com.yfd.platform.config.ResponseResult; -import com.yfd.platform.modules.experimentalData.domain.DualTreeResponse; -import com.yfd.platform.modules.experimentalData.domain.FileCompareResult; -import com.yfd.platform.modules.experimentalData.domain.MoveCopyFileFolderRequest; -import com.yfd.platform.modules.experimentalData.domain.TsFiles; +import com.yfd.platform.modules.experimentalData.domain.*; import com.yfd.platform.modules.experimentalData.service.ITsFilesService; import com.yfd.platform.modules.specialDocument.domain.Files; import com.yfd.platform.modules.specialDocument.service.IFilesService; @@ -20,6 +17,7 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -53,6 +51,7 @@ public class TsFilesController { * pageNum 当前页 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 ***********************************/ + @Log(module = "实验数据管理", value = "分页查询实验数据管理文档内容!") @GetMapping("/page") @ApiOperation("分页查询实验数据管理文档内容") @PreAuthorize("@el.check('select:tsfiles')") @@ -69,6 +68,7 @@ public class TsFilesController { * pageNum 当前页 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 ***********************************/ + @Log(module = "实验数据管理", value = "查询实验数据管理文件夹!") @GetMapping("/listTsFiles") @ApiOperation("查询实验数据管理文件夹") @PreAuthorize("@el.check('select:tsfiles')") @@ -84,7 +84,7 @@ public class TsFilesController { * TsFiles 文档内容 * 返回值说明: com.yfd.platform.config.ResponseResult 返回新增成功或者失败 ***********************************/ - @Log(module = "实验数据管理", value = "新增试验数据管理文档内容!", type = "1") + @Log(module = "实验数据管理", value = "新增试验数据管理文档内容!") @PostMapping("/addTsFiles") @ApiOperation("新增试验数据管理文档内容") @ResponseBody @@ -108,7 +108,7 @@ public class TsFilesController { * TsFiles 文档内容 * 返回值说明: com.yfd.platform.config.ResponseResult 返回新增成功或者失败 ***********************************/ - @Log(module = "实验数据管理", value = "新增试验数据管理文件夹", type = "1") + @Log(module = "实验数据管理", value = "新增试验数据管理文件夹") @PostMapping("/addTsFile") @ApiOperation("新增试验数据管理文件夹") @ResponseBody @@ -129,7 +129,7 @@ public class TsFilesController { * TsFiles 文档内容 * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "修改试验数据管理文档内容", type = "1") + @Log(module = "试验数据管理", value = "修改试验数据管理文档内容") @PostMapping("/updateTsFiles") @ApiOperation("修改试验数据管理文档内容") @PreAuthorize("@el.check('update:tsFiles')") @@ -145,38 +145,40 @@ public class TsFilesController { /********************************** * 用途说明: 根据ID删除试验数据管理-文档内容 * 参数说明 id 文档内容ID + * 参数说明 type local还是minio * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "根据ID删除试验数据管理文档内容", type = "1") + @Log(module = "试验数据管理", value = "根据ID删除试验数据管理文档内容") @PostMapping("/deleteTsFilesById") @ApiOperation("根据ID删除试验数据管理文档内容") @PreAuthorize("@el.check('del:tsFiles')") - public ResponseResult deleteTsFilesById(@RequestParam String id) { + public ResponseResult deleteTsFilesById(@RequestParam String id, @RequestParam String type) { if (StrUtil.isBlank(id)) { return ResponseResult.error("参数为空"); } List dataset = Arrays.asList(id); - return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset)); + return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type)); } /********************************** * 用途说明: 批量删除试验数据管理-文档内容 * 参数说明 ids 文档内容id数组 + * 参数说明 type local还是minio * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 ***********************************/ - @Log(module = "专项文档管理", value = "批量删除试验数据管理文档内容", type = "1") + @Log(module = "专项文档管理", value = "批量删除试验数据管理文档内容") @PostMapping("/deleteTsFilesByIds") @ApiOperation("批量删除试验数据管理文档内容") @PreAuthorize("@el.check('del:tsFiles')") - public ResponseResult deleteTsFilesByIds(@RequestParam String ids) { + public ResponseResult deleteTsFilesByIds(@RequestParam String ids, @RequestParam String type) { if (StrUtil.isBlank(ids)) { return ResponseResult.error("参数为空"); } String[] splitIds = ids.split(","); // 数组转集合 List dataset = Arrays.asList(splitIds); - return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset)); + return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type)); } @@ -192,6 +194,7 @@ public class TsFilesController { * 参数说明 parentId 父ID * 返回值说明: 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) { @@ -215,6 +218,7 @@ public class TsFilesController { * 参数说明 parentId 父ID * 返回值说明: com.yfd.platform.config.ResponseResult ***********************************/ + @Log(module = "实验数据管理", value = "解压缩接口!") @PostMapping("/decompression") @ApiOperation("解压缩接口") public ResponseResult decompressionFolder(String id, String decompressionPath, String parentId) { @@ -231,7 +235,6 @@ public class TsFilesController { } - /** * 移动文件或文件夹 * 参数说明 newPath 新路径 @@ -241,6 +244,7 @@ public class TsFilesController { * 参数说明 Rename 重命名的文件名称 * 参数说明 type 覆盖还是重命名 0 1 */ + @Log(module = "实验数据管理", value = "移动文件文件夹!") @PostMapping("/moveFileFolder") @ApiOperation("移动") public ResponseResult moveFileFolder(@RequestBody MoveCopyFileFolderRequest request) throws IOException { @@ -273,6 +277,7 @@ public class TsFilesController { * 参数说明 Rename 重命名的文件名称 * 参数说明 type 覆盖还是重命名 0 1 */ + @Log(module = "实验数据管理", value = "复制文件文件夹!") @PostMapping("/copyFileFolder") @ApiOperation("复制") public ResponseResult copyFileFolder(@RequestBody MoveCopyFileFolderRequest request) throws IOException { @@ -295,24 +300,26 @@ public class TsFilesController { } - - /** * 对比两个目录的文件差异 * - * @param localPath 本地目录的相对路径(相对于E:\yun\qqq) - * @param minioPath MinIO目录的相对路径(相对于test-bucket/qqq) + * @param ids 勾选的所有数据ID集合 * @return 文件差异列表 */ + @Log(module = "实验数据管理", value = "对比本地和minio的文件差异!") @PostMapping("/compare") @ApiOperation("对比两个目录的文件差异") - public ResponseResult compareDirectories(@RequestParam String localPath, @RequestParam String minioPath) { + public ResponseResult compareDirectories( String ids, String nodeId, String taskId) { + try { - if (StrUtil.isBlank(localPath) && StrUtil.isBlank(minioPath)) { - return ResponseResult.error("参数为空"); + List dataset = new ArrayList<>(); + if (StrUtil.isNotEmpty(ids)) { + String[] splitIds = ids.split(","); + // 数组转集合 + dataset = Arrays.asList(splitIds); } - TsFiles tsFiles = tsFilesService.compareDirectories(localPath, minioPath); + TsFiles tsFiles = tsFilesService.compareDirectories(dataset, nodeId, taskId); return ResponseResult.successData(tsFiles); } catch (Exception e) { return ResponseResult.error("对比失败"); @@ -322,20 +329,18 @@ public class TsFilesController { /********************************** * 用途说明: 将文件上传到备份空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 + * 参数说明 parameter 数据集合 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ + @Log(module = "实验数据管理", value = "将文件上传到备份空间!") @PostMapping("/uploadToBackup") @ApiOperation("将文件上传到备份空间") - public ResponseResult uploadToBackup(@RequestParam String paths, @RequestParam String names, @RequestParam String sizes) { + public ResponseResult uploadToBackup(@RequestBody Parameter parameter) { - - if (StrUtil.isBlank(paths) && StrUtil.isBlank(names)) { + if (parameter == null) { return ResponseResult.error("参数为空"); } - Boolean isOk = tsFilesService.uploadToBackup(paths, names, sizes); + Boolean isOk = tsFilesService.uploadToBackup(parameter); if (isOk) { return ResponseResult.success(); } else { @@ -346,20 +351,19 @@ public class TsFilesController { /********************************** * 用途说明: 从备份空间下载到工作空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 + * 参数说明 parameter 数据集合 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ + @Log(module = "实验数据管理", value = "从备份空间下载到工作空间!") @PostMapping("/downloadToLocal") @ApiOperation("从备份空间下载到工作空间") - public ResponseResult downloadToLocal(@RequestParam String paths, @RequestParam String names, @RequestParam String sizes) { + public ResponseResult downloadToLocal(@RequestBody Parameter parameter) { - if (StrUtil.isBlank(paths) && StrUtil.isBlank(names)) { + if (parameter == null) { return ResponseResult.error("参数为空"); } - Boolean isOk = tsFilesService.downloadToLocal(paths, names, sizes); + Boolean isOk = tsFilesService.downloadToLocal(parameter); if (isOk) { return ResponseResult.success(); } else { @@ -369,17 +373,16 @@ public class TsFilesController { } - - /********************************** * 用途说明: 查询本地和备份空间结构树 * 参数说明 taskId 节点ID * 参数说明 nodeId 任务ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回双树数据 ***********************************/ + @Log(module = "实验数据管理", value = "查询本地和备份空间结构树!") @PostMapping("/listLocalAndBackup") @ApiOperation("查询本地和备份空间结构树") - public ResponseResult listLocalAndBackup( String taskId, String nodeId) { + public ResponseResult listLocalAndBackup(String taskId, String nodeId) { if (StrUtil.isBlank(taskId) && StrUtil.isBlank(nodeId)) { @@ -391,8 +394,4 @@ public class TsFilesController { } - - - - } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsNodesController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsNodesController.java index 7109a23..69d2a04 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsNodesController.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsNodesController.java @@ -42,6 +42,7 @@ public class TsNodesController { * taskId 所属任务ID * 返回值说明: 专项文档节点树形结构 ***********************************/ + @Log(module = "试验数据管理", value = "获取试验任务节点树形结构!") @PostMapping("/getTsNodesTree") @ApiOperation("获取试验任务节点树形结构") @ResponseBody @@ -56,7 +57,7 @@ public class TsNodesController { * 参数说明 tsnodes 试验任务节点信息 * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "增加试验任务节点",type = "1") + @Log(module = "试验数据管理", value = "增加试验任务节点!") @PostMapping("/addTsNodes") @ApiOperation("增加试验任务节点") @PreAuthorize("@el.check('add:tsnodes')") @@ -74,7 +75,7 @@ public class TsNodesController { * 参数说明 tsnodes 试验任务节点信息 * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "修改试验任务节点",type = "1") + @Log(module = "试验数据管理", value = "修改试验任务节点!") @PostMapping("/updateTsNodes") @ApiOperation("修改试验任务节点") @PreAuthorize("@el.check('update:tsnodes')") @@ -91,7 +92,7 @@ public class TsNodesController { * 参数说明 id 试验任务节点ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "根据ID删除试验任务节点",type = "1") + @Log(module = "试验数据管理", value = "根据ID删除试验任务节点!") @PostMapping("/deleteTsNodesById") @ApiOperation("根据ID删除试验任务节点") @PreAuthorize("@el.check('del:tsnodes')") diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java index 219343c..938518f 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java @@ -46,6 +46,7 @@ public class TsTaskController { * pageNum 当前页 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 ***********************************/ + @Log(module = "试验数据管理", value = "分页查询试验数据管理试验任务管理!") @GetMapping("/page") @ApiOperation("分页查询试验数据管理试验任务管理") @PreAuthorize("@el.check('select:tsTask')") @@ -61,7 +62,7 @@ public class TsTaskController { * TsTask 试验任务管理 * 返回值说明: com.yfd.platform.config.ResponseResult 返回新增成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "新增试验数据管理试验任务管理!",type = "1") + @Log(module = "试验数据管理", value = "新增试验数据管理试验任务管理!") @PostMapping("/addtsTask") @ApiOperation("新增试验数据管理试验任务管理") @ResponseBody @@ -85,7 +86,7 @@ public class TsTaskController { * TsTask 试验任务管理 * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "修改试验数据管理试验任务管理",type = "1") + @Log(module = "试验数据管理", value = "修改试验数据管理试验任务管理!") @PostMapping("/updatetsTask") @ApiOperation("修改试验数据管理试验任务管理") @PreAuthorize("@el.check('update:tsTask')") @@ -107,7 +108,7 @@ public class TsTaskController { * 参数说明 id 试验数据管理ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ - @Log(module = "试验数据管理", value = "根据ID删除试验数据管理试验任务管理",type = "1") + @Log(module = "试验数据管理", value = "根据ID删除试验数据管理试验任务管理") @PostMapping("/deleteTsTaskById") @ApiOperation("根据ID删除试验数据管理试验任务管理") @PreAuthorize("@el.check('del:tsTask')") @@ -128,7 +129,7 @@ public class TsTaskController { * 参数说明 ids 试验数据管理id数组 * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 ***********************************/ - @Log(module = "试验数据管理", value = "批量删除试验数据管理试验任务管理",type = "1") + @Log(module = "试验数据管理", value = "批量删除试验数据管理试验任务管理!") @PostMapping("/deleteTsTaskByIds") @ApiOperation("批量删除试验数据管理试验任务管理") @PreAuthorize("@el.check('del:tsTask')") @@ -153,7 +154,7 @@ public class TsTaskController { * 返回值说明: 试验数据管理试验任务管理 ***********************************/ @PostMapping("/list") - @ApiOperation("查询所有试验数据管理试验任务管理") + @ApiOperation("查询所有试验数据管理试验任务管理!") @ResponseBody //@PreAuthorize("@el.check('select:devicesignal')") public ResponseResult listTsTask() { diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/Parameter.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/Parameter.java new file mode 100644 index 0000000..39a42a0 --- /dev/null +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/Parameter.java @@ -0,0 +1,11 @@ +package com.yfd.platform.modules.experimentalData.domain; + +import lombok.Data; + +import java.util.List; + +@Data +public class Parameter { + public List parameterLists; +// public String parameterLists; +} diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ParameterList.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ParameterList.java new file mode 100644 index 0000000..5e44f79 --- /dev/null +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ParameterList.java @@ -0,0 +1,13 @@ +package com.yfd.platform.modules.experimentalData.domain; + +import lombok.Data; + +@Data +public class ParameterList { + + public String path; + public String size; + public String name; + public String type; + +} diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TreeDTO.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TreeDTO.java index 46429ca..c906519 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TreeDTO.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TreeDTO.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.List; @Data @@ -98,5 +99,6 @@ public class TreeDTO { private String path; // 子节点列表 - private List children; + // 初始化 children 为一个空列表 + private List children = new ArrayList<>(); } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TsFiles.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TsFiles.java index 95e8c94..d2d7dba 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TsFiles.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/TsFiles.java @@ -166,24 +166,24 @@ public class TsFiles implements Serializable { // private String creator; // // -// /** -// * 检查本地有而 MinIO 没有的文件 -// */ -// @TableField(exist = false) -// private List localOnlyFiles; -// -// -// /** -// * 检查 MinIO 有而本地没有的文件 -// */ -// @TableField(exist = false) -// private List minioOnlyFiles; -// -// -// /** -// * 检查 MD5 不一致的文件 -// */ -// @TableField(exist = false) -// private List md5MismatchedFiles; + /** + * 检查本地有而 MinIO 没有的文件 + */ + @TableField(exist = false) + private List localOnlyFiles; + + + /** + * 检查 MinIO 有而本地没有的文件 + */ + @TableField(exist = false) + private List minioOnlyFiles; + + + /** + * 检查 MD5 不一致的文件 + */ + @TableField(exist = false) + private List md5MismatchedFiles; } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java index 3d9d565..b3c7a0e 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java @@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.modules.experimentalData.domain.DualTreeResponse; import com.yfd.platform.modules.experimentalData.domain.MoveCopyFileFolderRequest; +import com.yfd.platform.modules.experimentalData.domain.Parameter; import com.yfd.platform.modules.experimentalData.domain.TsFiles; import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.web.bind.annotation.RequestParam; import java.io.FileNotFoundException; import java.io.IOException; @@ -54,10 +56,11 @@ public interface ITsFilesService extends IService { /********************************** * 用途说明: 批量删除试验数据管理-文档内容 - * 参数说明 ids 文档内容id数组 + * 参数说明 id 文档内容ID + * 参数说明 type local还是minio * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 ***********************************/ - String deleteTsFilesByIds(List dataset); + String deleteTsFilesByIds(List dataset,String type); /********************************** @@ -83,25 +86,22 @@ public interface ITsFilesService extends IService { String decompressionFolder(String id,String decompressionPath,String parentId); - TsFiles compareDirectories(String localPath, String minioPath); /********************************** * 用途说明: 将文件上传到备份空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 + * 参数说明 parameter 数据集合 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ - Boolean uploadToBackup(String paths, String names, String sizes); + Boolean uploadToBackup(Parameter parameter); /********************************** * 用途说明: 从备份空间下载到工作空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 + * 参数说明 parameter 数据集合 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ - Boolean downloadToLocal(String paths, String names, String sizes); + Boolean downloadToLocal(Parameter parameter); + + /*********************************** * 用途说明:新增试验数据管理-文件夹 @@ -150,4 +150,13 @@ public interface ITsFilesService extends IService { * 返回值说明: com.yfd.platform.config.ResponseResult 返回双树数据 ***********************************/ DualTreeResponse listLocalAndBackup(String taskId, String nodeId); + + + /** + * 对比两个目录的文件差异 + * + * @param dataset 勾选的所有数据ID集合 + * @return 文件差异列表 + */ + TsFiles compareDirectories(List dataset,String nodeId , String taskId); } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java index 8e09248..23a2503 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java @@ -40,14 +40,18 @@ 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 net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.StringValue; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.poi.ss.formula.functions.T; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; import java.io.*; @@ -165,6 +169,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl } // queryWrapper.isNotNull("work_path"); + queryWrapper.ne("work_path", ""); //排序 queryWrapper.orderByDesc("is_file");//时间 // queryWrapper.orderByDesc("upload_time");//时间 @@ -178,7 +183,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl for (TsFiles tsFiles : records) { //如果是文件夹 跳过循环 if (tsFiles.getIsFile().equals("FOLDER")) { - if(tsFiles.getUpdateTime() == null){ + if (tsFiles.getUpdateTime() == null) { tsFiles.setUpdateTime(tsFiles.getUploadTime()); } continue; @@ -198,7 +203,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl } else { tsFiles.setType(fileItemResult.getType().getValue()); } - if(tsFiles.getUpdateTime() == null){ + if (tsFiles.getUpdateTime() == null) { tsFiles.setUpdateTime(tsFiles.getUploadTime()); } } @@ -251,50 +256,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl } -// /********************************** -// * 用途说明: 分页查询试验数据管理-文件夹下内容 -// * 参数说明 -// * id id -// * pageNum 当前页 -// * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 -// ***********************************/ -// @Override -// public Page getsubFilepage(String id, Page page) { -// QueryWrapper queryWrapper = new QueryWrapper<>(); -// queryWrapper.eq("parent_id", id); -// //分页查询 -// Page subFilepage = tsFilesMapper.selectPage(page, queryWrapper); -// if (subFilepage == null) { -// subFilepage = new Page<>(); -// subFilepage.setRecords(new ArrayList<>()); // 确保 records 被初始化 -// } -// List records = subFilepage.getRecords(); -// for (TsFiles tsFiles : records) { -// //如果是文件夹 跳过循环 -// if (tsFiles.getIsFile().equals("FOLDER")) { -// continue; -// } -// String workPath = tsFiles.getWorkPath(); -// String fileNameData = tsFiles.getFileName(); -// //主要是用于文件路径加名称 -// String path = workPath + fileNameData; -// //准备获取文件的信息 -// AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("local"); -// FileItemResult fileItemResult = fileService.getFileItem(path); -// tsFiles.setUrl(fileItemResult.getUrl()); -// //如果是压缩文件 类型就给zip -// boolean isValid = hasValidExtension(fileItemResult.getName()); -// if (isValid) { -// tsFiles.setType("ZIP"); -// } else { -// tsFiles.setType(fileItemResult.getType().getValue()); -// } -// } -// subFilepage.setRecords(records); // 同步到 tsFilesPage -// return subFilepage; -// } - - /*********************************** * 用途说明:新增试验数据管理-文档内容 * 参数说明 @@ -472,7 +433,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 修改数据库 int valueUpdate = tsFilesMapper.updateById(tsFiles); if (valueUpdate == 1) { - LOGGER.error("\"local和minio修改成功,表结构修改成功\""); + LOGGER.info("local和minio修改成功,表结构修改成功"); return ResponseResult.success(); } else { LOGGER.error("local和minio修改成功,表结构修改失败"); @@ -508,7 +469,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 修改数据库 int valueUpdate = tsFilesMapper.updateById(tsFiles); if (valueUpdate == 1) { - LOGGER.error("\"local和minio修改成功,表结构修改成功\""); + LOGGER.info("local和minio修改成功,表结构修改成功"); return ResponseResult.success(); } else { LOGGER.error("local和minio修改成功,表结构修改失败"); @@ -580,12 +541,13 @@ public class TsFilesServiceImpl extends ServiceImpl impl /********************************** * 用途说明: 批量删除试验数据管理-文档内容 - * 参数说明 ids 文档内容id数组 + * 参数说明 id 文档内容ID + * 参数说明 type local还是minio * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 ***********************************/ @Override @Transactional(rollbackFor = Exception.class)// 添加事务注解,遇到异常时回滚 - public String deleteTsFilesByIds(List dataset) { + public String deleteTsFilesByIds(List dataset, String type) { List filesList = tsFilesMapper.selectBatchIds(dataset); int LocalSuccessCount = 0, LocalFailCount = 0, Localtotal = CollUtil.size(dataset); @@ -599,13 +561,17 @@ public class TsFilesServiceImpl extends ServiceImpl impl BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem(); deleteItemData.setName(files.getFileName()); deleteItemData.setPassword(""); - deleteItemData.setPath(files.getWorkPath()); + if ("local".equals(type)) { + deleteItemData.setPath(files.getWorkPath()); + } else { + deleteItemData.setPath(files.getBackupPath()); + } deleteItemData.setType(FileTypeEnum.FOLDER); deleteItemList.add(deleteItemData); //首先通过ID集合查询所有的内容 然后放到List deleteItems里面 放好以后删除数据库 然后删除minio BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest(); batchDeleteRequest.setDeleteItems(deleteItemList); - batchDeleteRequest.setStorageKey("local"); + batchDeleteRequest.setStorageKey(type); AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey()); List deleteItems = batchDeleteRequest.getDeleteItems(); @@ -642,7 +608,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl //如果是1 说明成功删除 if (deleteSuccessCount >= 1) { - int valueDelete = deleteFolder(files.getId()); + int valueDelete = deleteFolder(files.getId(), type); if (valueDelete > 0) { LocalSuccessCount = valueDelete; LOGGER.info("表结构成功删除"); @@ -702,7 +668,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl } //如果是1 说明成功删除 if (deleteSuccessCount == 1) { - int valueDelete = tsFilesMapper.deleteById(files.getId()); + int valueDelete = deleteFiles(files.getId(), type); if (valueDelete > 0) { LocalSuccessCount++; LOGGER.info("表结构成功删除"); @@ -718,12 +684,12 @@ public class TsFilesServiceImpl extends ServiceImpl impl } // 调用删除方法并返回删除数量 - public int deleteFolder(String folderId) { - return deleteFolderAndSubFiles(folderId); // 返回删除的记录数量 + public int deleteFolder(String folderId, String type) { + return deleteFolderAndSubFiles(folderId, type); // 返回删除的记录数量 } // 递归删除文件夹及其子文件,并返回删除的记录数量 - public int deleteFolderAndSubFiles(String folderId) { + public int deleteFolderAndSubFiles(String folderId, String type) { int deletedCount = 0; // 记录删除的数量 // 查询该文件夹下的所有子文件 @@ -734,21 +700,96 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 递归删除子文件 for (TsFiles subFile : subFiles) { // 递归调用删除子文件的逻辑 - deletedCount += deleteFolderAndSubFiles(subFile.getId()); // 累加删除数量 + deletedCount += deleteFolderAndSubFiles(subFile.getId(), type); // 累加删除数量 - // 删除子文件记录(从数据库中删除) - tsFilesMapper.deleteById(subFile.getId()); + //判断是删除本地还是minio + if ("local".equals(type)) { + //删除本地的时候判断minio路径是否为空 如果为空直接删除 如果不为空把workPath修改成空 + if (StringUtils.isNotEmpty(subFile.getBackupPath())) { + subFile.setWorkPath(""); + tsFilesMapper.updateById(subFile); + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(subFile.getId()); + } + + } else { + //删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空 + if (StringUtils.isNotEmpty(subFile.getWorkPath())) { + subFile.setBackupPath(""); + tsFilesMapper.updateById(subFile); + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(subFile.getId()); + } + } deletedCount++; // 每删除一个文件,数量加1 } - // 删除文件夹本身(根据传入的文件夹 ID 删除) - tsFilesMapper.deleteById(folderId); + //删除修改自身 + TsFiles tsFilesData = tsFilesMapper.selectById(folderId); + //判断是删除本地还是minio + if ("local".equals(type)) { + //删除本地的时候判断minio路径是否为空 如果为空直接删除 如果不为空把workPath修改成空 + if (StringUtils.isNotEmpty(tsFilesData.getBackupPath())) { + tsFilesData.setWorkPath(""); + tsFilesMapper.updateById(tsFilesData); + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(tsFilesData.getId()); + } + + } else { + //删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空 + if (StringUtils.isNotEmpty(tsFilesData.getWorkPath())) { + tsFilesData.setBackupPath(""); + tsFilesMapper.updateById(tsFilesData); + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(tsFilesData.getId()); + } + } deletedCount++; // 删除文件夹本身,数量加1 return deletedCount; // 返回删除的数量 } + public int deleteFiles(String id, String type) { + int deletedCount = 0; + TsFiles tsFilesData = tsFilesMapper.selectById(id); + //判断是删除本地还是minio + if ("local".equals(type)) { + //删除本地的时候判断minio路径是否为空 如果为空直接删除 如果不为空把workPath修改成空 + if (StringUtils.isNotEmpty(tsFilesData.getBackupPath())) { + tsFilesData.setWorkPath(""); + tsFilesMapper.updateById(tsFilesData); + deletedCount++; + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(tsFilesData.getId()); + deletedCount++; + + } + + } else { + //删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空 + if (StringUtils.isNotEmpty(tsFilesData.getWorkPath())) { + tsFilesData.setBackupPath(""); + tsFilesMapper.updateById(tsFilesData); + deletedCount++; + + } else { + // 删除子文件记录(从数据库中删除) + tsFilesMapper.deleteById(tsFilesData.getId()); + deletedCount++; + + } + } + return deletedCount; + } + + /********************************** * 用途说明: 压缩文件夹接口 * 参数说明 ids 文件id数组 @@ -811,12 +852,14 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 调用压缩方法 Boolean value = compressDirectoriesToZip(sourceDirs, zipOut); if (value) { + zipOut.finish(); // 确保所有数据都已写入 //表结构增加 nodeId, String TaskId TsFiles tsFiles = new TsFiles(); tsFiles.setTaskId(filesList.get(0).getTaskId()); tsFiles.setNodeId(filesList.get(0).getNodeId()); tsFiles.setWorkPath(compressedPath); tsFiles.setIsFile("FILE"); + tsFiles.setFileSize(String.valueOf(zipFilePath.toFile().length())); tsFiles.setParentId(parentId); tsFiles.setBackupPath(""); tsFiles.setKeywords(""); @@ -826,8 +869,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl //上传人是当前登录人 UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); LoginUser loginuser = (LoginUser) authentication.getPrincipal(); - - List filesToSave = new ArrayList<>(); // 设置当前时间 LocalDateTime now = LocalDateTime.now(); // 转换为 Timestamp @@ -989,17 +1030,40 @@ public class TsFilesServiceImpl extends ServiceImpl impl queryWrapper.eq("name", "filePath"); StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); - // 获取压缩文件记录 - TsFiles zipFileRecord = tsFilesMapper.selectById(id); - String localRootDir = storageSourceConfig.getValue() + decompressionPath; + // 2. 获取压缩包记录 + TsFiles zipFileRecord = tsFilesMapper.selectById(id); + String zipName = getFileNameWithoutExtension(zipFileRecord.getFileName()); // 示例:222 try { - // 解压到临时目录 - Path zipFilePath = Paths.get(localRootDir, zipFileRecord.getFileName()); - File unzippedRoot = unzipToTemp(zipFilePath, localRootDir); + // 3. 构建解压目标路径(示例:E:/yun/333/222/) + Path destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath); // 关键修改:强制创建222子目录 + Files.createDirectories(destRoot); + // 4. 执行解压(ZIP内容解压到222子目录) + File unzippedRoot = unzipToTemp(Paths.get(storageSourceConfig.getValue(), decompressionPath, zipFileRecord.getFileName()), destRoot.toString()); + // 5. 创建根目录记录(示例:222的work_path为/333/222/) + TsFiles rootFolder = createFolderRecord( + zipName, // 文件夹名称222 + parentId, // 父ID是333的ID + buildFolderPath(decompressionPath), // /333/222/ + zipFileRecord.getTaskId(), + zipFileRecord.getNodeId(), + zipFileRecord.getUploader() + ); + String rootFolderId = rootFolder.getId(); + + + // 6. 处理子内容(路径从/333/222/开始) + processFolderContents( + unzippedRoot, + rootFolderId, + buildFolderPathq(decompressionPath + "/" + zipName + "/"), // /333/222/ + zipFileRecord.getTaskId(), + zipFileRecord.getNodeId(), + zipFileRecord.getUploader() + ); // 递归保存结构到数据库 - saveFolderStructure(unzippedRoot, parentId, decompressionPath, zipFileRecord.getTaskId(), zipFileRecord.getNodeId(), zipFileRecord.getUploader()); + // saveFolderStructure(unzippedRoot, parentId, decompressionPath, zipFileRecord.getTaskId(), zipFileRecord.getNodeId(), zipFileRecord.getUploader()); return "解压存储成功"; } catch (Exception e) { @@ -1008,245 +1072,114 @@ public class TsFilesServiceImpl extends ServiceImpl impl } /** - * 保存文件夹结构 - * - * @param currentFile 当前文件/目录 - * @param parentId 父记录ID - * @param parentWorkPath 父工作路径 - * @param taskId 任务ID - * @param nodeId 节点ID - * @param uploader 上传人 + * 创建文件夹记录 */ - - /** - * 保存文件夹结构(分阶段处理:先处理目录,再处理文件) - * - * @param currentFile 当前文件/目录 - * @param parentId 父记录ID - * @param parentWorkPath 父工作路径 - * @param taskId 任务ID - * @param nodeId 节点ID - * @param uploader 上传人 - */ - private void saveFolderStructure(File currentFile, String parentId, String parentWorkPath, String taskId, String nodeId, String uploader) { - // 阶段1:处理目录结构 - processFolders(currentFile, parentId, parentWorkPath, taskId, nodeId, uploader); - - // 阶段2:处理文件 - processFiles(currentFile, parentId, parentWorkPath, taskId, nodeId, uploader); + private TsFiles createFolderRecord(String folderName, String parentId, String workPath, + String taskId, String nodeId, String uploader) { + //上传人是当前登录人 + UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + TsFiles folderRecord = new TsFiles(); + folderRecord.setNodeId(nodeId); + folderRecord.setTaskId(taskId); + folderRecord.setParentId(parentId); + folderRecord.setFileName(folderName); + folderRecord.setWorkPath(workPath); + folderRecord.setIsFile("FOLDER"); + folderRecord.setFileSize("0"); + folderRecord.setUploadTime(new Timestamp(System.currentTimeMillis())); + operateTsfeiles(folderRecord); // 插入或更新记录 + return folderRecord; } /** - * 阶段1:递归创建目录结构 + * 递归处理子内容(关键:区分根目录和子内容路径) */ - private void processFolders(File currentFile, String parentId, String parentWorkPath, - String taskId, String nodeId, String uploader) { - // 如果是文件直接返回 - if (currentFile.isFile()) return; - // 创建或更新当前目录记录 - String currentFolderId = createOrUpdateFolderRecord(currentFile, parentId, parentWorkPath, taskId, nodeId, uploader); - // 递归处理子目录(先处理所有子目录) - File[] children = currentFile.listFiles(); - if (children != null) { - for (File child : children) { - if (child.isDirectory()) { - processFolders( - child, - currentFolderId, - buildFolderPath(parentWorkPath + currentFile.getName()), - taskId, - nodeId, - uploader - ); - } + private void processFolderContents(File currentDir, String parentId, String parentWorkPath, + String taskId, String nodeId, String uploader) { + File[] children = currentDir.listFiles(); + if (children == null) return; + + for (File child : children) { + if (child.isDirectory()) { + // 处理子目录(示例:555) + String folderWorkPath = parentWorkPath + child.getName() + "/"; + + TsFiles folderRecord = new TsFiles(); + folderRecord.setNodeId(nodeId); + folderRecord.setTaskId(taskId); + folderRecord.setParentId(parentId); // 父ID是222的ID + folderRecord.setFileName(child.getName()); + folderRecord.setWorkPath(parentWorkPath); // /333/222/555/ + folderRecord.setIsFile("FOLDER"); + folderRecord.setFileSize("0"); + folderRecord.setUploadTime(new Timestamp(System.currentTimeMillis())); + operateTsfeiles(folderRecord); + + // 递归处理子目录内容 + processFolderContents( + child, + folderRecord.getId(), + folderWorkPath, + taskId, + nodeId, + uploader + ); + } else { + // 处理文件(示例:1.txt) + TsFiles fileRecord = new TsFiles(); + fileRecord.setNodeId(nodeId); + fileRecord.setTaskId(taskId); + fileRecord.setParentId(parentId); // 父ID是222的ID + fileRecord.setFileName(child.getName()); + fileRecord.setWorkPath(parentWorkPath); // /333/222/ + fileRecord.setIsFile("FILE"); + fileRecord.setUploadTime(new Timestamp(System.currentTimeMillis())); + fileRecord.setFileSize(String.valueOf(child.length())); + operateTsfeiles(fileRecord); +// +// tsFilesMapper.insert(fileRecord); } } } - /** - * 阶段2:处理文件(此时目录结构已存在) - */ - /** - * 优化后的文件处理逻辑 - */ - private void processFiles(File currentFile, String parentId, String parentWorkPath, - String taskId, String nodeId, String uploader) { - // 如果是文件直接处理 - if (currentFile.isFile()) { - String actualParentId = findActualParentIdForFile(currentFile, taskId, nodeId); - createFileRecord(currentFile, actualParentId, parentWorkPath, taskId, nodeId, uploader); - return; - } + private void operateTsfeiles(TsFiles tsFiles) { + //上传人是当前登录人 + UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); - // 处理目录中的文件 - File[] children = currentFile.listFiles(); - if (children != null) { - for (File child : children) { - if (child.isFile()) { - // 关键修改:根据文件实际路径查询父目录ID - String actualParentId = findActualParentIdForFile(child, taskId, nodeId); - createFileRecord(child, actualParentId, parentWorkPath, taskId, nodeId, uploader); - } else { - processFiles( - child, - parentId, - buildFolderPath(parentWorkPath + child.getName() + "/"), // 更新父路径 - taskId, - nodeId, - uploader - ); - } - } - } - } - - /** - * 根据文件物理路径查询对应的父目录ID - */ - private String findActualParentIdForFile(File file, String taskId, String nodeId) { - // 获取文件的逻辑父路径 - String parentWorkPath = buildCurrentPathForParent(file); - - // 查询数据库获取目录ID - TsFiles parentDir = tsFilesMapper.selectOne(new QueryWrapper() - .eq("task_id", taskId) - .eq("node_id", nodeId) - .eq("work_path", parentWorkPath) - .eq("is_file", "FOLDER")); - - return parentDir != null ? parentDir.getParentId() : null; - } - - /** - * 构建文件的父目录路径(优化版) - */ - private String buildCurrentPathForParent(File file) { - // 获取完整逻辑路径(示例:/99/解压目录/subfolder/) - String fullPath = buildCurrentPath("", file); - - // 确保路径中的多个 "/" 被替换成一个 "/" - fullPath = fullPath.replaceAll("/+", "/"); - - // 截取父目录部分 - if (file.isFile()) { - // 确保在路径中有 "/" 存在,且去除文件名 - int lastSlash = fullPath.lastIndexOf('/'); - if (lastSlash != -1) { - return fullPath.substring(0, lastSlash + 1); - } - // 如果路径中没有找到 "/",说明路径仅是文件名,返回空路径 - return "/"; - } - - // 对于文件夹路径,直接返回完整路径 - return fullPath; - } - - - /** - * 创建文件记录 - */ - private void createFileRecord(File file, String parentId, String parentWorkPath, - String taskId, String nodeId, String uploader) { - TsFiles record = buildCommonRecord(file, parentId, parentWorkPath, taskId, nodeId, uploader); - record.setIsFile("FILE"); - record.setFileSize(String.valueOf(file.length())); + String path = normalizePath(tsFiles.getWorkPath()); + tsFiles.setWorkPath(path); QueryWrapper query = new QueryWrapper() - .eq("node_id", nodeId) - .eq("task_id", taskId) - .eq("work_path", record.getWorkPath()) - .eq("file_name", file.getName()) - .eq("is_file", "FILE"); // 确保是文件类型 - + .eq("node_id", tsFiles.getNodeId()) + .eq("task_id", tsFiles.getTaskId()) + .eq("work_path", tsFiles.getWorkPath()) + .eq("file_name", tsFiles.getFileName()) + .eq("is_file", tsFiles.getIsFile()); TsFiles existing = tsFilesMapper.selectOne(query); if (existing != null) { existing.setUpdateTime(new Timestamp(System.currentTimeMillis())); + existing.setUploader(loginuser.getUsername()); tsFilesMapper.updateById(existing); } else { - tsFilesMapper.insert(record); + tsFiles.setUploader(loginuser.getUsername()); + tsFilesMapper.insert(tsFiles); } } + /** + * 路径标准化(确保以/结尾) + */ + private String buildFolderPathq(String path) { + path = path.replaceAll("/+", "/"); // 合并多个斜杠 + return path.startsWith("/") ? path : "/" + path; // 确保绝对路径 + } + // 文件夹路径标准化(确保以/结尾) private String buildFolderPath(String path) { return path.endsWith("/") ? path : path + "/"; } - /** - * 创建或更新目录记录(返回生成的目录ID) - */ - private String createOrUpdateFolderRecord(File folder, String parentId, String parentWorkPath, - String taskId, String nodeId, String uploader) { - TsFiles record = buildCommonRecord(folder, parentId, parentWorkPath, taskId, nodeId, uploader); - record.setIsFile("FOLDER"); - record.setFileSize("0"); - - QueryWrapper query = new QueryWrapper() - .eq("node_id", nodeId) - .eq("task_id", taskId) - .eq("work_path", record.getWorkPath()) - .eq("file_name", folder.getName()) - .eq("is_file", "FOLDER"); // 增加类型过滤 - - TsFiles existing = tsFilesMapper.selectOne(query); - if (existing != null) { - existing.setUpdateTime(new Timestamp(System.currentTimeMillis())); - tsFilesMapper.updateById(existing); - return existing.getId(); - } else { - tsFilesMapper.insert(record); - return record.getId(); - } - } - - - /** - * 公共记录构建方法 - */ - private TsFiles buildCommonRecord(File file, String parentId, String parentWorkPath, - String taskId, String nodeId, String uploader) { - TsFiles record = new TsFiles(); - record.setNodeId(nodeId); - record.setTaskId(taskId); - record.setParentId(parentId); - record.setFileName(file.getName()); - record.setWorkPath(buildCurrentPath(parentWorkPath, file)); - record.setUploadTime(new Timestamp(System.currentTimeMillis())); - record.setUploader(uploader); - return record; - } - - - /** - * 构建当前文件或文件夹的 work_path - * - * @return 当前文件或文件夹的 work_path - */ - private String buildCurrentPath(String parentPath, File file) { - // 1. 获取存储根路径(如 E:\yun) - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("name", "filePath"); - StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); - String rootPath = storageSourceConfig.getValue(); - - // 2. 计算相对路径(统一使用正斜杠) - String relativePath = file.getAbsolutePath() - .replace(rootPath, "") - .replace("\\", "/"); - - // 3. 处理文件路径 - if (file.isFile()) { - // 示例:relativePath = "99/解压目录/subfolder/file.txt" → "99/解压目录/subfolder/" - int lastSlash = relativePath.lastIndexOf('/'); - return (lastSlash != -1 ? relativePath.substring(0, lastSlash + 1) : ""); - } else { - // 目录路径:确保以/结尾 - int lastSlash = relativePath.lastIndexOf('/'); - return (lastSlash != -1 ? relativePath.substring(0, lastSlash + 1) : ""); - } - } - - /** * 解压 ZIP 文件到临时目录 * @@ -1254,6 +1187,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl * @param baseDir 基础解压目录(如 /var/storage) * @return 解压后的根目录 File 对象 */ + //这个方法会直接覆盖 下面那个不会 那个会报错 TODO private File unzipToTemp(Path zipFilePath, String baseDir) throws IOException { // 1. 直接构建目标路径(baseDir/zipName) String zipName = getFileNameWithoutExtension(zipFilePath.getFileName().toString()); @@ -1281,9 +1215,13 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 确保父目录存在 Files.createDirectories(destPath.getParent()); - // 写入文件(使用缓冲提升性能) + // 写入文件(使用缓冲提升性能,覆盖已存在的文件) try (BufferedOutputStream bos = new BufferedOutputStream( - Files.newOutputStream(destPath, StandardOpenOption.CREATE_NEW))) { + Files.newOutputStream(destPath, + StandardOpenOption.CREATE, // 如果文件不存在则创建 + StandardOpenOption.TRUNCATE_EXISTING // 如果文件存在则覆盖 + ) + )) { byte[] buffer = new byte[8192]; int len; while ((len = zis.read(buffer)) > 0) { @@ -1296,6 +1234,49 @@ public class TsFilesServiceImpl extends ServiceImpl impl } return destRoot.toFile(); } + //上面这个方法会覆盖 TODO +// private File unzipToTemp(Path zipFilePath, String baseDir) throws IOException { +// // 1. 直接构建目标路径(baseDir/zipName) +// String zipName = getFileNameWithoutExtension(zipFilePath.getFileName().toString()); +// Path destRoot = Paths.get(baseDir, zipName); +// Files.createDirectories(destRoot); // 确保目标目录存在 +// +// try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath.toFile()))) { +// ZipEntry entry; +// while ((entry = zis.getNextEntry()) != null) { +// if (entry.getName().startsWith("__MACOSX/")) { +// zis.closeEntry(); +// continue; +// } +// +// // 2. 直接解压到目标路径(不再嵌套临时目录) +// Path destPath = destRoot.resolve(entry.getName()).normalize(); +// validatePathSafety(destPath, destRoot); // 确保路径安全 +// +// // 3. 处理目录 +// if (entry.isDirectory()) { +// Files.createDirectories(destPath); +// } +// // 4. 处理文件 +// else { +// // 确保父目录存在 +// Files.createDirectories(destPath.getParent()); +// +// // 写入文件(使用缓冲提升性能) +// try (BufferedOutputStream bos = new BufferedOutputStream( +// Files.newOutputStream(destPath, StandardOpenOption.CREATE_NEW))) { +// byte[] buffer = new byte[8192]; +// int len; +// while ((len = zis.read(buffer)) > 0) { +// bos.write(buffer, 0, len); +// } +// } +// } +// zis.closeEntry(); +// } +// } +// return destRoot.toFile(); +// } /** @@ -1315,11 +1296,10 @@ public class TsFilesServiceImpl extends ServiceImpl impl return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); } - /*******************************************************本地和minio对比开始****************************************************************/ @Override - public TsFiles compareDirectories(String localPath, String minioPath) { + public TsFiles compareDirectories(List dataset, String nodeId, String taskId) { //查询本地文件路径根目录 如 E:\yun QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "filePath"); @@ -1329,25 +1309,111 @@ public class TsFilesServiceImpl extends ServiceImpl impl QueryWrapper queryWrapper1 = new QueryWrapper<>(); queryWrapper1.eq("name", "bucketName"); StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1); - - // 获取完整路径 - String fullLocalPath = localPath; - String fullMinioPath = minioPath; - List results = new ArrayList<>(); - - String localBasePath = storageSourceConfig.getValue() + localPath; // 获取文件列表 - Map localFiles = listLocalFiles(fullLocalPath); TsFiles tsFiles = new TsFiles(); try { - // 获取minio文件列表 - AbstractBaseFileService fileServiceMinio = storageSourceContext.getByStorageKey("minio"); - List fileItemListMinio = fileServiceMinio.fileLists(fullMinioPath); - // 获取本地文件列表 - AbstractBaseFileService fileServiceLocal = storageSourceContext.getByStorageKey("local"); - List fileItemListLocal = fileServiceLocal.fileLists(fullLocalPath); + List fileItemListMinio = new ArrayList<>(); + List fileItemListLocal = new ArrayList<>(); + if (StringUtils.isNotEmpty(nodeId) && StringUtils.isNotEmpty(taskId)) { + QueryWrapper queryWrapper2 = new QueryWrapper<>(); + queryWrapper2.eq("task_id", taskId) + .eq("node_id", nodeId).and(i -> i.eq("work_path", "/").or() + .eq("backup_path", "/")); + List allNodes = tsFilesMapper.selectList(queryWrapper2); + for (TsFiles tsFiles1 : allNodes) { + if (tsFiles1 != null) { + // 获取minio文件列表 + if (StringUtils.isNotEmpty(tsFiles1.getBackupPath())) { + AbstractBaseFileService fileServiceMinio = storageSourceContext.getByStorageKey("minio"); + List minioFiles = fileServiceMinio.fileListData(tsFiles1.getBackupPath(), tsFiles1.getFileName()); + // 将当前文件列表添加到 fileItemListMinio + if (minioFiles != null) { + fileItemListMinio.addAll(minioFiles); + } + } + + // 获取本地文件列表 + if (StringUtils.isNotEmpty(tsFiles1.getWorkPath())) { + AbstractBaseFileService fileServiceLocal = storageSourceContext.getByStorageKey("local"); + List localFiles = fileServiceLocal.fileListData(tsFiles1.getWorkPath(), tsFiles1.getFileName()); + + // 将当前文件列表添加到 fileItemListLocal + if (localFiles != null) { + fileItemListLocal.addAll(localFiles); + } + } + } + } + + } else { + for (String id : dataset) { + TsFiles tsFiles1 = tsFilesMapper.selectById(id); + if (tsFiles1 != null) { + // 获取minio文件列表 + if (StringUtils.isNotEmpty(tsFiles1.getBackupPath())) { + AbstractBaseFileService fileServiceMinio = storageSourceContext.getByStorageKey("minio"); + List minioFiles = fileServiceMinio.fileListData(tsFiles1.getBackupPath(), tsFiles1.getFileName()); + // 将当前文件列表添加到 fileItemListMinio + if (minioFiles != null) { + fileItemListMinio.addAll(minioFiles); + } + } + + // 获取本地文件列表 + if (StringUtils.isNotEmpty(tsFiles1.getWorkPath())) { + AbstractBaseFileService fileServiceLocal = storageSourceContext.getByStorageKey("local"); + List localFiles = fileServiceLocal.fileListData(tsFiles1.getWorkPath(), tsFiles1.getFileName()); + + // 将当前文件列表添加到 fileItemListLocal + if (localFiles != null) { + fileItemListLocal.addAll(localFiles); + } + } + } + } + } + + //去除重复的 + Set seenMinioPaths = new HashSet<>(); // 用于记录已经处理过的 path + name + Iterator iteratorMinio = fileItemListMinio.iterator(); + while (iteratorMinio.hasNext()) { + FileItemResult fileItemResult = iteratorMinio.next(); + String path = normalizePath(fileItemResult.getPath()); + String name = fileItemResult.getName(); + String uniqueKey = path + name; // 唯一标识 + if (seenMinioPaths.contains(uniqueKey)) { + // 如果已经存在,则移除当前项 + iteratorMinio.remove(); + } else { + // 如果不存在,则添加到已处理集合中,并更新 path + seenMinioPaths.add(uniqueKey); + fileItemResult.setPath(path); + } + } + + //去除重复的 + Set seenLocalPaths = new HashSet<>(); // 用于记录已经处理过的 path + name + Iterator iteratorLocal = fileItemListLocal.iterator(); + while (iteratorLocal.hasNext()) { + FileItemResult fileItemResult = iteratorLocal.next(); + String path = normalizePath(fileItemResult.getPath()); + String name = fileItemResult.getName(); + String uniqueKey = path + name; // 唯一标识 + + if (seenLocalPaths.contains(uniqueKey)) { + // 如果已经存在,则移除当前项 + iteratorLocal.remove(); + } else { + // 如果不存在,则添加到已处理集合中,并更新 path + seenLocalPaths.add(uniqueKey); + fileItemResult.setPath(path); + } + } + // 对比文件 - tsFiles = compareFiles(fileItemListMinio, fileItemListLocal, localBasePath, storageSourceConfig1.getValue()); + tsFiles = compareFiles(fileItemListMinio, fileItemListLocal, storageSourceConfig.getValue(), storageSourceConfig1.getValue()); + LOGGER.info("minio文件 " + fileItemListMinio); + LOGGER.info("本地文件 " + fileItemListLocal); } catch (Exception e) { e.printStackTrace(); @@ -1372,25 +1438,48 @@ public class TsFilesServiceImpl extends ServiceImpl impl } - public TsFiles compareFiles(List fileItemListMinio, List fileItemListLocal, String localBasePath, String bucketName) { + public TsFiles compareFiles(List fileItemListMinio, List fileItemListLocal, String filePath, String bucketName) { TsFiles tsFiles = new TsFiles(); // 将 MinIO 文件列表转换为 Map(文件名 -> 文件信息) Map minioFileMap = new HashMap<>(); for (FileItemResult file : fileItemListMinio) { - minioFileMap.put(file.getName(), file); + // 处理路径格式 + String path = normalizePath(file.getPath()); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("file_name", file.getName()); + queryWrapper.eq("backup_path", path); + TsFiles tsFiles1 = tsFilesMapper.selectOne(queryWrapper); + if (tsFiles1 != null) { + file.setId(tsFiles1.getId()); + + } + + minioFileMap.put(path + file.getName(), file); } // 将本地文件列表转换为 Map(文件名 -> 文件信息) Map localFileMap = new HashMap<>(); for (FileItemResult file : fileItemListLocal) { - localFileMap.put(file.getName(), file); + // 处理路径格式 + String path = normalizePath(file.getPath()); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("file_name", file.getName()); + queryWrapper.eq("work_path", path); + TsFiles tsFiles1 = tsFilesMapper.selectOne(queryWrapper); + if (tsFiles1 != null) { + file.setId(tsFiles1.getId()); + + } + localFileMap.put(path + file.getName(), file); } // 检查本地有而 MinIO 没有的文件 List localOnlyFiles = new ArrayList<>(); for (FileItemResult localFile : fileItemListLocal) { - if (!minioFileMap.containsKey(localFile.getName())) { + // 处理路径格式 + String path = normalizePath(localFile.getPath()); + if (!minioFileMap.containsKey(path + localFile.getName())) { localOnlyFiles.add(localFile); } } @@ -1399,7 +1488,8 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 检查 MinIO 有而本地没有的文件 List minioOnlyFiles = new ArrayList<>(); for (FileItemResult minioFile : fileItemListMinio) { - if (!localFileMap.containsKey(minioFile.getName())) { + String path = normalizePath(minioFile.getPath()); + if (!localFileMap.containsKey(path + minioFile.getName())) { minioOnlyFiles.add(minioFile); } } @@ -1407,18 +1497,17 @@ public class TsFilesServiceImpl extends ServiceImpl impl // 检查文件大小不一致的文件 List sizeMismatchedFiles = new ArrayList<>(); - // 检查 MD5 不一致的文件 - //List md5MismatchedFiles = new ArrayList<>(); // 检查 MD5 不一致的文件 List md5MismatchedFiles = new ArrayList<>(); // 遍历 MinIO 文件 for (FileItemResult minioFile : fileItemListMinio) { + String path = normalizePath(minioFile.getPath()); // 检查 MinIO 文件是否在本地存在 - if (localFileMap.containsKey(minioFile.getName())) { - FileItemResult localFile = localFileMap.get(minioFile.getName()); - + if (localFileMap.containsKey(path + minioFile.getName())) { + FileItemResult localFile = localFileMap.get(path + minioFile.getName()); + String localBasePath = filePath + minioFile.getPath(); // 检查文件是否为文件夹,如果是文件夹,跳过 File localFilePath = new File(localBasePath + localFile.getName()); if (localFilePath.isDirectory()) { @@ -1465,29 +1554,15 @@ public class TsFilesServiceImpl extends ServiceImpl impl LOGGER.info("MinIO 中没有该文件,本地有文件,跳过对比: " + minioFile.getName()); } } -// tsFiles.setLocalOnlyFiles(localOnlyFiles); -// tsFiles.setMinioOnlyFiles(minioOnlyFiles); -// tsFiles.setMd5MismatchedFiles(md5MismatchedFiles); + tsFiles.setLocalOnlyFiles(localOnlyFiles); + tsFiles.setMinioOnlyFiles(minioOnlyFiles); + tsFiles.setMd5MismatchedFiles(md5MismatchedFiles); LOGGER.info("文件大小不一致的文件:" + sizeMismatchedFiles); LOGGER.info("文件大小一致但 MD5 不一致的文件:" + md5MismatchedFiles); return tsFiles; } - - /** - * 递归列出本地文件 - * - * @param path 要列出的本地目录路径 - * @return 文件信息Map,key为相对路径,value为文件信息 - */ - private Map listLocalFiles(String path) { - Map files = new HashMap<>(); - File root = new File(path); - listLocalFilesRecursive(root, root.getAbsolutePath(), files); - return files; - } - /** * 递归列出本地文件 * @@ -1511,51 +1586,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl } } - /** - * 计算文件的MD5哈希值(支持大文件流式处理) - * - * @param file 要计算的文件对象 - * @return 32位小写十六进制MD5字符串,计算失败返回null - */ - private String calculateMD5(File file) { - // 前置校验 - if (!file.exists() || file.isDirectory()) { - return null; - } - final int BUFFER_SIZE = 8192; // 8KB缓冲区 - try (// 自动关闭资源 - InputStream is = new FileInputStream(file); - // 使用DigestInputStream自动处理摘要计算 - DigestInputStream dis = new DigestInputStream(is, MessageDigest.getInstance("MD5"))) { - - // 读取缓冲区(仅用于更新摘要,无需处理数据) - byte[] buffer = new byte[BUFFER_SIZE]; - // 循环读取直到文件结束(read方法返回值被忽略,重点是通过读取更新摘要) - while (dis.read(buffer) != -1) { /* 空循环体 */ } - - // 获取最终的摘要字节数组 - byte[] digest = dis.getMessageDigest().digest(); - - // 将字节数组转换为十六进制字符串 - StringBuilder hexString = new StringBuilder(); - for (byte b : digest) { - // 确保每个字节转换为两位十六进制 - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); // 补零 - } - hexString.append(hex); - } - return hexString.toString(); - } catch (NoSuchAlgorithmException e) { - // Java标准库保证MD5存在,此处异常仅为防御性编程 - throw new RuntimeException("MD5 algorithm not found", e); - } catch (IOException e) { - System.err.println("Error reading file: " + file.getAbsolutePath()); - return null; - } - } /*******************************************************本地和minio对比结束****************************************************************/ @@ -1564,18 +1595,214 @@ public class TsFilesServiceImpl extends ServiceImpl impl /******************************************************对比结束以后开始同步***************************************************************************************/ /********************************** * 用途说明: 将文件上传到备份空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 + * 参数说明 parameter 数据集合 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ @Override - public Boolean uploadToBackup(String paths, String names, String sizes) { + public Boolean uploadToBackup(Parameter parameter) { + Boolean value = false; + + // 设置当前时间 + LocalDateTime now = LocalDateTime.now(); + // 转换为 Timestamp + Timestamp currentTime = Timestamp.valueOf(now); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("name", "filePath"); + StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); + QueryWrapper queryWrapper1 = new QueryWrapper<>(); + queryWrapper1.eq("name", "bucketName"); + StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1); + + List parameterLists = parameter.getParameterLists(); + + //循环一条一条上传 + for (ParameterList parameterListData : parameterLists) { + if ("FOLDER".equals(parameterListData.getType())) { + String name = parameterListData.getName(); + String path = parameterListData.getPath(); + // 处理路径格式 + path = normalizePath(path); + + + String localPath = storageSourceConfig.getValue() + path;//localPath是本地的路径 + String minioPath = path;//// MinIO上传目标路径 + // 使用Java NIO的Path类来构造压缩文件的完整路径,存储在localPath目录下 + // 查询当前文件记录 + TsFiles currentFile = getFileRecord(name, path); + if (currentFile == null) return false; + + // 递归创建父文件夹 + boolean parentsCreated = createParentFolders(currentFile.getParentId()); + if (!parentsCreated) { + LOGGER.error("父文件夹创建失败"); + return false; + } + + //新增节点的时候 创建文件夹 + NewFolderRequest newFolderRequest = new NewFolderRequest(); + newFolderRequest.setName(currentFile.getFileName());//新建的文件夹名称,示例值(/a/b/c) + newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问,则支持请求密码,示例值(123456) + newFolderRequest.setPath(minioPath);//请求路径,示例值(/) + newFolderRequest.setStorageKey("minio");//存储源 key,示例值(local minio) + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey()); + boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName()); + LOGGER.info("同步本地到minio文件夹路径加名称" + newFolderRequest.getPath()+newFolderRequest.getName()); + + //如果文件夹创建成功 + if (flag) { + currentFile.setUpdateTime(currentTime); + currentFile.setBackupPath(minioPath); + int valueAdded = tsFilesMapper.updateById(currentFile); + if (valueAdded == 1) { + LOGGER.info("同步本地到minio,minio文件夹创建成功,表结构修改成功。"); + value = true; + } else { + LOGGER.error("同步本地到minio,minio文件夹创建成功,表结构修改失败"); + value = false; + } + + } else { + LOGGER.error("同步本地到minio,minio文件夹创建失败"); + value = false; + } + } else { + + String name = parameterListData.getName(); + String path = parameterListData.getPath(); + String size = parameterListData.getSize(); + // 处理路径格式 + path = normalizePath(path); + + String localPath = storageSourceConfig.getValue() + path;//localPath是本地的路径 + String minioPath = path;//// MinIO上传目标路径 + + // 使用Java NIO的Path类来构造压缩文件的完整路径,存储在localPath目录下 + Path FilePath = Paths.get(localPath, name); + // 4. 上传压缩文件到MinIO + File zipFile = FilePath.toFile(); // 获取本地文件 + + // 查询当前文件记录 + TsFiles currentFile = getFileRecord(name, path); + if (currentFile == null) return false; + + // 递归创建父文件夹 + boolean parentsCreated = createParentFolders(currentFile.getParentId()); + if (!parentsCreated) { + LOGGER.error("父文件夹创建失败"); + return false; + } + //把本地文件上传到minio + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("minio"); + boolean flag1 = fileService.UploadFiles(storageSourceConfig1.getValue(), minioPath + name, zipFile); + LOGGER.info("同步本地到minio文件路径加名称" + storageSourceConfig1.getValue()+minioPath + name); + + //如果上传成功 修改 表结构 + if (flag1) { + currentFile.setUpdateTime(currentTime); + currentFile.setBackupPath(minioPath); + int valueAdded = tsFilesMapper.updateById(currentFile); + if (valueAdded == 1) { + LOGGER.info("同步本地到minio,minio文件创建成功,表结构修改成功。"); + value = true; + } else { + LOGGER.error("同步本地到minio,minio文件创建成功,表结构修改失败"); + value = false; + } + } else { + LOGGER.error("同步本地到minio,minio文件创建失败"); + value = false; + } + } + } + return value; + } + + // 规范化路径格式 + private String normalizePath(String path) { + if (!path.startsWith("/")) path = "/" + path; + if (!path.endsWith("/")) path += "/"; + return path.replace("//", "/"); + } + + // 查询文件记录 + private TsFiles getFileRecord(String name, String path) { + QueryWrapper query = new QueryWrapper<>(); + query.eq("file_name", name) + .eq("work_path", path); + return tsFilesMapper.selectOne(query); + } + + // 递归创建minio父文件夹 + private boolean createParentFolders(String parentId) { + TsFiles parentFolder = tsFilesMapper.selectById(parentId); + if (parentFolder == null || "00".equals(parentId)) { + return true; // 到达根目录 + } + + // 先递归处理上级 + boolean ancestorsCreated = createParentFolders(parentFolder.getParentId()); + if (!ancestorsCreated) return false; + + // 检查当前文件夹是否已创建 + if (StringUtils.isEmpty(parentFolder.getBackupPath())) { + boolean folderCreated = createMinioFolder(parentFolder); + if (!folderCreated) return false; + } + return true; + } + + // 创建MinIO文件夹 + private boolean createMinioFolder(TsFiles folder) { + try { + // 设置当前时间 + LocalDateTime now = LocalDateTime.now(); + // 转换为 Timestamp + Timestamp currentTime = Timestamp.valueOf(now); + AbstractBaseFileService minioService = storageSourceContext.getByStorageKey("minio"); + //String folderPath = folder.getWorkPath() + folder.getFileName() + "/"; + String folderPath = folder.getWorkPath() + folder.getFileName(); + + //新增节点的时候 创建文件夹 + NewFolderRequest newFolderRequest = new NewFolderRequest(); + newFolderRequest.setName(folder.getFileName());//新建的文件夹名称,示例值(/a/b/c) + newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问,则支持请求密码,示例值(123456) + newFolderRequest.setPath(folder.getWorkPath());//请求路径,示例值(/) + newFolderRequest.setStorageKey("minio");//存储源 key,示例值(local minio) + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey()); + boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName()); + if (flag) { + folder.setUpdateTime(currentTime); + folder.setBackupPath(folder.getWorkPath()); + tsFilesMapper.updateById(folder); + LOGGER.info("创建文件夹成功:{}", folderPath); + return true; + } + return false; + } catch (Exception e) { + LOGGER.error("创建文件夹失败:{}", folder.getFileName(), e); + return false; + } + } + + + /********************************** + * 用途说明: 从备份空间下载到工作空间 + * 参数说明 parameter 数据集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 + ***********************************/ + @Override + public Boolean downloadToLocal(Parameter parameter) { Boolean value = true; + // 设置当前时间 + LocalDateTime now = LocalDateTime.now(); + // 转换为 Timestamp + Timestamp currentTime = Timestamp.valueOf(now); + //上传人是当前登录人 -// UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); -// LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "filePath"); @@ -1585,122 +1812,141 @@ public class TsFilesServiceImpl extends ServiceImpl impl QueryWrapper queryWrapper1 = new QueryWrapper<>(); queryWrapper1.eq("name", "bucketName"); StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1); + List parameterLists = parameter.getParameterLists(); + for (ParameterList parameterListData : parameterLists) { + //创建文件夹 + if ("FOLDER".equals(parameterListData.getType())) { + String name = parameterListData.getName(); + String path = parameterListData.getPath(); + // 处理路径格式 + path = normalizePath(path); - List pathsList = Arrays.asList(paths.split(",")); - List nameList = Arrays.asList(names.split(",")); - List sizeList = Arrays.asList(sizes.split(",")); + //通过名称和minio路径查询表结构的数据 + QueryWrapper queryWrapperFile = new QueryWrapper<>(); + queryWrapperFile.eq("file_name", name); + queryWrapperFile.eq("backup_path", path); + TsFiles tsFiles = tsFilesMapper.selectOne(queryWrapperFile); + if (tsFiles == null) { + return false; + } + LOGGER.info("同步minio到本地,查询的文件夹对象", tsFiles); - for (int i = 0; i < nameList.size(); i++) { - String name = nameList.get(i).trim(); - String path = pathsList.get(i).trim(); - // 确保 path 最前面有 / - if (!path.startsWith("/")) { - path = "/" + path; - } - // 确保 path 最后面有 / - if (!path.endsWith("/")) { - path = path + "/"; - } - String size = sizeList.get(i).trim(); - - String localPath = storageSourceConfig.getValue() + path;//localPath是本地的路径 - String minioPath = path;//// MinIO上传目标路径 - // 使用Java NIO的Path类来构造压缩文件的完整路径,存储在localPath目录下 - Path FilePath = Paths.get(localPath, name); - // 4. 上传压缩文件到MinIO - File zipFile = FilePath.toFile(); // 获取本地文件 - - - //判断是不是文件夹 如果是 增加节点表 - File localFilePath = new File(localPath + name); - if (localFilePath.isDirectory()) { - - String nodeName = path.replaceAll("(^/|^\\\\|/|\\\\$)", ""); - QueryWrapper queryWrapperNode = new QueryWrapper<>(); - queryWrapperNode.eq("node_name", nodeName); - TsNodes tsNodesData = tsNodesMapper.selectOne(queryWrapperNode); - //增加节点表 - TsNodes tsnodes = new TsNodes(); - tsnodes.setParentId(tsNodesData.getNodeId());//上级节点的 节点ID - tsnodes.setTaskId(tsNodesData.getTaskId()); //任务ID - tsnodes.setNodeName(name);//节点名称 是文件名 - //创建人是当前登录人 - //tsnodes.setCreator(loginuser.getUsername()); - tsnodes.setCreator(null); - //当前操作时间 - LocalDateTime now = LocalDateTime.now(); - // 转换为 Timestamp - Timestamp currentTime = Timestamp.valueOf(now); - tsnodes.setCreateTime(currentTime); - //通过获取上级节点的条数 设置节点顺序 - QueryWrapper queryWrapperNodeOrder = new QueryWrapper<>(); - queryWrapperNodeOrder.eq("parent_id", tsnodes.getParentId()); - int orderno = tsNodesMapper.selectCount(queryWrapperNodeOrder) + 1; - //序号 - tsnodes.setNodeOrder(orderno); - - //判断 local或者minio有没有成功 - //新增节点的时候 创建文件夹 - NewFolderRequest newFolderRequest = new NewFolderRequest(); - newFolderRequest.setName(tsnodes.getNodeName());//新建的文件夹名称,示例值(/a/b/c) - newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问,则支持请求密码,示例值(123456) - newFolderRequest.setPath(minioPath);//请求路径,示例值(/) - newFolderRequest.setStorageKey("minio");//存储源 key,示例值(local minio) - AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey()); - boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName()); - - int valueAdded = tsNodesMapper.insert(tsnodes); - if (valueAdded == 1) { - value = true; - } else { - value = false; + // 递归创建父文件夹 + boolean parentsCreated = createLocalParentFolders(tsFiles.getParentId()); + if (!parentsCreated) { + LOGGER.error("父文件夹创建失败"); + return false; } + //新增节点的时候 创建文件夹 + NewFolderRequest newFolderRequest = new NewFolderRequest(); + newFolderRequest.setName(tsFiles.getFileName());//新建的文件夹名称,示例值(/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()); + LOGGER.info("同步minio到本地文件夹路径加名称" + newFolderRequest.getPath()+ newFolderRequest.getName()); + + //如果文件夹创建成功 + if (flag) { + tsFiles.setUpdateTime(currentTime); + tsFiles.setWorkPath(path); + int valueAdded = tsFilesMapper.updateById(tsFiles); + if (valueAdded == 1) { + LOGGER.info("同步minio到本地,本地文件夹创建成功,表结构修改成功。"); + value = true; + } else { + LOGGER.error("同步minio到本地,本地文件夹创建成功,表结构修改失败"); + value = false; + } + + } else { + LOGGER.error("同步minio到本地,本地文件夹创建失败"); + value = false; + } + } else { //如果是文件 - boolean flag1 = false; + String name = parameterListData.getName(); + String path = parameterListData.getPath(); + String size = parameterListData.getSize(); + // 处理路径格式 + path = normalizePath(path); + + //通过名称和minio路径查询表结构的数据 + QueryWrapper queryWrapperFile = new QueryWrapper<>(); + queryWrapperFile.eq("file_name", name); + queryWrapperFile.eq("backup_path", path); + TsFiles tsFiles = tsFilesMapper.selectOne(queryWrapperFile); + if (tsFiles == null) { + return false; + } + // 递归创建父文件夹 + boolean parentsCreated = createLocalParentFolders(tsFiles.getParentId()); + if (!parentsCreated) { + LOGGER.error("父文件夹创建失败"); + return false; + } + + String localPath = storageSourceConfig.getValue() + path + name;//localPath是本地的路径 + String minioPath = path + name;//// MinIO上传目标路径 + + FileOutputStream outputStream = null; // 输出流 + S3ObjectInputStream inputStream = null; // 输入流 + S3Object s3Object = null; try { AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("minio"); - flag1 = fileService.UploadFiles(storageSourceConfig1.getValue(), minioPath + name, zipFile); - } catch (Exception e) { - e.printStackTrace(); - value = false; - } - //如果minio增加成功 新增表结构 - if (flag1) { - QueryWrapper queryWrapper2 = new QueryWrapper<>(); - queryWrapper2.eq("file_name", name);//名称 - queryWrapper2.eq("work_path", path);//路径 - List tsFiles = tsFilesMapper.selectList(queryWrapper2); - //如果说没有的话 我就新增 有的话就不变 - if (tsFiles == null || tsFiles.isEmpty()) { - QueryWrapper queryWrapper3 = new QueryWrapper<>(); - queryWrapper3.eq("work_path", path);//路径 - List tsFilesList = tsFilesMapper.selectList(queryWrapper3); + // 获取 MinIO 文件对象 + s3Object = fileService.getObject(storageSourceConfig1.getValue(), minioPath); + // 获取文件输入流 + inputStream = s3Object.getObjectContent(); - TsFiles files1 = new TsFiles(); - files1.setTaskId(tsFilesList.get(0).getTaskId()); - files1.setNodeId(tsFilesList.get(0).getNodeId()); - files1.setFileName(name); - files1.setFileSize(size); - files1.setWorkPath(path); - files1.setBackupPath(path); - files1.setKeywords(null); - files1.setDescription(null); - // 设置当前时间 - LocalDateTime now = LocalDateTime.now(); - // 转换为 Timestamp - Timestamp currentTime = Timestamp.valueOf(now); - files1.setUploadTime(currentTime); - //获取登录人 - //files1.setUploader(loginuser.getUsername()); - files1.setUploader(null); - int valueAdded = tsFilesMapper.insert(files1); - if (valueAdded == 1) { - value = true; - } else { - value = false; + // 创建本地文件 + File localFile = new File(localPath); + if (!localFile.getParentFile().exists()) { + localFile.getParentFile().mkdirs(); // 创建父目录 + } + + // 将文件内容写入本地文件 + outputStream = new FileOutputStream(localFile); + IOUtils.copy(inputStream, outputStream); + //文件下载成功以后 修改表结构 + tsFiles.setUpdateTime(currentTime); + tsFiles.setWorkPath(path); + int valueAdded = tsFilesMapper.updateById(tsFiles); + if (valueAdded == 1) { + LOGGER.info("同步minio到本地,本地文件创建成功,表结构修改成功。"); + LOGGER.info("文件下载成功:" + localPath); + value = true; + } else { + LOGGER.error("同步minio到本地,本地文件创建成功,表结构修改失败"); + value = false; + } + } catch (Exception e) { + LOGGER.error("文件下载失败:" + e.getMessage()); + } finally { + // 关闭流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + LOGGER.error("关闭输入流失败:" + e.getMessage()); + } + } + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + LOGGER.error("关闭输出流失败:" + e.getMessage()); + } + } + if (s3Object != null) { + try { + s3Object.close(); + } catch (IOException e) { + LOGGER.error("关闭 S3Object 失败:" + e.getMessage()); } } } @@ -1709,134 +1955,56 @@ public class TsFilesServiceImpl extends ServiceImpl impl return value; } - /********************************** - * 用途说明: 从备份空间下载到工作空间 - * 参数说明 paths 路径集合 - * 参数说明 names 文件名集合 - * 参数说明 sizes 文件大小集合 - * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 - ***********************************/ - @Override - public Boolean downloadToLocal(String paths, String names, String sizes) { - Boolean value = true; - //上传人是当前登录人 -// UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); -// LoginUser loginuser = (LoginUser) authentication.getPrincipal(); - - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("name", "filePath"); - StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper); - - - QueryWrapper queryWrapper1 = new QueryWrapper<>(); - queryWrapper1.eq("name", "bucketName"); - StorageSourceConfig storageSourceConfig1 = storageSourceConfigMapper.selectOne(queryWrapper1); - - List pathsList = Arrays.asList(paths.split(",")); - List nameList = Arrays.asList(names.split(",")); - List sizeList = Arrays.asList(sizes.split(",")); - - for (int i = 0; i < nameList.size(); i++) { - String name = nameList.get(i).trim(); - String path = pathsList.get(i).trim(); - // 确保 path 最前面有 / - if (!path.startsWith("/")) { - path = "/" + path; - } - // 确保 path 最后面有 / - if (!path.endsWith("/")) { - path = path + "/"; - } - String size = sizeList.get(i).trim(); - - String localPath = storageSourceConfig.getValue() + path + name;//localPath是本地的路径 - String minioPath = path + name;//// MinIO上传目标路径 - - FileOutputStream outputStream = null; // 输出流 - S3ObjectInputStream inputStream = null; // 输入流 - S3Object s3Object = null; - - try { - AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("minio"); - // 获取 MinIO 文件对象 - s3Object = fileService.getObject(storageSourceConfig1.getValue(), minioPath); - // 获取文件输入流 - inputStream = s3Object.getObjectContent(); - - // 创建本地文件 - File localFile = new File(localPath); - if (!localFile.getParentFile().exists()) { - localFile.getParentFile().mkdirs(); // 创建父目录 - } - - // 将文件内容写入本地文件 - outputStream = new FileOutputStream(localFile); - IOUtils.copy(inputStream, outputStream); - System.out.println("文件下载成功:" + localPath); - } catch (Exception e) { - System.err.println("文件下载失败:" + e.getMessage()); - } finally { - // 关闭流 - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - System.err.println("关闭输入流失败:" + e.getMessage()); - } - } - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException e) { - System.err.println("关闭输出流失败:" + e.getMessage()); - } - } - if (s3Object != null) { - try { - s3Object.close(); - } catch (IOException e) { - System.err.println("关闭 S3Object 失败:" + e.getMessage()); - } - } - } - - QueryWrapper queryWrapper2 = new QueryWrapper<>(); - queryWrapper2.eq("file_name", name);//名称 - queryWrapper2.eq("work_path", path);//路径 - List tsFiles = tsFilesMapper.selectList(queryWrapper2); - //如果说没有的话 我就新增 有的话就不变 - if (tsFiles == null || tsFiles.isEmpty()) { - QueryWrapper queryWrapper3 = new QueryWrapper<>(); - queryWrapper3.eq("work_path", path);//路径 - List tsFilesList = tsFilesMapper.selectList(queryWrapper3); - - TsFiles files1 = new TsFiles(); - files1.setTaskId(tsFilesList.get(0).getTaskId()); - files1.setNodeId(tsFilesList.get(0).getNodeId()); - files1.setFileName(name); - files1.setFileSize(size); - files1.setWorkPath(path); - files1.setBackupPath(path); - files1.setKeywords(null); - files1.setDescription(null); - // 设置当前时间 - LocalDateTime now = LocalDateTime.now(); - // 转换为 Timestamp - Timestamp currentTime = Timestamp.valueOf(now); - files1.setUploadTime(currentTime); - //获取登录人 - //files1.setUploader(loginuser.getUsername()); - files1.setUploader(null); - int valueAdded = tsFilesMapper.insert(files1); - if (valueAdded == 1) { - value = true; - } else { - value = false; - } - } + // 递归创建本地父文件夹 + private boolean createLocalParentFolders(String parentId) { + TsFiles parentFolder = tsFilesMapper.selectById(parentId); + if (parentFolder == null || "00".equals(parentId)) { + return true; // 到达根目录 + } + + // 先递归处理上级 + boolean ancestorsCreated = createLocalParentFolders(parentFolder.getParentId()); + if (!ancestorsCreated) return false; + + // 检查当前文件夹是否已创建 + if (StringUtils.isEmpty(parentFolder.getWorkPath())) { + boolean folderCreated = createLocalFolder(parentFolder); + if (!folderCreated) return false; + } + return true; + } + + // 创建本地文件夹 + private boolean createLocalFolder(TsFiles folder) { + try { + // 设置当前时间 + LocalDateTime now = LocalDateTime.now(); + // 转换为 Timestamp + Timestamp currentTime = Timestamp.valueOf(now); + //String folderPath = folder.getWorkPath() + folder.getFileName() + "/"; + String folderPath = folder.getBackupPath() + folder.getFileName(); + + //新增节点的时候 创建文件夹 + NewFolderRequest newFolderRequest = new NewFolderRequest(); + newFolderRequest.setName(folder.getFileName());//新建的文件夹名称,示例值(/a/b/c) + newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问,则支持请求密码,示例值(123456) + newFolderRequest.setPath(folder.getBackupPath());//请求路径,示例值(/) + newFolderRequest.setStorageKey("local");//存储源 key,示例值(local minio) + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey()); + boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName()); + if (flag) { + folder.setUpdateTime(currentTime); + folder.setWorkPath(folder.getBackupPath()); + tsFilesMapper.updateById(folder); + LOGGER.info("创建文件夹成功:{}", folderPath); + return true; + } + return false; + } catch (Exception e) { + LOGGER.error("创建文件夹失败:{}", folder.getFileName(), e); + return false; } - return value; } @@ -2039,6 +2207,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl newRecord.setParentId(parentId); newRecord.setFileName(targetFileName); newRecord.setWorkPath(newWorkPath); + newRecord.setBackupPath(fileRecord.getBackupPath()); newRecord.setFileSize(fileRecord.getFileSize()); newRecord.setKeywords(fileRecord.getKeywords()); newRecord.setDescription(fileRecord.getDescription()); @@ -2052,9 +2221,8 @@ public class TsFilesServiceImpl extends ServiceImpl impl } } // 删除原记录 - List ids = new ArrayList<>(); - ids.add(fileRecord.getId()); - deleteTsFilesByIds(ids); + tsFilesMapper.deleteById(fileRecord.getId()); + } else { // 重命名模式:插入新记录 TsFiles newRecord = new TsFiles(); @@ -2064,12 +2232,14 @@ public class TsFilesServiceImpl extends ServiceImpl impl newRecord.setParentId(parentId); newRecord.setFileName(targetFileName); newRecord.setWorkPath(newWorkPath); + newRecord.setBackupPath(fileRecord.getBackupPath()); newRecord.setFileSize(fileRecord.getFileSize()); newRecord.setKeywords(fileRecord.getKeywords()); newRecord.setDescription(fileRecord.getDescription()); newRecord.setUploadTime(new Timestamp(System.currentTimeMillis())); // 上传时间 newRecord.setUploader(fileRecord.getUploader()); // 上传人 tsFilesMapper.insert(newRecord); + // 删除原记录 tsFilesMapper.deleteById(fileRecord.getId()); } } @@ -2106,6 +2276,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl newChild.setParentId(parentId); newChild.setFileName(child.getFileName()); newChild.setWorkPath(childNewPath); + newChild.setBackupPath(child.getBackupPath()); newChild.setFileSize(child.getFileSize()); newChild.setKeywords(child.getKeywords()); newChild.setDescription(child.getDescription()); @@ -2121,16 +2292,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl } } - private String getRelativePath(String fullPath, String oldPrefix) { - // 防御性校验:严格路径匹配 - if (!fullPath.startsWith(oldPrefix)) { - throw new IllegalArgumentException("路径不隶属于旧目录: " + fullPath); - } - String relative = fullPath.substring(oldPrefix.length()); - return relative.startsWith("/") ? relative.substring(1) : relative; // 去除开头的/ - } - - /** * 检查路径是否为空 */ @@ -2211,8 +2372,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl } - - //========================= 核心辅助方法 =========================// /** @@ -2459,74 +2618,76 @@ public class TsFilesServiceImpl extends ServiceImpl impl ***********************************/ @Override public DualTreeResponse listLocalAndBackup(String taskId, String nodeId) { - // 1. 查询根节点 + // 1. 查询符合条件的所有节点(一次性加载,避免递归查询数据库) QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("node_id",nodeId); - queryWrapper.eq("task_id",taskId); - List rootNodes = tsFilesMapper.selectList(queryWrapper); + queryWrapper.eq("task_id", taskId) + .eq("node_id", nodeId); + List allNodes = tsFilesMapper.selectList(queryWrapper); - // 2. 构建本地树(workPath) + // 2. 构建内存中的父子关系映射(提高查询效率) + Map> parentChildrenMap = allNodes.stream() + .collect(Collectors.groupingBy(TsFiles::getParentId)); + + // 3. 构建本地树和Minio树 List localTrees = new ArrayList<>(); - for (TsFiles rootNode : rootNodes) { - TreeDTO localTree = buildTree(rootNode, true); - if (localTree != null) { - localTrees.add(localTree); - } - } - - // 3. 构建 Minio 树(backupPath) List minioTrees = new ArrayList<>(); - for (TsFiles rootNode : rootNodes) { - TreeDTO minioTree = buildTree(rootNode, false); - if (minioTree != null) { - minioTrees.add(minioTree); + + // 4. 从顶级节点(parentId为00)开始构建树 + List rootNodes = parentChildrenMap.get("00"); + if (rootNodes != null) { + for (TsFiles rootNode : rootNodes) { + // 构建本地树 + TreeDTO localTree = buildTree(rootNode, parentChildrenMap, true); + if (localTree != null) { + localTrees.add(localTree); + } + + // 构建Minio树 + TreeDTO minioTree = buildTree(rootNode, parentChildrenMap, false); + if (minioTree != null) { + minioTrees.add(minioTree); + } } } - // 4. 返回双树结构 return new DualTreeResponse(localTrees, minioTrees); } /** - * 递归构建树形结构 + * 递归构建树形结构(内存操作,不再查询数据库) * - * @param tsFiles 当前节点 - * @param isLocal 是否是本地树(true: workPath, false: backupPath) - * @return 构建好的树节点 + * @param current 当前节点 + * @param parentChildrenMap 父子关系映射表 + * @param isLocal 是否是本地树 + * @return 有效的树节点(路径不为空) */ - private TreeDTO buildTree(TsFiles tsFiles, boolean isLocal) { - // 1. 将当前节点转换为 DTO - TreeDTO dto = convertToDTO(tsFiles, isLocal); - - // 2. 如果当前节点路径为空,则直接返回(不构建子树) + private TreeDTO buildTree(TsFiles current, Map> parentChildrenMap, boolean isLocal) { + // 1. 转换为DTO并检查路径有效性 + TreeDTO dto = convertToDTO(current, isLocal); + String path = isLocal ? current.getWorkPath() : current.getBackupPath(); if (dto.getPath() == null || dto.getPath().trim().isEmpty()) { return null; } - // 3. 查询当前节点的子节点 - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("parent_id",tsFiles.getId()); - List children = tsFilesMapper.selectList(queryWrapper); - - if (children != null && !children.isEmpty()) { - // 4. 递归构建子树 + // 2. 查找子节点(从内存映射中获取) + List children = parentChildrenMap.get(current.getId()); + if (children != null) { for (TsFiles child : children) { - TreeDTO childDto = buildTree(child, isLocal); + TreeDTO childDto = buildTree(child, parentChildrenMap, isLocal); if (childDto != null) { dto.getChildren().add(childDto); } } } - - // 5. 返回构建好的树节点 return dto; } + /** * 将 FileNode 转换为 TreeDTO * - * @param node 当前节点 - * @param isLocal 是否是本地树(true: workPath, false: backupPath) + * @param node 当前节点 + * @param isLocal 是否是本地树(true: workPath, false: backupPath) * @return 转换后的 DTO */ private TreeDTO convertToDTO(TsFiles node, boolean isLocal) { @@ -2544,6 +2705,53 @@ public class TsFilesServiceImpl extends ServiceImpl impl dto.setUploadTime(node.getUploadTime()); dto.setUploader(node.getUploader()); dto.setUpdateTime(node.getUpdateTime()); + + //查询本地 + if (isLocal) { + String workPath = node.getWorkPath(); + if (StringUtils.isEmpty(workPath)) { + dto.setPath(null); + return dto; + } + String fileNameData = node.getFileName(); + //主要是用于文件路径加名称 + String path = workPath + fileNameData; + //准备获取文件的信息 + if ("FILE".equals(node.getIsFile())) { + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("local"); + FileItemResult fileItemResult = fileService.getFileItem(path); + dto.setUrl(fileItemResult.getUrl()); + //如果是压缩文件 类型就给zip + boolean isValid = hasValidExtension(fileItemResult.getName()); + if (isValid) { + dto.setType("ZIP"); + } else { + dto.setType(fileItemResult.getType().getValue()); + } + } + } else { + String backupPath = node.getBackupPath(); + if (backupPath == null || StringUtils.isEmpty(backupPath)) { + dto.setPath(null); + return dto; + } + String fileNameData = node.getFileName(); + //主要是用于文件路径加名称 + String path = backupPath + fileNameData; + //准备获取文件的信息 + if ("FILE".equals(node.getIsFile())) { + AbstractBaseFileService fileService = storageSourceContext.getByStorageKey("minio"); + FileItemResult fileItemResult = fileService.getFileItem(path); + dto.setUrl(fileItemResult.getUrl()); + //如果是压缩文件 类型就给zip + boolean isValid = hasValidExtension(fileItemResult.getName()); + if (isValid) { + dto.setType("ZIP"); + } else { + dto.setType(fileItemResult.getType().getValue()); + } + } + } // 设置路径字段 dto.setPath(isLocal ? node.getWorkPath() : node.getBackupPath()); return dto; diff --git a/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/FilesController.java b/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/FilesController.java index 0f8c1cf..68f97dd 100644 --- a/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/FilesController.java +++ b/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/FilesController.java @@ -47,6 +47,7 @@ public class FilesController { * pageNum 当前页 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 ***********************************/ + @Log(module = "专项文档管理", value = "分页查询专项文档管理文档内容") @GetMapping("/page") @ApiOperation("分页查询专项文档管理文档内容") @PreAuthorize("@el.check('select:files')") @@ -63,7 +64,7 @@ public class FilesController { * Files 文档内容 * 返回值说明: com.yfd.platform.config.ResponseResult 返回新增成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "新增专项文档管理文档内容!",type = "1") + @Log(module = "专项文档管理", value = "新增专项文档管理文档内容!") @PostMapping("/addFiles") @ApiOperation("新增专项文档管理文档内容") @ResponseBody @@ -87,7 +88,7 @@ public class FilesController { * Files 文档内容 * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "修改专项文档管理文档内容",type = "1") + @Log(module = "专项文档管理", value = "修改专项文档管理文档内容") @PostMapping("/updateFiles") @ApiOperation("修改专项文档管理文档内容") @PreAuthorize("@el.check('update:files')") @@ -110,7 +111,7 @@ public class FilesController { * 参数说明 id 文档内容ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "根据ID删除专项文档管理文档内容",type = "1") + @Log(module = "专项文档管理", value = "根据ID删除专项文档管理文档内容") @PostMapping("/deleteFilesById") @ApiOperation("根据ID删除专项文档管理文档内容") @PreAuthorize("@el.check('del:systemdevice')") @@ -128,7 +129,7 @@ public class FilesController { * 参数说明 ids 文档内容id数组 * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 ***********************************/ - @Log(module = "专项文档管理", value = "批量删除专项文档管理文档内容",type = "1") + @Log(module = "专项文档管理", value = "批量删除专项文档管理文档内容") @PostMapping("/deleteFilesByIds") @ApiOperation("批量删除专项文档管理文档内容") @PreAuthorize("@el.check('del:systemdevice')") diff --git a/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/NodesController.java b/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/NodesController.java index 04d31b9..cdb8a77 100644 --- a/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/NodesController.java +++ b/java/src/main/java/com/yfd/platform/modules/specialDocument/controller/NodesController.java @@ -39,6 +39,7 @@ public class NodesController { * projectId 所属项目ID * 返回值说明: 专项文档节点树形结构 ***********************************/ + @Log(module = "专项文档管理", value = "获取专项文档节点树形结构") @PostMapping("/getNodesTree") @ApiOperation("获取专项文档节点树形结构") @ResponseBody @@ -53,7 +54,7 @@ public class NodesController { * 参数说明 nodes 专项文档节点信息 * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "增加专项文档节点",type = "1") + @Log(module = "专项文档管理", value = "增加专项文档节点") @PostMapping("/addNodes") @ApiOperation("增加专项文档节点") @PreAuthorize("@el.check('add:nodes')") @@ -70,7 +71,7 @@ public class NodesController { * 参数说明 nodes 专项文档节点信息 * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "修改专项文档节点",type = "1") + @Log(module = "专项文档管理", value = "修改专项文档节点") @PostMapping("/updateNodes") @ApiOperation("修改专项文档节点") @PreAuthorize("@el.check('update:nodes')") @@ -87,7 +88,7 @@ public class NodesController { * 参数说明 id 专项文档节点ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ - @Log(module = "专项文档管理", value = "根据ID删除专项文档节点",type = "1") + @Log(module = "专项文档管理", value = "根据ID删除专项文档节点") @PostMapping("/deleteNodesById") @ApiOperation("根据ID删除专项文档节点") @PreAuthorize("@el.check('del:nodes')") diff --git a/java/src/main/java/com/yfd/platform/modules/specialDocument/service/impl/NodesServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/specialDocument/service/impl/NodesServiceImpl.java index a4a2085..e7456ad 100644 --- a/java/src/main/java/com/yfd/platform/modules/specialDocument/service/impl/NodesServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/specialDocument/service/impl/NodesServiceImpl.java @@ -360,9 +360,14 @@ public class NodesServiceImpl extends ServiceImpl implements return ResponseResult.error("节点名称已存在!"); } int valueUpdate = nodesMapper.updateById(nodes); + + // 4. 递归获取所有子节点ID(包含当前节点) + List affectedNodeIds = getAllChildNodeIds(nodes.getId()); + + // 5. 更新相关文件路径 + updateFilePaths(affectedNodeIds, nodes.getProjectId(), nodeNameOld, nodeName); + if (valueUpdate == 1) { - - List pathNodes = new ArrayList<>(); Nodes nodesData = nodesMapper.selectById(nodes.getParentId()); // 从当前节点向上遍历,直到根节点 @@ -400,43 +405,85 @@ public class NodesServiceImpl extends ServiceImpl implements } } + /** + * 更新文件路径中的节点名称 + */ + private void updateFilePaths(List nodeIds, String projectId, String oldName, String newName) { + // 查询需要更新的文件记录 + List filesToUpdate = filesMapper.selectList( + new QueryWrapper() + .in("node_id", nodeIds) + .eq("project_id", projectId) + ); + + // 构建路径替换规则 + String pathPattern = "/" + oldName; // 正确转义斜杠 + String replacement = "/" + newName; // 保留斜杠或结尾不变 + + // 批量更新文件路径 + filesToUpdate.forEach(file -> { + String newPath = file.getFilePath().replace(pathPattern, replacement); + file.setFilePath(newPath); + }); + // 批量更新数据库 + if (!filesToUpdate.isEmpty()) { + for (Files files : filesToUpdate) { + filesMapper.updateById(files); + } + } + } + + + /** + * 递归获取所有子节点ID(包含当前节点) + */ + private List getAllChildNodeIds(String nodeId) { + List nodeIds = new ArrayList<>(); + nodeIds.add(nodeId); + + // 查询直接子节点 + List directChildren = nodesMapper.selectList( + new QueryWrapper().eq("parent_id", nodeId)); + + // 递归获取子节点的子节点 + for (Nodes child : directChildren) { + nodeIds.addAll(getAllChildNodeIds(child.getId())); + } + + return nodeIds; + } + + /********************************** * 用途说明: 根据ID删除专项文档节点 * 参数说明 id 专项文档节点ID * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 ***********************************/ @Override + @Transactional(rollbackFor = Exception.class) // 启用事务 public boolean deleteNodesById(String id) { Boolean value = false; - //根据ID 查询当前数据 + // 根据ID 查询当前数据 Nodes nodes = nodesMapper.selectById(id); - // 删除当前节点 - int deleteCount = nodesMapper.deleteById(id); - //删除当前节点的 文件 - QueryWrapper queryWrapper1 = new QueryWrapper<>(); - queryWrapper1.eq("node_id", nodes.getId()); - queryWrapper1.eq("task_id", nodes.getProjectId()); - filesMapper.delete(queryWrapper1); - + if (nodes == null) { + return false; // 节点不存在 + } + // 构建节点路径 List pathNodes = new ArrayList<>(); Nodes nodesData = nodesMapper.selectById(nodes.getParentId()); - // 从当前节点向上遍历,直到根节点 while (nodesData != null) { pathNodes.add(nodesData.getNodeName()); - // 如果父节点是 "00",说明已经到了根节点,停止遍历 if ("00".equals(nodesData.getParentId())) { break; } - // 获取父节点 - nodesData = nodesMapper.selectById(nodesData.getParentId()); // 修正:从 nodesData 中获取 parentId + nodesData = nodesMapper.selectById(nodesData.getParentId()); } - // 反转路径,使其从根节点到当前节点 Collections.reverse(pathNodes); String path = String.join("/", pathNodes); - //删除minio准备数据 + // 删除 MinIO 中的文件夹 List deleteItemList = new ArrayList<>(); BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem(); deleteItemData.setName(nodes.getNodeName()); @@ -449,9 +496,9 @@ public class NodesServiceImpl extends ServiceImpl implements batchDeleteRequest.setDeleteItems(deleteItemList); batchDeleteRequest.setStorageKey("minio"); AbstractBaseFileService fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey()); - List deleteItems = batchDeleteRequest.getDeleteItems(); - int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItems); - for (BatchDeleteRequest.DeleteItem deleteItem : deleteItems) { + + int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList); + for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) { boolean flag = false; try { if (deleteItem.getType() == FileTypeEnum.FILE) { @@ -470,34 +517,33 @@ public class NodesServiceImpl extends ServiceImpl implements deleteFailCount++; } } - if (totalCount > 1) { - //return ResponseResult.success("批量删除 " + totalCount + " 个, 删除成功 " + deleteSuccessCount + " 个, 失败 " + deleteFailCount + " 个."); - LOGGER.error("批量删除 " + totalCount + " 个, 删除成功 " + deleteSuccessCount + " 个, 失败 " + deleteFailCount + " 个."); - } else { - //return totalCount == deleteSuccessCount ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); - LOGGER.error("批量删除 " + totalCount + " 个, 删除成功 " + deleteSuccessCount + " 个, 失败 " + deleteFailCount + " 个."); - } - //如果是1 说明成功删除 if (deleteSuccessCount >= 1) { // 递归删除子节点 deleteChildren(nodes.getId(), nodes.getProjectId()); - value = true; + // 删除当前节点的文件 + filesMapper.delete( + new QueryWrapper() + .eq("node_id", nodes.getId()) + .eq("project_id", nodes.getProjectId()) + ); + + // 删除当前节点 + int deleteCount = nodesMapper.deleteById(id); + value = deleteCount > 0; } else { value = false; } + return value; } /** * 递归删除子节点 - * - * @param parentId 父节点ID */ private void deleteChildren(String parentId, String projectId) { - - // 使用 QueryWrapper 查询当前节点的所有子节点 + // 查询当前节点的所有子节点 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("parent_id", parentId); queryWrapper.eq("project_id", projectId); @@ -505,14 +551,19 @@ public class NodesServiceImpl extends ServiceImpl implements // 递归删除每个子节点 for (Nodes child : children) { - deleteChildren(child.getId(), child.getProjectId()); // 递归删除子节点的子节点 - nodesMapper.deleteById(child.getId()); // 删除当前子节点 - //批量文件的数据 + // 删除子节点下的文件 QueryWrapper queryWrapper1 = new QueryWrapper<>(); - queryWrapper1.eq("id", parentId); + queryWrapper1.eq("node_id", child.getId()); // 使用子节点的 ID queryWrapper1.eq("project_id", projectId); filesMapper.delete(queryWrapper1); + + // 递归删除子节点的子节点 + deleteChildren(child.getId(), projectId); + + // 删除当前子节点 + nodesMapper.deleteById(child.getId()); } + } // // // @@ -526,7 +577,6 @@ public class NodesServiceImpl extends ServiceImpl implements // deleteChildren(child.getId()); // 递归删除子节点的子节点 // nodesMapper.deleteById(child.getId()); // 删除当前子节点 // } - } } \ No newline at end of file diff --git a/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java b/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java index b5f223f..058e48e 100644 --- a/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java +++ b/java/src/main/java/com/yfd/platform/modules/storage/model/result/FileItemResult.java @@ -41,6 +41,7 @@ public class FileItemResult implements Serializable { //用于对比Md5文件 private String locatMd5; private String minioMd5; + private String id; /** * 获取路径和名称的组合, 并移除重复的路径分隔符 /. diff --git a/java/src/main/java/com/yfd/platform/modules/storage/service/base/AbstractS3BaseFileService.java b/java/src/main/java/com/yfd/platform/modules/storage/service/base/AbstractS3BaseFileService.java index d63bfab..239d7b2 100644 --- a/java/src/main/java/com/yfd/platform/modules/storage/service/base/AbstractS3BaseFileService.java +++ b/java/src/main/java/com/yfd/platform/modules/storage/service/base/AbstractS3BaseFileService.java @@ -55,9 +55,13 @@ public abstract class AbstractS3BaseFileService

extends A return s3FileList(folderPath); } +// @Override +// public List fileLists(String folderPath) { +// return s3FileLists(folderPath); +// } @Override - public List fileLists(String folderPath) { - return s3FileLists(folderPath); + public List fileListData(String path, String name ) { + return s3FileListData(path,name); } /** * 默认 S3 获取对象下载链接的方法, 如果指定了域名, 则替换为自定义域名. @@ -157,87 +161,190 @@ public abstract class AbstractS3BaseFileService

extends A return fileItemList; } - public List s3FileLists(String path) { + public List s3FileListData(String path, String name) { + List fileItemList = new ArrayList<>(); String bucketName = param.getBucketName(); - path = StringUtils.trimStartSlashes(path); // 去掉路径开头的斜杠 + if(path == null || StringUtils.isEmpty(path)){ + return fileItemList; + } + path = ensurePathWithSlash(path); // 确保路径以斜杠开头 String fullPath = StringUtils.trimStartSlashes(StringUtils.concat(param.getBasePath(), path, ZFileConstant.PATH_SEPARATOR)); - List fileItemList = new ArrayList<>(); - // 调用递归方法获取文件列表 - listFilesInDirectory(bucketName, fullPath, path, fileItemList); + + // 新增 includeAll 参数控制是否包含所有子项 + listFilesInDirectory(bucketName, fullPath, path, name, fileItemList, false); return fileItemList; } - private void listFilesInDirectory(String bucketName, String fullPath, String path, List fileItemList) { + private void listFilesInDirectory( + String bucketName, + String fullPath, + String path, + String name, + List fileItemList, + boolean includeAll // 新增参数:是否强制包含所有内容 + ) { ListObjectsRequest listObjectsRequest = new ListObjectsRequest() .withBucketName(bucketName) - .withPrefix(fullPath) // 设置前缀为当前路径 - .withMaxKeys(1000) // 每次最多返回 1000 个对象 - .withDelimiter("/"); // 使用 "/" 作为分隔符 + .withPrefix(fullPath) + .withMaxKeys(1000) + .withDelimiter("/"); ObjectListing objectListing = s3Client.listObjects(listObjectsRequest); boolean isFirstWhile = true; do { if (!isFirstWhile) { - objectListing = s3Client.listNextBatchOfObjects(objectListing); // 处理分页 + objectListing = s3Client.listNextBatchOfObjects(objectListing); } // 处理文件 for (S3ObjectSummary s : objectListing.getObjectSummaries()) { - FileItemResult fileItemResult = new FileItemResult(); + if (s.getKey().equals(fullPath)) continue; - // 跳过当前目录本身 - if (s.getKey().equals(fullPath)) { - continue; - } - - // 获取文件名并去除前导斜杠 String fileName = s.getKey().substring(fullPath.length()); if (fileName.startsWith(ZFileConstant.PATH_SEPARATOR)) { - fileName = fileName.substring(1); // 去掉开头的斜杠 + fileName = fileName.substring(1); } - fileItemResult.setName(fileName); - fileItemResult.setSize(s.getSize()); - fileItemResult.setTime(s.getLastModified()); - fileItemResult.setType(FileTypeEnum.FILE); - fileItemResult.setPath(path); // 当前路径 - - // 构造完整路径并生成下载 URL - String fullPathAndName = StringUtils.concat(path, fileItemResult.getName()); - fileItemResult.setUrl(getDownloadUrl(fullPathAndName)); - - fileItemList.add(fileItemResult); + // 包含条件:强制包含 或 名称匹配 + if (includeAll || StrUtil.isEmpty(name) || fileName.equals(name)) { + FileItemResult item = new FileItemResult(); + item.setName(fileName); + item.setSize(s.getSize()); + item.setTime(s.getLastModified()); + item.setType(FileTypeEnum.FILE); + item.setPath(path); // 路径保持不变 + item.setUrl(getDownloadUrl(ensurePathWithSlash(path) + fileName)); // 确保路径以斜杠开头 + fileItemList.add(item); + } } // 处理文件夹 for (String commonPrefix : objectListing.getCommonPrefixes()) { - FileItemResult fileItemResult = new FileItemResult(); - - // 获取文件夹名称,去掉前导路径并修正末尾斜杠 String folderName = commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1); - if (StrUtil.isEmpty(folderName) || StrUtil.equals(folderName, StringUtils.DELIMITER_STR)) { - continue; // 跳过无效的文件夹名称 + if (StrUtil.isEmpty(folderName) || folderName.equals(StringUtils.DELIMITER_STR)) continue; + + // 判断是否匹配名称或需要强制包含 + boolean matchFolder = includeAll || StrUtil.isEmpty(name) || folderName.equals(name); + + if (matchFolder) { + FileItemResult folderItem = new FileItemResult(); + folderItem.setName(folderName); + folderItem.setType(FileTypeEnum.FOLDER); + folderItem.setPath(path); // 路径保持不变 + fileItemList.add(folderItem); } - fileItemResult.setName(folderName); - fileItemResult.setType(FileTypeEnum.FOLDER); - fileItemResult.setPath(path); // 当前路径 - fileItemList.add(fileItemResult); - - // 递归处理子文件夹 - String subFolderPath = path + folderName + ZFileConstant.PATH_SEPARATOR; // 修正路径拼接 - String subFolderFullPath = commonPrefix; // 子文件夹的完整路径 - listFilesInDirectory(bucketName, subFolderFullPath, subFolderPath, fileItemList); + // 递归处理子文件夹(如果匹配则强制包含子项) + String subPath = ensurePathWithSlash(path) + folderName + ZFileConstant.PATH_SEPARATOR; // 确保路径以斜杠开头并正确拼接 + String subFullPath = commonPrefix; + listFilesInDirectory( + bucketName, + subFullPath, + subPath, + name, + fileItemList, + matchFolder // 关键修改:如果父文件夹匹配,则强制包含所有子项 + ); } isFirstWhile = false; - } while (objectListing.isTruncated()); // 处理分页 + } while (objectListing.isTruncated()); } + // 确保路径以斜杠开头 + private String ensurePathWithSlash(String path) { + if (path.startsWith(ZFileConstant.PATH_SEPARATOR)) { + return path; + } else { + return ZFileConstant.PATH_SEPARATOR + path; + } + } + //这个方法有用 获取指定路径下的所有的文件以及文件夹 +// public List s3FileLists(String path) { +// String bucketName = param.getBucketName(); +// path = StringUtils.trimStartSlashes(path); // 去掉路径开头的斜杠 +// String fullPath = StringUtils.trimStartSlashes(StringUtils.concat(param.getBasePath(), path, ZFileConstant.PATH_SEPARATOR)); +// +// List fileItemList = new ArrayList<>(); +// +// // 调用递归方法获取文件列表 +// listFilesInDirectory(bucketName, fullPath, path, fileItemList); +// +// return fileItemList; +// } +// +// private void listFilesInDirectory(String bucketName, String fullPath, String path, List fileItemList) { +// ListObjectsRequest listObjectsRequest = new ListObjectsRequest() +// .withBucketName(bucketName) +// .withPrefix(fullPath) // 设置前缀为当前路径 +// .withMaxKeys(1000) // 每次最多返回 1000 个对象 +// .withDelimiter("/"); // 使用 "/" 作为分隔符 +// +// ObjectListing objectListing = s3Client.listObjects(listObjectsRequest); +// boolean isFirstWhile = true; +// +// do { +// if (!isFirstWhile) { +// objectListing = s3Client.listNextBatchOfObjects(objectListing); // 处理分页 +// } +// +// // 处理文件 +// for (S3ObjectSummary s : objectListing.getObjectSummaries()) { +// FileItemResult fileItemResult = new FileItemResult(); +// +// // 跳过当前目录本身 +// if (s.getKey().equals(fullPath)) { +// continue; +// } +// +// // 获取文件名并去除前导斜杠 +// String fileName = s.getKey().substring(fullPath.length()); +// if (fileName.startsWith(ZFileConstant.PATH_SEPARATOR)) { +// fileName = fileName.substring(1); // 去掉开头的斜杠 +// } +// +// fileItemResult.setName(fileName); +// fileItemResult.setSize(s.getSize()); +// fileItemResult.setTime(s.getLastModified()); +// fileItemResult.setType(FileTypeEnum.FILE); +// fileItemResult.setPath(path); // 当前路径 +// +// // 构造完整路径并生成下载 URL +// String fullPathAndName = StringUtils.concat(path, fileItemResult.getName()); +// fileItemResult.setUrl(getDownloadUrl(fullPathAndName)); +// +// fileItemList.add(fileItemResult); +// } +// +// // 处理文件夹 +// for (String commonPrefix : objectListing.getCommonPrefixes()) { +// FileItemResult fileItemResult = new FileItemResult(); +// +// // 获取文件夹名称,去掉前导路径并修正末尾斜杠 +// String folderName = commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1); +// if (StrUtil.isEmpty(folderName) || StrUtil.equals(folderName, StringUtils.DELIMITER_STR)) { +// continue; // 跳过无效的文件夹名称 +// } +// +// fileItemResult.setName(folderName); +// fileItemResult.setType(FileTypeEnum.FOLDER); +// fileItemResult.setPath(path); // 当前路径 +// fileItemList.add(fileItemResult); +// +// // 递归处理子文件夹 +// String subFolderPath = path + folderName + ZFileConstant.PATH_SEPARATOR; // 修正路径拼接 +// String subFolderFullPath = commonPrefix; // 子文件夹的完整路径 +// listFilesInDirectory(bucketName, subFolderFullPath, subFolderPath, fileItemList); +// } +// +// isFirstWhile = false; +// } while (objectListing.isTruncated()); // 处理分页 +// } + @Override public FileItemResult getFileItem(String pathAndName) { diff --git a/java/src/main/java/com/yfd/platform/modules/storage/service/base/BaseFileService.java b/java/src/main/java/com/yfd/platform/modules/storage/service/base/BaseFileService.java index 446309c..eb5a887 100644 --- a/java/src/main/java/com/yfd/platform/modules/storage/service/base/BaseFileService.java +++ b/java/src/main/java/com/yfd/platform/modules/storage/service/base/BaseFileService.java @@ -262,15 +262,30 @@ public interface BaseFileService { */ S3Object getObject(String bucketName, String key); - /*** +// /*** +// * 获取指定路径下的文件及文件夹 +// * +// * @param folderPath +// * 文件夹路径 +// * +// * @return 文件及文件夹列表 +// * +// * @throws Exception 获取文件列表中出现的异常 / +// */ +// List fileLists(String folderPath) throws Exception; + + /*** * 获取指定路径下的文件及文件夹 * - * @param folderPath - * 文件夹路径 + * @param path + * 文件或者文件夹路径 + * @param name + * 文件或者文件夹名称 * * @return 文件及文件夹列表 * - * @throws Exception 获取文件列表中出现的异常 + * @throws Exception 获取文件列表中出现的异常 / */ - List fileLists(String folderPath) throws Exception; + List fileListData(String path, String name) throws Exception; + } diff --git a/java/src/main/java/com/yfd/platform/modules/storage/service/impl/LocalServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/storage/service/impl/LocalServiceImpl.java index a670e9f..95a27d4 100644 --- a/java/src/main/java/com/yfd/platform/modules/storage/service/impl/LocalServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/storage/service/impl/LocalServiceImpl.java @@ -29,6 +29,7 @@ import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -57,8 +58,6 @@ public class LocalServiceImpl extends AbstractProxyTransferService { } - - @Override public List fileList(String folderPath) throws FileNotFoundException { checkPathSecurity(folderPath); @@ -178,85 +177,164 @@ public class LocalServiceImpl extends AbstractProxyTransferService { public ObjectMetadata getObjectMetadata(String name, String path) { return null; } + @Override public S3Object getObject(String bucketName, String key) { return null; } + @Override - public List fileLists(String folderPath) throws Exception { + public List fileListData(String folderPath, String name) throws Exception { checkPathSecurity(folderPath); - List fileItemList = new ArrayList<>(); + List resultList = new ArrayList<>(); + String basePath = param.getFilePath(); - String fullPath = StringUtils.concat(param.getFilePath() + folderPath); + // 处理根目录特殊情况 + String fullPath = folderPath.equals("/") + ? Paths.get(basePath).toString() + : Paths.get(basePath, folderPath).toString(); - File file = new File(fullPath); + File targetDir = new File(fullPath); - if (!file.exists()) { - throw new FileNotFoundException("文件不存在"); + if (!targetDir.exists()) { + throw new FileNotFoundException("路径不存在: " + fullPath); } - // 调用递归方法处理文件夹及其内容(跳过第一个文件夹) - listFilesInDirectory(file, folderPath, fileItemList, true); + // 不添加目标文件夹本身到结果列表(与示例数据结构一致) + // resultList.add(convertToFileItem(targetDir, folderPath)); - return fileItemList; + // 列出目标目录内容 + if (targetDir.isDirectory()) { + String effectiveParentPath = folderPath.endsWith("/") + ? folderPath + : folderPath + "/"; + listFilesInDirectory(targetDir, effectiveParentPath, resultList); + } + + return resultList; } - private void listFilesInDirectory(File file, String folderPath, List fileItemList, boolean skipFirstFolder) throws IOException { - // 跳过第一个文件夹(folderPath),从它下面的内容开始处理 - if (skipFirstFolder) { - // 如果当前文件是 folderPath, 直接跳过,开始处理它的子文件夹 - if (file.isDirectory()) { - File[] files = file.listFiles(); - if (files != null) { - for (File f : files) { - // 递归进入下一级文件夹,路径应该是 folderPath + 当前文件夹名 - listFilesInDirectory(f, folderPath, fileItemList, false); - } - } - } - } else { - // 处理当前的文件夹或文件 - if (file.isDirectory()) { - // 对于文件夹,路径是 folderPath - fileItemList.add(fileToFileItems(file, folderPath)); + /** + * 递归列出文件夹下的所有内容 + */ + private void listFilesInDirectory(File directory, String parentPath, List resultList) { + File[] files = directory.listFiles(); + if (files == null) return; - // 递归处理当前文件夹内部的文件和文件夹 - File[] files = file.listFiles(); - if (files != null) { - for (File f : files) { - // 递归进入下一级文件夹,路径是 folderPath + 当前文件夹名 - String newPath = folderPath + file.getName(); - listFilesInDirectory(f, newPath, fileItemList, false); - } - } - } else { - // 对于文件,路径是 folderPath - fileItemList.add(fileToFileItems(file, folderPath)); + for (File file : files) { + // 创建文件项:父路径不带文件名 + FileItemResult item = convertToFileItem(file, parentPath); + resultList.add(item); + + // 递归处理子目录 + if (file.isDirectory()) { + String newParentPath = parentPath + file.getName() + "/"; + listFilesInDirectory(file, newParentPath, resultList); } } } - //lilin增加 - private FileItemResult fileToFileItems(File file, String folderPath) { - FileItemResult result = new FileItemResult(); - result.setName(file.getName()); - result.setPath(folderPath); // 这里返回的是父目录路径,不包含文件名 - // 使用 FileTypeEnum 设置文件类型 - if (file.isDirectory()) { - result.setType(FileTypeEnum.FOLDER); - } else { - result.setType(FileTypeEnum.FILE); - } - - result.setSize(file.length()); - // 设置文件的修改时间为 Date 类型 - result.setTime(new Date(file.lastModified())); - - return result; + /** + * 转换文件对象为结果对象 + */ + private FileItemResult convertToFileItem(File file, String parentPath) { + FileItemResult item = new FileItemResult(); + item.setName(file.getName()); + item.setPath(formatPath(parentPath)); // 父路径不带当前文件名 + item.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE); + item.setSize(file.isDirectory() ? 0 : file.length()); + item.setTime(new Date(file.lastModified())); + return item; } + /** + * 统一格式化路径 + */ + private String formatPath(String path) { + // 保证路径以/开头且不以/结尾(示例数据结构风格) + path = path.replaceAll("/+", "/"); + if (!path.startsWith("/")) path = "/" + path; + if (path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length()-1); + return path; + } + + //以下是通过路径获取所有的文件以及文件夹 +// @Override +// public List fileLists(String folderPath) throws Exception { +// checkPathSecurity(folderPath); +// +// List fileItemList = new ArrayList<>(); +// +// String fullPath = StringUtils.concat(param.getFilePath() + folderPath); +// +// File file = new File(fullPath); +// +// if (!file.exists()) { +// throw new FileNotFoundException("文件不存在"); +// } +// +// // 调用递归方法处理文件夹及其内容(跳过第一个文件夹) +// listFilesInDirectory(file, folderPath, fileItemList, true); +// +// return fileItemList; +// } + +// private void listFilesInDirectory(File file, String folderPath, List fileItemList, boolean skipFirstFolder) throws IOException { +// // 跳过第一个文件夹(folderPath),从它下面的内容开始处理 +// if (skipFirstFolder) { +// // 如果当前文件是 folderPath, 直接跳过,开始处理它的子文件夹 +// if (file.isDirectory()) { +// File[] files = file.listFiles(); +// if (files != null) { +// for (File f : files) { +// // 递归进入下一级文件夹,路径应该是 folderPath + 当前文件夹名 +// listFilesInDirectory(f, folderPath, fileItemList, false); +// } +// } +// } +// } else { +// // 处理当前的文件夹或文件 +// if (file.isDirectory()) { +// // 对于文件夹,路径是 folderPath +// fileItemList.add(fileToFileItems(file, folderPath)); +// +// // 递归处理当前文件夹内部的文件和文件夹 +// File[] files = file.listFiles(); +// if (files != null) { +// for (File f : files) { +// // 递归进入下一级文件夹,路径是 folderPath + 当前文件夹名 +// String newPath = folderPath + file.getName(); +// listFilesInDirectory(f, newPath, fileItemList, false); +// } +// } +// } else { +// // 对于文件,路径是 folderPath +// fileItemList.add(fileToFileItems(file, folderPath)); +// } +// } +// } +// //lilin增加 +// private FileItemResult fileToFileItems(File file, String folderPath) { +// FileItemResult result = new FileItemResult(); +// result.setName(file.getName()); +// result.setPath(folderPath); // 这里返回的是父目录路径,不包含文件名 +// +// // 使用 FileTypeEnum 设置文件类型 +// if (file.isDirectory()) { +// result.setType(FileTypeEnum.FOLDER); +// } else { +// result.setType(FileTypeEnum.FILE); +// } +// +// result.setSize(file.length()); +// // 设置文件的修改时间为 Date 类型 +// result.setTime(new Date(file.lastModified())); +// +// return result; +// } + @Override public void uploadFile(String pathAndName, InputStream inputStream) { @@ -333,12 +411,10 @@ public class LocalServiceImpl extends AbstractProxyTransferService { /** * 检查路径合法性: - * - 只有以 . 开头的允许通过,其他的如 ./ ../ 的都是非法获取上层文件夹内容的路径. + * - 只有以 . 开头的允许通过,其他的如 ./ ../ 的都是非法获取上层文件夹内容的路径. * - * @param paths - * 文件路径 - * - * @throws IllegalArgumentException 文件路径包含非法字符时会抛出此异常 + * @param paths 文件路径 + * @throws IllegalArgumentException 文件路径包含非法字符时会抛出此异常 */ private static void checkPathSecurity(String... paths) { for (String path : paths) { @@ -352,12 +428,10 @@ public class LocalServiceImpl extends AbstractProxyTransferService { /** * 检查路径合法性: - * - 不为空,且不包含 \ / 字符 + * - 不为空,且不包含 \ / 字符 * - * @param names - * 文件路径 - * - * @throws IllegalArgumentException 文件名包含非法字符时会抛出此异常 + * @param names 文件路径 + * @throws IllegalArgumentException 文件名包含非法字符时会抛出此异常 */ private static void checkNameSecurity(String... names) { for (String name : names) { diff --git a/java/src/main/java/com/yfd/platform/system/controller/UserController.java b/java/src/main/java/com/yfd/platform/system/controller/UserController.java index 495a0ea..6fc8619 100644 --- a/java/src/main/java/com/yfd/platform/system/controller/UserController.java +++ b/java/src/main/java/com/yfd/platform/system/controller/UserController.java @@ -66,10 +66,10 @@ public class UserController { @ApiOperation("查询用户信息") @ResponseBody public ResponseResult queryUsers(String orgid, - String username, String institutionId, Page page) { + String nickname, Page page) { Page> mapPage = userService.queryUsers(orgid, - username, institutionId, page); + nickname, page); return ResponseResult.successData(mapPage); } diff --git a/java/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java b/java/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java index e55a9ae..99fa066 100644 --- a/java/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java +++ b/java/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java @@ -90,7 +90,7 @@ public interface SysUserMapper extends BaseMapper { boolean delInRoleUsersByUserid(@Param("userid") String userid,@Param("roleids")String[] roleids); Page> queryUsers(String orgid, - String username,String institutionId, + String nickname, Page page); Map getOrganizationByid(String id); diff --git a/java/src/main/java/com/yfd/platform/system/service/IUserService.java b/java/src/main/java/com/yfd/platform/system/service/IUserService.java index 923960d..002edd0 100644 --- a/java/src/main/java/com/yfd/platform/system/service/IUserService.java +++ b/java/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -128,7 +128,7 @@ public interface IUserService extends IService { boolean addUserRoles(String roleid, String userid); //Page queryUsers(String orgid, String username, Page page); - Page> queryUsers(String orgid, String username,String institutionId, Page page); + Page> queryUsers(String orgid, String nickname, Page page); /*********************************** * 用途说明:根据ID批量删除用户 diff --git a/java/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/java/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java index b7f885d..0e391ee 100644 --- a/java/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -474,32 +474,28 @@ public class UserServiceImpl extends ServiceImpl impleme @Override public Page> queryUsers(String orgid, - String username, - String institutionId, - Page page) { - if(StrUtil.isBlank(institutionId)){ - institutionId=getUserInfo().getInstitutionId(); - } - Page> mapPage = sysUserMapper.queryUsers(orgid, - username, institutionId, page); - List> list = new ArrayList<>(); - List> records = mapPage.getRecords(); - -// String institutionId=getUserInfo().getInstitutionId(); - for (Map record : records) { - String id = (String) record.get("id"); - List sysRoles = sysRoleMapper.getRoleByUserId(id); - record.put("roles", sysRoles); - if(StrUtil.isNotEmpty(institutionId)){ - if(ObjUtil.isNotEmpty(record.get("institution_id"))&&record.get("institution_id").toString().equals(institutionId)){ - list.add(record); - } - }else{ + String nickname, + Page page) { + //增加权限控制(usertype:0-超级管理员 1-系统管理员 2-普通用户) + Page> mapPage = null; + SysUser currentUser=this.getUserInfo(); + int usertype=currentUser.getUsertype(); + if(usertype==0 ||usertype==1) { //管理员才能查询用户数据 + mapPage = sysUserMapper.queryUsers(orgid, + nickname, page); + List> list = new ArrayList<>(); + List> records = mapPage.getRecords(); + for (Map record : records) { + String id = (String) record.get("id"); + List sysRoles = sysRoleMapper.getRoleByUserId(id); + record.put("roles", sysRoles); list.add(record); } + mapPage.setRecords(list); + return mapPage; + }else{ + return mapPage; } - mapPage.setRecords(list); - return mapPage; } /*********************************** diff --git a/java/src/main/resources/mapper/system/SysUserMapper.xml b/java/src/main/resources/mapper/system/SysUserMapper.xml index e66128a..684695d 100644 --- a/java/src/main/resources/mapper/system/SysUserMapper.xml +++ b/java/src/main/resources/mapper/system/SysUserMapper.xml @@ -57,9 +57,6 @@ u.usertype, u.username, u.nickname, - u.institution_id, - institution.name as institution_name, - u.institution_type, u.sex, u.email, u.phone, @@ -70,19 +67,13 @@ u.lastmodifydate FROM sys_user u - LEFT JOIN sys_vision_institution institution ON institution.id = u.institution_id where 1=1 and u.usertype != 0 - + and u.orgid = #{orgid} - - - and u.nickname like concat('%', #{username},'%') - - - - and u.institution_id = #{institutionId} + + and u.nickname like concat('%',#{nickname},'%') ORDER BY u.lastmodifydate DESC