From 072f043c009c4e1f6b707fae447246ebdd81a5df Mon Sep 17 00:00:00 2001 From: gulongcheng <474084054@qq.com> Date: Mon, 8 Dec 2025 16:29:47 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E6=88=90=E6=8A=A5=E5=91=8A=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExportWordScriptExecuteConfig.java | 2 +- .../impl/MinioFileIDataFileServiceImpl.java | 43 ++++--- .../handler/ExportWordScriptHandler.java | 120 ++++++++---------- 3 files changed, 77 insertions(+), 88 deletions(-) diff --git a/common/src/main/java/com/sdm/common/entity/flowable/executeConfig/ExportWordScriptExecuteConfig.java b/common/src/main/java/com/sdm/common/entity/flowable/executeConfig/ExportWordScriptExecuteConfig.java index af980793..0ea6bd72 100644 --- a/common/src/main/java/com/sdm/common/entity/flowable/executeConfig/ExportWordScriptExecuteConfig.java +++ b/common/src/main/java/com/sdm/common/entity/flowable/executeConfig/ExportWordScriptExecuteConfig.java @@ -10,5 +10,5 @@ public class ExportWordScriptExecuteConfig extends BaseExecuteConfig { private String fileRegularStr="^aa\\.xml$"; // 导出脚本文件id - private String exportScriptFileId; + private Long exportScriptFileId; } diff --git a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java index 0914d8cd..b7b70a60 100644 --- a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java @@ -2537,7 +2537,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { throw new RuntimeException("文件夹不存在,ID: " + downloadDirId); } - if(!Objects.equals(DataTypeEnum.DIRECTORY.getValue(),folderInfo.getFileType())){ + if (!Objects.equals(DataTypeEnum.DIRECTORY.getValue(), folderInfo.getFileType())) { throw new RuntimeException("指定 ID 不是文件夹类型: " + downloadDirId); } @@ -2551,19 +2551,12 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { folderObjectKey += "/"; } - // 2. 构建本地基础路径(直接使用 basePath + folderObjectKey) + // 2. 构建本地基础路径(basePath 就是最终根目录) Path localBaseDir = Paths.get(basePath).toAbsolutePath().normalize(); - Path fullLocalBase = localBaseDir.resolve(folderObjectKey).normalize(); - - // 安全校验:确保 fullLocalBase 确实在 basePath 下 - if (!fullLocalBase.startsWith(localBaseDir)) { - throw new RuntimeException("非法文件夹路径,可能包含路径穿越: " + folderObjectKey); - } - try { - Files.createDirectories(fullLocalBase); + Files.createDirectories(localBaseDir); // 确保 basePath 存在 } catch (Exception e) { - throw new RuntimeException("无法创建本地目录: " + fullLocalBase, e); + throw new RuntimeException("无法创建本地基础目录: " + localBaseDir, e); } // 3. 列出 MinIO 中该前缀下的所有对象(递归) @@ -2579,7 +2572,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } } - // 4. 遍历并下载每个对象(任一失败立即抛出 RuntimeException) + // 4. 遍历并下载每个对象 for (Result result : results) { Item item = result.get(); String objectKey = item.objectName(); @@ -2589,19 +2582,29 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { continue; } - // 如果提供了正则表达式,则进行过滤 + // 理论上 listObjects(folderObjectKey) 返回的都以 folderObjectKey 开头,但加个校验更安全 + if (!objectKey.startsWith(folderObjectKey)) { + continue; + } + + // 剥离文件夹前缀,得到相对路径 + String relativePath = objectKey.substring(folderObjectKey.length()); + if (relativePath.isEmpty()) { + continue; // 忽略空路径(不应发生) + } + + // 如果提供了正则表达式,则只匹配文件名(不是完整路径) if (pattern != null) { - String fileName = Paths.get(objectKey).getFileName().toString(); + String fileName = Paths.get(relativePath).getFileName().toString(); if (!pattern.matcher(fileName).matches()) { - // 不匹配正则表达式的文件跳过下载 - continue; + continue; // 跳过不匹配的文件 } } - // 构建本地文件路径:basePath + objectKey - Path localFilePath = localBaseDir.resolve(objectKey).normalize(); + // 构建本地文件路径:basePath + 相对路径 + Path localFilePath = localBaseDir.resolve(relativePath).normalize(); - // 二次安全校验:防止 objectKey 含 ../ 导致越界 + // 安全校验:防止路径穿越(例如 relativePath 含 ../../) if (!localFilePath.startsWith(localBaseDir)) { throw new RuntimeException("检测到非法对象路径,拒绝下载: " + objectKey); } @@ -2624,7 +2627,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { throw new RuntimeException("下载对象失败: " + objectKey, e); } } - + return SdmResponse.success(); } diff --git a/flowable/src/main/java/com/sdm/flowable/delegate/handler/ExportWordScriptHandler.java b/flowable/src/main/java/com/sdm/flowable/delegate/handler/ExportWordScriptHandler.java index 5745cb56..c8320110 100644 --- a/flowable/src/main/java/com/sdm/flowable/delegate/handler/ExportWordScriptHandler.java +++ b/flowable/src/main/java/com/sdm/flowable/delegate/handler/ExportWordScriptHandler.java @@ -14,6 +14,7 @@ import com.sdm.flowable.entity.ProcessNodeParam; import com.sdm.flowable.service.IProcessNodeParamService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.flowable.engine.delegate.DelegateExecution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -58,6 +59,8 @@ public class ExportWordScriptHandler implements ExecutionHandler result = new ArrayList<>(); + int runningStatus = -1; + try { + log.info("开始同步执行脚本"); + Process process = Runtime.getRuntime().exec(commands); + log.info("准备获取脚本输出"); + log.info("开始获取脚本输出"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + log.info("executePython:" + line); + result.add(line); + } + log.info("脚本执行完成"); + runningStatus = process.waitFor(); + log.info("脚本运行状态:" + runningStatus); + } catch (IOException | InterruptedException e) { + log.error("执行脚本失败:" + e); + return; + } + if (runningStatus != 0) { + log.error("执行脚本失败"); + return; + } else { + log.info(commands + "执行脚本完成!"); + } + try { + // 获取临时路径中脚本生成的报告 + uploadResultFileToMinio(basePath + "report.docx",currentNodeOutputDirId); + } catch (Exception ex) { + log.error("生成自动化报告失败:{}", ex.getMessage(), ex); + throw new RuntimeException("生成自动化报告失败"); } - List imageFileIdList = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(imageFileIdList)) { - String randomId = RandomUtil.generateString(16); - log.info("临时路径为:{}", randomId); - for (Long fileId : imageFileIdList) { - dataFeignClient.downloadFileToLocal(fileId, TEMP_REPORT_PATH + randomId); - } - // 调用脚本 - log.info("调用脚本中。。。。。。"); - String commands = "python /opt/script/exportWord.py " + TEMP_REPORT_PATH + randomId + File.separator; - log.info("command:" + commands); - List result = new ArrayList<>(); - int runningStatus = -1; - try { - log.info("开始同步执行脚本"); - Process process = Runtime.getRuntime().exec(commands); - log.info("准备获取脚本输出"); - log.info("开始获取脚本输出"); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - log.info("executePython:" + line); - result.add(line); - } - log.info("脚本执行完成"); - runningStatus = process.waitFor(); - log.info("脚本运行状态:" + runningStatus); - } catch (IOException | InterruptedException e) { - log.error("执行脚本失败:" + e); - return; - } - if (runningStatus != 0) { - log.error("执行脚本失败"); - return; - } else { - log.info(commands + "执行脚本完成!"); - } - try { - // 获取临时路径中脚本生成的报告 - uploadResultFileToMinio(TEMP_REPORT_PATH + randomId + File.separator + "report.docx"); - - } catch (Exception ex) { - log.error("生成自动化报告失败:{}", ex.getMessage(), ex); - throw new RuntimeException("生成自动化报告失败"); - } - // 删除临时路径 - log.info("删除临时路径:{},中。。。。。。", randomId); - deleteFolder(new File(TEMP_REPORT_PATH + randomId)); - } } catch (Exception e) { log.error("执行ExportWordScript失败", e); throw new RuntimeException("执行ExportWordScript失败: " + e.getMessage(), e); @@ -167,7 +165,7 @@ public class ExportWordScriptHandler implements ExecutionHandler