diff --git a/common/src/main/java/com/sdm/common/feign/impl/data/DataClientFeignClientImpl.java b/common/src/main/java/com/sdm/common/feign/impl/data/DataClientFeignClientImpl.java index 7973acef..d5172b3a 100644 --- a/common/src/main/java/com/sdm/common/feign/impl/data/DataClientFeignClientImpl.java +++ b/common/src/main/java/com/sdm/common/feign/impl/data/DataClientFeignClientImpl.java @@ -121,4 +121,13 @@ public class DataClientFeignClientImpl implements IDataFeignClient { } } + @Override + public void downloadFileToLocal(Long fileId,String path) { + try { + dataClient.downloadFileToLocal(fileId,path); + } catch (Exception e) { + log.error("下载文件响应", e); + } + } + } diff --git a/common/src/main/java/com/sdm/common/feign/inter/data/IDataFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/data/IDataFeignClient.java index 8ce2f339..bd668304 100644 --- a/common/src/main/java/com/sdm/common/feign/inter/data/IDataFeignClient.java +++ b/common/src/main/java/com/sdm/common/feign/inter/data/IDataFeignClient.java @@ -5,6 +5,7 @@ import com.sdm.common.entity.req.data.*; import com.sdm.common.entity.req.system.LaunchApproveReq; import com.sdm.common.entity.resp.data.FileMetadataInfoResp; import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; @@ -43,5 +44,9 @@ public interface IDataFeignClient { @GetMapping("/data/queryFileMetadataInfo") SdmResponse queryFileMetadataInfo(@RequestParam(value = "uuid") String uuid, @RequestParam(value = "uuidOwnType") String uuidOwnType); + @PostMapping("/data/downloadFileToLocal") + void downloadFileToLocal(@RequestParam(value = "fileId") @Validated Long fileId, @RequestParam(value = "path") @Validated String path); + + } diff --git a/common/src/main/java/com/sdm/common/utils/excel/ExcelUtil.java b/common/src/main/java/com/sdm/common/utils/excel/ExcelUtil.java index 10bfda74..22ab70a7 100644 --- a/common/src/main/java/com/sdm/common/utils/excel/ExcelUtil.java +++ b/common/src/main/java/com/sdm/common/utils/excel/ExcelUtil.java @@ -7,6 +7,7 @@ import com.alibaba.fastjson2.JSONObject; import com.sdm.common.entity.ExportExcelFormat; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFCellStyle; @@ -258,7 +259,7 @@ public class ExcelUtil { String value = rowObj.getString(exportExcelFormat.getKey()); if(value != null && !"null".equalsIgnoreCase(value)) { JSONObject dictData = exportExcelFormat.getDictData(); - if(dictData != null) + if(ObjectUtils.isNotEmpty(dictData)) { value = dictData.getString(value); } diff --git a/data/src/main/java/com/sdm/data/controller/DataFileController.java b/data/src/main/java/com/sdm/data/controller/DataFileController.java index 4e7c8b84..48fd4ce2 100644 --- a/data/src/main/java/com/sdm/data/controller/DataFileController.java +++ b/data/src/main/java/com/sdm/data/controller/DataFileController.java @@ -405,6 +405,17 @@ public class DataFileController implements IDataFeignClient { return IDataFileService.batchAddFileInfo(req); } + /** + * 下载文件到本地临时目录 + * + * @param fileId + * @param path + */ + @PostMapping("/downloadFileToLocal") + @Operation(summary = "下载文件到本地临时目录", description = "下载文件到本地临时目录") + public void downloadFileToLocal(@RequestParam(value = "fileId") @Validated Long fileId, @RequestParam(value = "path") @Validated String path) { + IDataFileService.downloadFileToLocal(fileId,path); + } } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/service/IDataFileService.java b/data/src/main/java/com/sdm/data/service/IDataFileService.java index e217f203..fe2d4e66 100644 --- a/data/src/main/java/com/sdm/data/service/IDataFileService.java +++ b/data/src/main/java/com/sdm/data/service/IDataFileService.java @@ -320,4 +320,9 @@ public interface IDataFileService { SdmResponse> batchAddFileInfo(UploadFilesReq req); + /** + * 下载文件到本地临时目录 + */ + void downloadFileToLocal(Long fileId,String path); + } \ No newline at end of file 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 852b12b7..3547734b 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 @@ -59,10 +59,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; @@ -2205,4 +2202,30 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { }, nonSensitiveTaskPool); } + @Override + public void downloadFileToLocal(Long fileId,String path) { + try { + FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileId).one(); + if (ObjectUtils.isEmpty(fileMetadataInfo)) { + return; + } + String fileObjectKey = fileMetadataInfo.getObjectKey(); + boolean hasDownloadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DOWNLOAD); + if (!hasDownloadPermission) { + return; + } + // 从MinIO下载文件 + byte[] fileData = minioService.downloadFile(fileObjectKey); + // 写入响应流 + File folder = new File(path); + folder.mkdir(); + FileOutputStream outputStream = new FileOutputStream(path + File.separator + fileMetadataInfo.getOriginalName()); + outputStream.write(fileData); + outputStream.flush(); + outputStream.close(); + } catch (Exception e) { + log.error("下载文件失败", e); + } + } + } \ No newline at end of file diff --git a/data/src/main/java/com/sdm/data/service/impl/SystemFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/SystemFileIDataFileServiceImpl.java index 2efd7c7f..3129cb2d 100644 --- a/data/src/main/java/com/sdm/data/service/impl/SystemFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/SystemFileIDataFileServiceImpl.java @@ -1548,4 +1548,35 @@ public class SystemFileIDataFileServiceImpl implements IDataFileService { } return SdmResponse.success(); } + + @Override + public void downloadFileToLocal(Long fileId,String downloadPath) { + if (StringUtils.isNotBlank(downloadPath) && downloadPath.contains("..")) { + log.error(downloadPath + "非法文件路径!"); + return; + } + String path = Tools.getRootPath(rootPath, String.valueOf(ThreadLocalContext.getTenantId())) + File.separator + downloadPath; + File file = new File(path); + if (!file.exists()) { + log.error("文件不存在" + path); + } + try { + // 以流的形式下载文件 + InputStream fis = new BufferedInputStream(new FileInputStream(file)); + byte[] buffer = new byte[fis.available()]; + // 读取fis的数据到buffer数组里 + fis.read(buffer); + fis.close(); + byte[] fileBuffer = buffer; + OutputStream toClient = new BufferedOutputStream(new FileOutputStream(path)); + toClient.write(fileBuffer); + toClient.flush(); + toClient.close(); + } catch (IOException e) { + log.error("下载文件失败:" + e); + } + } + + + } diff --git a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java index 73547a1b..176cdcce 100644 --- a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java +++ b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java @@ -5,14 +5,12 @@ import com.sdm.common.entity.req.data.CreateDirReq; import com.sdm.common.entity.req.data.QueryDirReq; import com.sdm.common.entity.req.data.UploadFilesReq; import com.sdm.project.model.entity.SimulationRun; -import com.sdm.project.model.req.GetRunVersionReq; -import com.sdm.project.model.req.ProjectTreeTagReq; -import com.sdm.project.model.req.SpdmAddTaskRunReq; -import com.sdm.project.model.req.SpdmTaskRunReq; +import com.sdm.project.model.req.*; import com.sdm.project.model.resp.RunVersionInfoResp; import com.sdm.project.service.ISimulationRunService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; @@ -112,4 +110,14 @@ public class SimulationRunController { return runService.getRunVersion(req); } + /** + * 生成自动化报告 + * + * @return + */ + @PostMapping("/generateReport") + public void generateReport(@RequestBody SpdmReportReq req, HttpServletResponse response) { + runService.generateReport(req,response); + } + } diff --git a/project/src/main/java/com/sdm/project/model/req/SpdmReportReq.java b/project/src/main/java/com/sdm/project/model/req/SpdmReportReq.java new file mode 100644 index 00000000..527ea20e --- /dev/null +++ b/project/src/main/java/com/sdm/project/model/req/SpdmReportReq.java @@ -0,0 +1,32 @@ +package com.sdm.project.model.req; + +import com.sdm.project.model.bo.TaskNode; +import com.sdm.project.model.entity.SimulationPerformance; +import lombok.Data; + +import java.util.List; + +/** + * @Author xuyundi + * @Date 2024/3/5 + * @Note + */ +@Data +public class SpdmReportReq { + + /** + * 任务执行————关键结果————图片结果的文件id集合 + */ + private List imageFileIdList; + + /** + * 性能指标集合 + */ + private List performanceList; + + /** + * 算例父节点信息集合 + */ + private List parentNodeInfoList; + +} diff --git a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java index 7bc20be0..4ac9ec86 100644 --- a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java +++ b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java @@ -6,11 +6,9 @@ import com.sdm.common.entity.req.data.QueryDirReq; import com.sdm.common.entity.req.data.UploadFilesReq; import com.sdm.project.model.entity.SimulationRun; import com.baomidou.mybatisplus.extension.service.IService; -import com.sdm.project.model.req.GetRunVersionReq; -import com.sdm.project.model.req.ProjectTreeTagReq; -import com.sdm.project.model.req.SpdmAddTaskRunReq; -import com.sdm.project.model.req.SpdmTaskRunReq; +import com.sdm.project.model.req.*; import com.sdm.project.model.resp.RunVersionInfoResp; +import jakarta.servlet.http.HttpServletResponse; import java.util.List; @@ -42,4 +40,5 @@ public interface ISimulationRunService extends IService { SdmResponse getRunVersion(GetRunVersionReq req); + void generateReport(SpdmReportReq req, HttpServletResponse response); } diff --git a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java index 3d941293..7108117d 100644 --- a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java @@ -6,10 +6,7 @@ import com.sdm.common.common.SdmResponse; import com.sdm.common.common.ThreadLocalContext; import com.sdm.common.entity.enums.DirTypeEnum; import com.sdm.common.entity.enums.NodeTypeEnum; -import com.sdm.common.entity.req.data.CreateDirReq; -import com.sdm.common.entity.req.data.DelDirReq; -import com.sdm.common.entity.req.data.QueryDirReq; -import com.sdm.common.entity.req.data.UploadFilesReq; +import com.sdm.common.entity.req.data.*; import com.sdm.common.entity.req.system.UserQueryReq; import com.sdm.common.entity.resp.data.FileMetadataInfoResp; import com.sdm.common.entity.resp.system.CIDUserResp; @@ -20,6 +17,7 @@ import com.sdm.project.common.MemberTypeEnum; import com.sdm.project.common.RunPerformanceStatusEnum; import com.sdm.project.dao.SimulationProjectMapper; import com.sdm.project.dao.SimulationRunMapper; +import com.sdm.project.model.bo.TaskNode; import com.sdm.project.model.bo.TaskNodeTag; import com.sdm.project.model.entity.SimulationPerformance; import com.sdm.project.model.entity.SimulationRun; @@ -35,6 +33,7 @@ import com.sdm.project.service.ISimulationPerformanceService; import com.sdm.project.service.ISimulationRunService; import com.sdm.project.service.ISimulationTaskMemberService; import com.sdm.project.service.ISimulationTaskService; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; @@ -44,6 +43,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; @@ -80,6 +82,8 @@ public class SimulationRunServiceImpl extends ServiceImpl StringUtils.isEmpty(run.getParentId())).collect(Collectors.toList()); } + + @Override + public void generateReport(SpdmReportReq req, HttpServletResponse response) { + log.info("生成自动化报告参数为:{}",req); + // 根据文件id下载文件到临时目录 + List imageFileIdList = req.getImageFileIdList(); + 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 { + // 获取临时路径中脚本生成的报告 + FileInputStream fileInputStream = new FileInputStream(TEMP_REPORT_PATH + randomId + File.separator + "report.docx"); + byte[] fileData = fileInputStream.readAllBytes(); + // 设置响应头 + response.reset(); + response.setContentType("application/octet-stream;charset=UTF-8"); + String encodedFileName = URLEncoder.encode("自动化报告_" + randomId, StandardCharsets.UTF_8); + response.addHeader("Content-Disposition", "attachment;filename=" + encodedFileName); + response.addHeader("Content-Length", String.valueOf(fileData.length)); + // 写入响应流 + OutputStream outputStream = response.getOutputStream(); + outputStream.write(fileData); + outputStream.flush(); + outputStream.close(); + fileInputStream.close(); + }catch (Exception ex) { + log.error("生成自动化报告失败:{}",ex.getMessage()); + throw new RuntimeException("生成自动化报告失败"); + } + // 删除临时路径 + log.info("删除临时路径:{},中。。。。。。",randomId); +// deleteFolder(new File(TEMP_REPORT_PATH + randomId)); + } + // 性能指标集合 + List performanceList = req.getPerformanceList(); + // 父节点集合 + List parentNodeInfoList = req.getParentNodeInfoList(); + + } + + public static void deleteFolder(File folder) { + if (folder.isDirectory()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + deleteFolder(file); + } + } + } + folder.delete(); + } + }