From 2b30e30fda68b2d148756bd08c0653e30917b21d Mon Sep 17 00:00:00 2001 From: lilin Date: Sat, 24 May 2025 11:43:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/NodesServiceImpl.java | 195 +++++++++++------- 1 file changed, 126 insertions(+), 69 deletions(-) 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 f5196ad..043bec6 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 @@ -61,6 +61,7 @@ import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; + /** *

* 专项文档节点表 服务实现类 @@ -114,7 +115,7 @@ public class NodesServiceImpl extends ServiceImpl implements ***********************************/ @Override public List> getNodesTree(String nodeName, String projectId) { - Project project = projectMapper.selectById(projectId); + Project project = projectMapper.selectById(projectId); // 查询所有节点数据 List> allNodes = getAllNodes(projectId); @@ -132,14 +133,14 @@ public class NodesServiceImpl extends ServiceImpl implements // 如果 nodeName 为空,返回所有根节点的完整树形结构 if (StringUtils.isEmpty(nodeName)) { for (Map rootNode : rootNodes) { - rootNode.put("path",basePath); + rootNode.put("path", basePath); result.addAll(buildFullTree(rootNode, allNodes)); } return result; } // 否则,返回从根节点到目标节点的树形结构 for (Map rootNode : rootNodes) { - rootNode.put("path",basePath); + rootNode.put("path", basePath); List> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName); if (!tree.isEmpty()) { result.addAll(tree); @@ -530,7 +531,7 @@ public class NodesServiceImpl extends ServiceImpl implements ***********************************/ @Override @Transactional(rollbackFor = Exception.class) // 启用事务 - public boolean deleteNodesById(String id,String path) { + public boolean deleteNodesById(String id, String path) { Boolean value = false; @@ -682,6 +683,10 @@ public class NodesServiceImpl extends ServiceImpl implements AbstractBaseFileService fileService = storageSourceContext.getByStorageId(storageId); //todo 首先获取两个集合 对比出数据库中没有的文件夹以及文件,递归增加 List fileItemList = fileService.fileList(fileListRequest.getPath()); + if (fileItemList.size() == 0) { + throw new Exception("该项目目录不存在或没有项目文档,请先建立项目目录和文档。"); + } + //获取数据库父节点为0的文件夹数据 通过所属项目ID和父节点查询 List nodesList = nodesMapper.selectList(new LambdaQueryWrapper().eq(Nodes::getParentId, "00").eq(Nodes::getProjectId, id)); @@ -718,38 +723,40 @@ public class NodesServiceImpl extends ServiceImpl implements // queryWrapper.eq(Nodes::getNodeOrder, obtainNodeType(index)); Nodes nodeData = nodesMapper.selectOne(queryWrapper); //如果没有 新增 并且递归 - if (nodeData == null){ + if (nodeData == null) { Nodes node = saveNodes(projectId, TOP_LEVEL_PARENT_NODE, item.getName(), obtainNodeType(index)); otherLevelsData(projectId, node.getId(), item.getName(), item.getPath(), index); - }else{ + } else { otherLevelsData(projectId, nodeData.getId(), item.getName(), item.getPath(), index); } } else { - //获取节点名称 - String nodeName = getLastPathSegment(item.getPath()); - //获取节点信息 主要用到ID - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("node_name", nodeName); - queryWrapper.eq("parent_id", TOP_LEVEL_PARENT_NODE); - Nodes node = nodesMapper.selectOne(queryWrapper); - - //新增之前先查询 - LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); - queryWrapper1.eq(Files::getProjectId, projectId); - queryWrapper1.eq(Files::getNodeId, node.getId()); - queryWrapper1.eq(Files::getFilePath, item.getPath()); - queryWrapper1.eq(Files::getFileName, item.getName()); - Files files = filesMapper.selectOne(queryWrapper1); - if (files == null){ - // 获取文件大小(字节) - long fileSizeInBytes = item.getSize(); - // 转换为 MB 并保留两位小数 - double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0); - String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数 - //保存文件信息 - saveFiles(projectId, node.getId(), item.getPath(), item.getName(), fileSizeFormatted); - } + //第一层属于节点,同级别的文件不做处理 + continue; +// //获取节点名称 +// String nodeName = getLastPathSegment(item.getPath()); +// //获取节点信息 主要用到ID +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("node_name", nodeName); +// queryWrapper.eq("parent_id", TOP_LEVEL_PARENT_NODE); +// Nodes node = nodesMapper.selectOne(queryWrapper); +// +// //新增之前先查询 +// LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); +// queryWrapper1.eq(Files::getProjectId, projectId); +// queryWrapper1.eq(Files::getNodeId, node.getId()); +// queryWrapper1.eq(Files::getFilePath, item.getPath()); +// queryWrapper1.eq(Files::getFileName, item.getName()); +// Files files = filesMapper.selectOne(queryWrapper1); +// if (files == null) { +// // 获取文件大小(字节) +// long fileSizeInBytes = item.getSize(); +// // 转换为 MB 并保留两位小数 +// double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0); +// String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数 +// //保存文件信息 +// saveFiles(projectId, node.getId(), item.getPath(), item.getName(), fileSizeFormatted); +// } } } } @@ -791,11 +798,11 @@ public class NodesServiceImpl extends ServiceImpl implements // queryWrapper.eq(Nodes::getNodeOrder, obtainNodeType(index)); Nodes nodeData = nodesMapper.selectOne(queryWrapper); //如果没有 新增 并且递归 - if (nodeData == null){ + if (nodeData == null) { //保存节点 Nodes node = saveNodes(projectId, parentId, item.getName(), obtainNodeType(index)); otherLevelsData(projectId, node.getId(), item.getName(), item.getPath(), index); - }else{ + } else { otherLevelsData(projectId, nodeData.getId(), item.getName(), item.getPath(), index); } } else { @@ -809,7 +816,7 @@ public class NodesServiceImpl extends ServiceImpl implements queryWrapper1.eq(Files::getFilePath, item.getPath()); queryWrapper1.eq(Files::getFileName, item.getName()); Files files = filesMapper.selectOne(queryWrapper1); - if (files == null){ + if (files == null) { // 获取文件大小(字节) long fileSizeInBytes = item.getSize(); // 转换为 MB 并保留两位小数 @@ -884,9 +891,13 @@ public class NodesServiceImpl extends ServiceImpl implements */ private void saveFiles(String projectId, String nodeId, String filePath, String fileName, String fileSize) { //获取当前登录用户 上传人是当前登录人 -// UsernamePasswordAuthenticationToken authentication = -// (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); -// LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = null; + if (authentication != null) { + loginuser = (LoginUser) authentication.getPrincipal(); + } + // 设置当前时间 LocalDateTime now = LocalDateTime.now(); // 转换为 Timestamp @@ -897,8 +908,11 @@ public class NodesServiceImpl extends ServiceImpl implements files.setNodeId(nodeId); files.setFilePath(filePath); files.setUploadTime(currentTime); - files.setUploader("admin"); - //files.setUploader(loginuser.getUsername()); + if (loginuser == null) { + files.setUploader(null); + } else { + files.setUploader(loginuser.getUsername()); + } files.setFileName(fileName); files.setFileSize(fileSize); filesMapper.insert(files); @@ -941,15 +955,20 @@ public class NodesServiceImpl extends ServiceImpl implements //解压以后的文件夹名称 String zipName = getFileNameWithoutExtension(fileName); // 示例:222 + //查询项目信息 + Project project = projectMapper.selectById(id); + + if(!project.getProjectName().equals(zipName)){ + throw new RuntimeException("压缩包名称需要和项目名称保持一致"); + } + String decompressionPath = "/" + "temporary"; //构建要解压的zip文件路径 例如 D:\yun\temporary\111.zip Path zipFilePath = Paths.get(storageSourceConfig.getValue(), decompressionPath, fileName); - // 4. 构建解压目标路径 例如 D:\yun\temporary\111 Path destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath, zipName); - //查询项目信息 - Project project = projectMapper.selectById(id); + //todo 如果项目不存在 直接拷贝到目标路径下 拷贝完成以后 然后新增一个项目结构 然后走一遍扫描 //源文件夹路径 @@ -960,11 +979,15 @@ public class NodesServiceImpl extends ServiceImpl implements //首先执行解压缩 unzipToTemp(zipFilePath, String.valueOf(destRoot)); // 执行上传并且插入数据库 - this.documentUploadById(id,sourceFolderPath,targetFolderPath,storageSourceConfig.getValue()); + this.documentUploadById(id, sourceFolderPath, targetFolderPath, storageSourceConfig.getValue()); //开始执行更新表数据 名称是去掉后缀以后的 uploadProject(zipName); + //获取文件路径 + String path = storageSourceConfig.getValue() + decompressionPath; // 替换为实际路径 + File target = new File(path); + deleteDirectory(target); } catch (IOException e) { throw new RuntimeException(e); } catch (Exception e) { @@ -979,6 +1002,29 @@ public class NodesServiceImpl extends ServiceImpl implements //todo 首先得上传到一个临时目录 然后找到这个目录我去解压缩到当前文件夹 解压缩以后 然后开始循环 我通过第一层去查询 如果没有就新增 如果有就循环 看看需不需要覆盖 + public static void deleteDirectory(File directory) throws Exception { + if (!directory.exists()) { + return; // 路径不存在直接返回 + } + + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file); // 递归删除子目录 + } else { + if (!file.delete()) { // 删除文件 + throw new Exception("无法删除文件: " + file.getAbsolutePath()); + } + } + } + } + + if (!directory.delete()) { // 删除空目录 + throw new Exception("无法删除目录: " + directory.getAbsolutePath()); + } + } + /********************************** * 用途说明: 专项文档管理扫描接口实际接口 * 参数说明 id 所属项目ID @@ -986,12 +1032,12 @@ public class NodesServiceImpl extends ServiceImpl implements * 参数说明 zipName 解压以后的文件夹名称 * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 ***********************************/ - public String documentUploadById(String id, String sourceFolderPath, String targetFolderPath,String value) throws Exception { + public String documentUploadById(String id, String sourceFolderPath, String targetFolderPath, String value) throws Exception { //源目录 File sourceDir = new File(value + sourceFolderPath); //目标目录 - File targetDir = new File( value + targetFolderPath); + File targetDir = new File(value + targetFolderPath); if (!targetDir.exists()) { FileUtils.copyDirectory(sourceDir, targetDir); @@ -1021,11 +1067,11 @@ public class NodesServiceImpl extends ServiceImpl implements //如果存在 判断是文件夹还是文件 如果是文件夹 则继续递归 if (fileItemResult.getType() == FileTypeEnum.FOLDER) { try { - documentUploadById(id, fileItemResult.getPath() + "/" + fileItemResult.getName(), targetFileItem.getPath()+ "/" + targetFileItem.getName(),value); + documentUploadById(id, fileItemResult.getPath() + "/" + fileItemResult.getName(), targetFileItem.getPath() + "/" + targetFileItem.getName(), value); } catch (Exception e) { throw new RuntimeException(e); } - }else { + } else { if (fileItemResult.getType() == FileTypeEnum.FILE) { //源路径 String sourcePath = value + sourceFolderPath + "/" + fileItemResult.getName(); @@ -1048,24 +1094,24 @@ public class NodesServiceImpl extends ServiceImpl implements //更改目标路径下的文件名称 renameFile(targetPath); //将源目录文件 复制到 目标目录文件 - copyWithOverride(sourcePath,targetPath); + copyWithOverride(sourcePath, targetPath); - }else { + } else { //将源目录文件 复制到 目标目录文件 todo这个地方是覆盖 - copyWithOverride(sourcePath,targetPath); + copyWithOverride(sourcePath, targetPath); } } } - }else{ + } else { //如果为空对象不存在 则直接拷贝 String sourcePath = value + sourceFolderPath; //目标路径 就是源路径去掉/temporary的部分 - String targetPath = sourcePath.replace("/" + "temporary", ""); + String targetPath = sourcePath.replace("/" + "temporary", ""); //将源目录文件 复制到 目标目录文件 - copyFolderWithOverride(sourcePath,targetPath); + copyFolderWithOverride(sourcePath, targetPath); } } @@ -1082,7 +1128,7 @@ public class NodesServiceImpl extends ServiceImpl implements projectLambdaQueryWrapper.eq(Project::getProjectName, projectName); Project project = projectMapper.selectOne(projectLambdaQueryWrapper); //如果项目不存在 - if(project == null){ + if (project == null) { String projectCode = this.generateNextProjectCode(); project.setProjectCode("1"); @@ -1108,8 +1154,6 @@ public class NodesServiceImpl extends ServiceImpl implements } - - //获取文件列表 String absolutePath = "/" + project.getProjectName() + "/"; FileListRequest fileListRequest = buildFileRequest(absolutePath); @@ -1138,8 +1182,6 @@ public class NodesServiceImpl extends ServiceImpl implements } - - // 编号生成工具方法 @Transactional(rollbackFor = Exception.class) public synchronized String generateNextProjectCode() { @@ -1236,9 +1278,6 @@ public class NodesServiceImpl extends ServiceImpl implements } - - - //获取路径下的所有文件和文件夹 private List obtainFileItemResultData(FileListRequest fileListRequest) throws Exception { @@ -1336,9 +1375,18 @@ public class NodesServiceImpl extends ServiceImpl implements try (ZipInputStream zis = new ZipInputStream( java.nio.file.Files.newInputStream(sourcePath), StandardCharsets.UTF_8)) { - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { + ZipEntry entry = null; + + while (true) { // 循环结构改为无限循环 try { + entry = zis.getNextEntry(); // 尝试获取下一个条目 + if (entry == null) { + break; // 没有更多条目,退出循环 + } + + // 打印当前处理的条目名称 + LOGGER.info("Processing ZIP entry: {}", entry.getName()); + // 跳过 macOS 系统文件 if (entry.getName().startsWith("__MACOSX")) { continue; @@ -1347,7 +1395,7 @@ public class NodesServiceImpl extends ServiceImpl implements // 标准化路径并处理目录标识 String entryName = entry.getName() .replace("\\", "/") - .replaceFirst("^/+", ""); // 去除开头的斜杠 + .replaceFirst("^/+", ""); // 去除开头的斜杠 // 检测是否为目录(兼容以'/'结尾的条目) boolean isDirectory = entry.isDirectory() || entry.getName().endsWith("/"); @@ -1364,16 +1412,15 @@ public class NodesServiceImpl extends ServiceImpl implements } Path targetPath = destRoot.resolve(entryName).normalize(); - validatePathSafetya(targetPath, destRoot); + validatePathSafetya(targetPath, destRoot); // 确保路径安全 // 处理目录 if (isDirectory) { java.nio.file.Files.createDirectories(targetPath); - } - // 处理文件 - else { + } else { + // 处理文件 if (java.nio.file.Files.exists(targetPath)) { - LOGGER.warn("文件已存在,跳过覆盖: {}", targetPath); + LOGGER.warn("File already exists, skip overwriting: {}", targetPath); continue; } // 确保父目录存在 @@ -1382,8 +1429,17 @@ public class NodesServiceImpl extends ServiceImpl implements IOUtils.copy(zis, os); } } + + } catch (IllegalArgumentException e) { + // 捕获 MALFORMED 异常并记录条目名称 + String errorEntry = (entry != null) ? entry.getName() : "Unknown Entry"; + LOGGER.error("MALFORMED ZIP Entry Detected! Entry Name: {}", errorEntry, e); } finally { - zis.closeEntry(); // 确保每个条目只关闭一次 + + if (entry != null) { + zis.closeEntry(); // 关闭当前条目 + } + } } } @@ -1391,6 +1447,7 @@ public class NodesServiceImpl extends ServiceImpl implements return destRoot.toFile(); } + /** * 路径安全验证(增强版) */