diff --git a/common/src/main/java/com/sdm/common/entity/req/data/UploadFilesReq.java b/common/src/main/java/com/sdm/common/entity/req/data/UploadFilesReq.java index 1078c42f..e8b9d51d 100644 --- a/common/src/main/java/com/sdm/common/entity/req/data/UploadFilesReq.java +++ b/common/src/main/java/com/sdm/common/entity/req/data/UploadFilesReq.java @@ -72,6 +72,12 @@ public class UploadFilesReq { @Schema(description = "文件类型 1:模型文件 2:仿真报告 3:计算文件 4:曲线文件 5:云图文件", implementation = FileBizTypeEnum.class) private Integer fileType; + /** + * 之前的预留tag字段,现在取tag1存储序号用来对生成任务报告里的图片排序 + */ + @Schema(description = "排序号") + private String tag1; + // ---------------------------------------------------------------- // 很重要,用于设置标签,通过autoFillDictTags切面 ,设置dictTagIdsCache /** 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 c3aa45a1..1c6498fd 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 @@ -444,7 +444,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { * @param fileSize 文件大小 * @return 创建后的文件元数据信息 */ - private FileMetadataInfo createFileMetadata(String filePath, String fileName, String projectId, String analysisDirectionId, String remarks, Long parentId, Long fileSize + private FileMetadataInfo createFileMetadata(String filePath, String fileName, String projectId, String analysisDirectionId, String remarks, Long parentId, Long fileSize, String tag1 ) { FileMetadataInfo fileInfo = new FileMetadataInfo(); fileInfo.setObjectKey(filePath); @@ -459,6 +459,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { fileInfo.setCreatorId(ThreadLocalContext.getUserId()); fileInfo.setTenantId(ThreadLocalContext.getTenantId()); fileInfo.setFileSize(fileSize); + fileInfo.setTag1(tag1); return fileInfo; } @@ -1604,7 +1605,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { minioService.copyFile(oldDirMinioObjectKey, newDirMinioObjectKey,sourceMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(newDirMinioObjectKey, sourceMetadataInfo.getOriginalName(), - null, null, null, targetParentMetadataInfo.getId(), sourceMetadataInfo.getFileSize()); + null, null, null, targetParentMetadataInfo.getId(), sourceMetadataInfo.getFileSize(), sourceMetadataInfo.getTag1()); fileMetadataInfoService.save(fileInfo); return SdmResponse.success("复制文件成功"); } catch (Exception e) { @@ -1951,7 +1952,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { minioService.uploadFile(req.getFile(), fileMinioObjectKey, null, dirMetadataInfo.getBucketName()); FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, originalName, - req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), req.getFile().getSize() + req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), req.getFile().getSize(), req.getTag1() ); fileMetadataInfoService.save(fileInfo); @@ -1990,7 +1991,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { try { FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, fileReq.getFileName(), - req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), fileReq.getSize()); + req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), fileReq.getSize(), null); fileInfo.setUploadTaskId(req.getUploadTaskId()); fileInfo.setTemplateId(req.getTemplateId()); fileInfo.setTemplateName(req.getTemplateName()); @@ -2600,7 +2601,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null,oldFileMetadataInfo.getBucketName()); // 创建目录元数据并保存到数据库 FileMetadataInfo newFileInfo = createFileMetadata(newFileMinioObjectKey, req.getFileName(), - req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), oldFileMetadataInfo.getParentId(), req.getFile().getSize() + req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), oldFileMetadataInfo.getParentId(), req.getFile().getSize(), null ); newFileInfo.setRemarks(oldFileMetadataInfo.getRemarks()); newFileInfo.setProjectId(oldFileMetadataInfo.getProjectId()); @@ -2792,7 +2793,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(avatarMinioObjectKey, newFilename, - null, null, null, parAvatarDirId, avatar.getSize()); + null, null, null, parAvatarDirId, avatar.getSize(), null); fileMetadataInfoService.save(fileInfo); JSONObject jsonObject = new JSONObject(); jsonObject.put("avatarId", fileInfo.getId()); @@ -2853,7 +2854,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(simulationParamMinioObjectKey, originalFilename, - null, null, null, parSimulationParamDirId, paramFile.getSize()); + null, null, null, parSimulationParamDirId, paramFile.getSize(), null); // 设置文件状态为审批中 暂不可见 fileInfo.setApprovalStatus(ApprovalFileDataStatusEnum.PENDING.getKey()); fileInfo.setApproveType(ApproveFileDataTypeEnum.UPLOAD_REVIEWING.getCode()); @@ -2949,7 +2950,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(trainingMinioObjectKey, originalFilename, - null, null, null, parTrainModelDirId, trainModelFile.getSize()); + null, null, null, parTrainModelDirId, trainModelFile.getSize(), null); fileMetadataInfoService.save(fileInfo); return SdmResponse.success(fileInfo.getId()); } catch (Exception e) { @@ -2994,7 +2995,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { // 创建目录元数据并保存到数据库 FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, originalFilename, - null, null, null, parDirId, file.getSize()); + null, null, null, parDirId, file.getSize(), null); fileMetadataInfoService.save(fileInfo); return SdmResponse.success(fileInfo.getId()); } catch (Exception e) { 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 90433691..d32e0cbb 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 @@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -43,6 +44,7 @@ import com.sdm.common.feign.impl.flowable.FlowableClientFeignClientImpl; import com.sdm.common.feign.impl.system.ApproveFeignClientImpl; import com.sdm.common.feign.impl.system.SysUserFeignClientImpl; import com.sdm.common.service.DataFileService; +import com.sdm.common.utils.CommonUtils; import com.sdm.common.utils.PageUtils; import com.sdm.common.utils.RandomUtil; import com.sdm.project.common.*; @@ -73,6 +75,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.*; +import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -81,6 +84,7 @@ import java.nio.file.StandardCopyOption; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import static com.sdm.common.service.BaseService.generateUuid; @@ -1626,6 +1630,7 @@ public class SimulationRunServiceImpl extends ServiceImpl imageNames = extractPicNames(reportContent); + if (CollectionUtils.isNotEmpty(imageNames)) { + int sortOrder = 1; + for (String imageName : imageNames) { + archiveReportAndImage(req.getTaskId(), req.getRunId(), randomId, FileBizTypeEnum.CLOUD_FILE, imageName, String.valueOf(sortOrder++)); } - - fileInputStream.close(); - // 删除临时路径 - log.info("删除临时路径:{},中。。。。。。", randomId); - deleteFolder(new File(TEMP_REPORT_PATH + randomId)); - return SdmResponse.success(); - } catch (Exception ex) { - log.error("生成自动化报告失败:{}", ex.getMessage()); - throw new RuntimeException("生成自动化报告失败"); } + + // 删除临时路径 +// log.info("删除临时路径:{},中。。。。。。", randomId); +// deleteFolder(new File(TEMP_REPORT_PATH + randomId)); } return SdmResponse.failed("生成自动化报告失败"); } + private void archiveReportAndImage(String taskId, String runId, String randomId, FileBizTypeEnum fileBizTypeEnum, String imageName, String sortOrder) { + byte[] fileData = null; + try { + String fileName = ""; + // 获取临时路径中脚本生成的报告 + if (fileBizTypeEnum == FileBizTypeEnum.CLOUD_FILE) { + fileName = imageName + ".png"; + } else { + fileName = "report_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + + ".docx"; + } + FileInputStream fileInputStream = new FileInputStream(TEMP_REPORT_PATH + randomId + File.separator + fileName); + fileData = fileInputStream.readAllBytes(); + + // 创建临时MultipartFile + MockMultipartFile multipartFile = new MockMultipartFile( + fileName, + fileName, + "application/octet-stream", + fileData); + if (StringUtils.isNotEmpty(taskId)) { + // 上传到任务下的交付物文件夹的报告文件夹下 + Long parentId = getParentDirId(taskId, fileBizTypeEnum.getDirName()); + UploadFilesReq filesReq = new UploadFilesReq(); + filesReq.setFile(multipartFile); + filesReq.setFileName(fileName); + filesReq.setFileType(fileBizTypeEnum.getValue()); + filesReq.setUuid(null); + filesReq.setDirId(parentId); + filesReq.setTag1(sortOrder); + filesReq.setFileTypeDictClass(FileDictTagEnum.FILE_TYPE.getDictClass()); + filesReq.setFileTypeDictValue(String.valueOf(fileBizTypeEnum.getValue())); + filesReq.setDictTags(Arrays.asList(FileDictTagEnum.FILE_TYPE.getDictClassFieldName(), FileDictTagEnum.FILE_TYPE.getDictValueFieldName())); + SdmResponse sdmResponse = uploadKeyResultFiles(filesReq); + if (!sdmResponse.isSuccess()) { + throw new RuntimeException("生成自动化报告上传任务报告结果目录失败"); + } + } else { + // 上传到算例下的报告文件夹下 + KeyResultReq resultReq = new KeyResultReq(); + resultReq.setKeyResultType(fileBizTypeEnum == FileBizTypeEnum.CLOUD_FILE ? KeyResultTypeEnum.IMAGE.getKeyResultType() : KeyResultTypeEnum.DOCUMENT.getKeyResultType()); + resultReq.setRunId(runId); + resultReq.setName(fileName); + + resultReq.setFile(multipartFile); + resultReq.setFileName(fileName); + resultReq.setFileType(fileBizTypeEnum.getValue()); + SdmResponse sdmResponse = addSimulationKeyResult(resultReq); + if (!sdmResponse.isSuccess()) { + throw new RuntimeException("生成自动化报告上传算例报告结果目录失败"); + } + } + fileInputStream.close(); + } catch (Exception ex) { + log.error("生成自动化报告失败:{}", ex.getMessage()); + throw new RuntimeException("生成自动化报告失败"); + } + } + + private void archivePerformances(String reportContent, String taskId, String runId, Long userId) { + // 解析前端传入的指标 + List performanceList = extractPerformance(reportContent); + + List oldPerformances = null; + if (StringUtils.isNotEmpty(runId)) { + // 算例下的指标 + oldPerformances = simulationPerformanceService.lambdaQuery().eq(SimulationPerformance::getRunId, runId).list(); + } else { + // 任务下的指标 + oldPerformances = simulationPerformanceService.lambdaQuery().eq(SimulationPerformance::getTaskId, taskId).isNull(SimulationPerformance::getRunId).list(); + } + if (CollectionUtils.isEmpty(oldPerformances)) { + // 直接新增 + for (SimulationPerformance newPerformance : performanceList) { + newPerformance.setTaskId(taskId); + newPerformance.setRunId(runId); + newPerformance.setUuid(RandomUtil.generateString(32)); + newPerformance.setCompleteStatus(getCompleteStatus(newPerformance)); + newPerformance.setCreator(userId); + } + } else { + Map> oldPerformanceMap = oldPerformances.stream().collect(Collectors.groupingBy(SimulationPerformance::getNodeName)); + for (SimulationPerformance newPerformance : performanceList) { + List oldPerformanceByNodeName = oldPerformanceMap.get(newPerformance.getNodeName()); + if (CollectionUtils.isNotEmpty(oldPerformanceByNodeName)) { + newPerformance.setId(oldPerformanceByNodeName.get(0).getId()); + newPerformance.setCompleteStatus(getCompleteStatus(newPerformance)); + } else { + newPerformance.setTaskId(taskId); + newPerformance.setRunId(runId); + newPerformance.setUuid(RandomUtil.generateString(32)); + newPerformance.setCompleteStatus(getCompleteStatus(newPerformance)); + newPerformance.setCreator(userId); + } + } + } + List updatePerformances = performanceList.stream().filter(i -> i.getId() != null).toList(); + List addPerformances = performanceList.stream().filter(i -> i.getId() == null).toList(); + simulationPerformanceService.updateBatchById(updatePerformances); + simulationPerformanceService.saveBatch(addPerformances); + } + + /** + * 读取前端传入的JSON参数,获得图片名称,以便获取到python脚本生成的图片文件 + */ + public List extractPicNames(String jsonData) { + List imageNames = new ArrayList<>(); + ObjectMapper objectMapper = new ObjectMapper(); + + try { + JsonNode rootNode = objectMapper.readTree(jsonData); + for (JsonNode node : rootNode) { + // 检查是否为图片类型 + if (node.has("type") && "img".equals(node.get("type").asText())) { + JsonNode valueNode = node.get("value"); + if (valueNode != null && valueNode.isArray()) { + // 遍历数组获取所有图片路径 + for (JsonNode imageNode : valueNode) { + if (imageNode.has("picName")) { + imageNames.add(imageNode.get("picName").asText()); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return imageNames; + } + + /** + * 读取前端传入的JSON参数,获得指标数据 + */ + public List extractPerformance(String jsonData) { + List performances = new ArrayList<>(); + ObjectMapper objectMapper = new ObjectMapper(); + + try { + JsonNode rootNode = objectMapper.readTree(jsonData); + for (JsonNode node : rootNode) { + // 检查是否为图片类型 + JsonNode paramsNode = node.get("params"); + if (paramsNode != null && paramsNode.has("tableType") && "performance".equals(paramsNode.get("tableType").asText())) { + JsonNode valueNode = node.get("value"); + if (valueNode != null && valueNode.isArray()) { + performances.addAll(objectMapper.convertValue(valueNode, new TypeReference>() {})); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + // 去重 + performances = new ArrayList<>(performances.stream() + .collect(Collectors.toMap( + SimulationPerformance::getNodeName, // key:nodeName + Function.identity(), // value:对象本身 + (existing, replacement) -> existing // 冲突时保留已有的(第一个) + )) + .values()); + return performances; + } + + public String getCompleteStatus(SimulationPerformance simulationPerformance) { + // 目标值 + String targetValueStr = simulationPerformance.getTargetValue(); + // 分析值 + String resultValueStr = simulationPerformance.getResultValue(); + // 达标方式 + String method = simulationPerformance.getMethod(); + // 校验 + if (StringUtils.isNotBlank(targetValueStr) && StringUtils.isNotBlank(resultValueStr)) { + if (!validateNumber(targetValueStr) || !validateNumber(resultValueStr)) { + return null; + } + try { + // 转换为BigDecimal(自动处理整数和小数) + BigDecimal targetValue = new BigDecimal(targetValueStr); + BigDecimal resultValue = new BigDecimal(resultValueStr); + + int comparison = resultValue.compareTo(targetValue); + boolean isPassed = switch (method) { + case "≥" -> comparison >= 0; + case ">" -> comparison > 0; + case "<" -> comparison < 0; + case "≤" -> comparison <= 0; + default -> throw new IllegalArgumentException("无效运算符: " + method); + }; + return isPassed ? PerformanceStatusEnum.STARTED.getCode() : PerformanceStatusEnum.NOT_STARTED.getCode(); + } catch (Exception e) { + log.error("数字格式异常:", e); + } + } + return null; + } + + private boolean validateNumber(String input) { + String trimmed = input.trim(); + // 前置格式检查 - 验证是不是有效数字格式 + if (!CommonUtils.isValidNumberFormat(trimmed)) { + return false; + } + return true; + } + @Override public void editReportAndDownload(EditReportReq req, HttpServletResponse response) { log.info("编辑报告参数为:{}", req);