优化自动生成报告,从前节点获取图片文件

This commit is contained in:
2026-01-21 14:10:36 +08:00
parent afaa08bbcc
commit 15a1fa6ffd

View File

@@ -28,9 +28,16 @@ import org.springframework.mock.web.MockMultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.sdm.common.entity.req.data.QueryDirReq;
import com.sdm.common.entity.resp.PageDataResp;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* @Description: 生成自动化报告脚本处理器
@@ -40,6 +47,17 @@ import java.util.regex.Pattern;
@Slf4j
@Component("exportWordScript")
public class ExportWordScriptHandler implements ExecutionHandler<Map<String, Object>,ExportWordScriptExecuteConfig> {
/**
* 图片文件后缀正则(忽略大小写)
*/
private static final Pattern IMAGE_FILE_PATTERN = Pattern.compile("(?i).*\\.(png|jpg|jpeg|gif)$");
/**
* 查询目录文件的默认分页大小
*/
private static final int QUERY_DIR_PAGE_SIZE = 1000;
@Autowired
private IDataFeignClient dataFeignClient;
@@ -54,92 +72,242 @@ public class ExportWordScriptHandler implements ExecutionHandler<Map<String, Obj
@Override
public void execute(DelegateExecution execution, Map<String, Object> params, ExportWordScriptExecuteConfig config) {
String runId = (String) execution.getVariable("runId");
String currentNodeId = execution.getCurrentActivityId();
String processDefinitionId = execution.getProcessDefinitionId();
try {
// 获取前置节点参数
String beforeNodeId = config.getBeforeNodeId();
String currentNodeId =execution.getCurrentActivityId();
String fileRegularStr = config.getFileRegularStr();
// 获取当前流程实例参数
String runId = (String) execution.getVariable("runId");
Long userId = (Long) execution.getVariable("userId");
String userName = (String) execution.getVariable("userName");
Long tenantId = (Long) execution.getVariable("tenantId");
ThreadLocalContext.setUserId(userId);
ThreadLocalContext.setUserName(userName);
ThreadLocalContext.setTenantId(tenantId);
String processDefinitionId = execution.getProcessDefinitionId();
log.info("ExportWordScriptHandler 开始执行 runId:{},userId:{},userName:{},tenantId:{},processDefinitionId:{}, beforeNodeId:{}, currentNodeId:{},fileRegularStr:{}", runId,userId,userName,tenantId,processDefinitionId, beforeNodeId, currentNodeId,fileRegularStr);
// 1. 初始化线程上下文
initThreadLocalContext(execution);
logExecutionStart(runId, config, currentNodeId, processDefinitionId);
// 2. 构建项目信息请求
ProjecInfoReq projecInfoReq = buildprojectInfoReq(params);
log.info("ExportWordScriptHandler的请求参数 projectInfoReq:{}", projecInfoReq);
// 3. 获取性能指标
List<SimulationPerformance> performanceList = fetchPerformanceList(runId);
// 4. 获取关键结果图片文件ID优先从前置节点获取否则从算列结果获取
String beforeNodeId = config.getBeforeNodeId();
List<Long> keyResultImageFileIds = StringUtils.hasText(beforeNodeId)
? fetchBeforeNodeImageFileIds(runId, beforeNodeId, processDefinitionId)
: fetchKeyResultImageFileIds(runId);
SdmResponse<List<PerformanceResp>> runPerformance = simuluationPerformanceFeignClient.getRunPerformance(runId);
if(!runPerformance.isSuccess()){
log.error("获取算列性能指标失败");
throw new RuntimeException("获取算列性能指标失败");
}
List<SimulationPerformance> performanceList = new ArrayList<>();
for (PerformanceResp datum : runPerformance.getData()) {
SimulationPerformance performance = new SimulationPerformance();
BeanUtils.copyProperties(datum, performance);
performanceList.add(performance);
}
log.info("ExportWordScriptHandler的返回参数 runPerformance:{}", runPerformance);
SdmResponse<List<Long>> simulationKeyResultFileIds = simulationRunFeignClient.getSimulationKeyResultFileIds(runId);
if(!simulationKeyResultFileIds.isSuccess()){
log.error("获取算列关键结果文件失败");
throw new RuntimeException("获取算列关键结果文件失败");
}
log.info("ExportWordScriptHandler的返回参数 simulationKeyResultFileIds:{}", simulationKeyResultFileIds);
ProcessNodeParam currentProcessNodeParam = processNodeParamService.lambdaQuery()
.eq(ProcessNodeParam::getRunId, runId)
.eq(ProcessNodeParam::getNodeId, currentNodeId)
.eq(ProcessNodeParam::getProcessDefinitionId, processDefinitionId)
.one();
// 获取当前节点输出文件夹信息
String currentNodeParamJson = currentProcessNodeParam.getParamJson();
JSONObject currentParamJsonObject = JSONObject.parseObject(currentNodeParamJson);
Long currentNodeOutputDirId = currentParamJsonObject.getLong("outputDirId");
FileMetadataInfoResp currentNodeFileMetadataInfoResp = getFileBaseInfo(currentNodeOutputDirId);
String currentNodeObjectKey = currentNodeFileMetadataInfoResp.getObjectKey();
log.info("当前节点配置参数:{}", currentNodeParamJson);
String currentNodeOutputDirPath = FlowableConfig.FLOWABLE_SIMULATION_BASEDIR + currentNodeObjectKey;
log.info("当前节点输出文件夹:{}", currentNodeOutputDirPath);
// todo 生成脚本的接口
SpdmReportReq req = new SpdmReportReq();
req.setProjecInfoReq(projecInfoReq);
req.setOutPutDirPath(currentNodeOutputDirPath);
req.setImageFileIdList(simulationKeyResultFileIds.getData());
req.setPerformanceList(performanceList);
SdmResponse<Void> voidSdmResponse = simulationRunFeignClient.generateReportInternal(req);
if(!voidSdmResponse.isSuccess()){
log.error("生成自动化报告失败");
throw new RuntimeException("生成自动化报告失败");
}
try {
String reportPath = currentNodeOutputDirPath + "report.docx";
log.info("报告路径:{}", reportPath);
// 获取临时路径中脚本生成的报告
uploadResultFileToMinio(currentNodeOutputDirPath + "report.docx",currentNodeOutputDirId);
} catch (Exception ex) {
log.error("生成自动化报告失败:{}", ex.getMessage(), ex);
throw new RuntimeException("生成自动化报告失败");
}
// 5. 获取输出目录信息
OutputDirInfo outputDirInfo = resolveOutputDirInfo(runId, currentNodeId, processDefinitionId);
// 6. 生成并上传报告
generateAndUploadReport(projecInfoReq, performanceList, keyResultImageFileIds, outputDirInfo);
} catch (Exception e) {
log.error("执行ExportWordScript失败", e);
throw new RuntimeException("执行ExportWordScript失败: " + e.getMessage(), e);
}
}
/**
* 初始化线程上下文
*/
private void initThreadLocalContext(DelegateExecution execution) {
Long userId = (Long) execution.getVariable("userId");
String userName = (String) execution.getVariable("userName");
Long tenantId = (Long) execution.getVariable("tenantId");
ThreadLocalContext.setUserId(userId);
ThreadLocalContext.setUserName(userName);
ThreadLocalContext.setTenantId(tenantId);
}
/**
* 记录执行开始日志
*/
private void logExecutionStart(String runId, ExportWordScriptExecuteConfig config,
String currentNodeId, String processDefinitionId) {
Long userId = ThreadLocalContext.getUserId();
String userName = ThreadLocalContext.getUserName();
Long tenantId = ThreadLocalContext.getTenantId();
log.info("ExportWordScriptHandler 开始执行 runId:{},userId:{},userName:{},tenantId:{}," +
"processDefinitionId:{}, beforeNodeId:{}, currentNodeId:{},fileRegularStr:{}",
runId, userId, userName, tenantId, processDefinitionId,
config.getBeforeNodeId(), currentNodeId, config.getFileRegularStr());
}
/**
* 获取性能指标列表
*/
private List<SimulationPerformance> fetchPerformanceList(String runId) {
SdmResponse<List<PerformanceResp>> response = simuluationPerformanceFeignClient.getRunPerformance(runId);
checkResponse(response, "获取算列性能指标失败");
List<SimulationPerformance> performanceList = new ArrayList<>();
for (PerformanceResp datum : response.getData()) {
SimulationPerformance performance = new SimulationPerformance();
BeanUtils.copyProperties(datum, performance);
performanceList.add(performance);
}
log.info("ExportWordScriptHandler的返回参数 runPerformance:{}", response);
return performanceList;
}
/**
* 获取关键结果-图片文件ID列表从算列结果获取
*/
private List<Long> fetchKeyResultImageFileIds(String runId) {
SdmResponse<List<Long>> response = simulationRunFeignClient.getSimulationKeyResultFileIds(runId);
checkResponse(response, "获取算列关键结果文件失败");
log.info("ExportWordScriptHandler的返回参数 simulationKeyResultFileIds:{}", response);
return response.getData();
}
/**
* 从前置节点输出目录获取图片文件ID列表
*/
private List<Long> fetchBeforeNodeImageFileIds(String runId, String beforeNodeId, String processDefinitionId) {
// 1. 获取前置节点输出目录ID
Long beforeNodeOutputDirId = resolveNodeOutputDirId(runId, beforeNodeId, processDefinitionId);
if (beforeNodeOutputDirId == null) {
log.warn("前置节点输出目录不存在beforeNodeId:{}", beforeNodeId);
return Collections.emptyList();
}
// 2. 查询目录下所有文件
List<FileMetadataInfoResp> fileList = queryDirFiles(beforeNodeOutputDirId);
if (CollectionUtils.isEmpty(fileList)) {
log.info("前置节点输出目录下无文件beforeNodeId:{}, dirId:{}", beforeNodeId, beforeNodeOutputDirId);
return Collections.emptyList();
}
// 3. 正则过滤图片文件并收集ID
List<Long> imageFileIds = fileList.stream()
.filter(file -> file.getOriginalName() != null
&& IMAGE_FILE_PATTERN.matcher(file.getOriginalName()).matches())
.map(FileMetadataInfoResp::getId)
.collect(Collectors.toList());
log.info("从前置节点获取到图片文件数量:{}, beforeNodeId:{}", imageFileIds.size(), beforeNodeId);
return imageFileIds;
}
/**
* 解析节点的输出目录ID
*/
private Long resolveNodeOutputDirId(String runId, String nodeId, String processDefinitionId) {
ProcessNodeParam nodeParam = processNodeParamService.lambdaQuery()
.eq(ProcessNodeParam::getRunId, runId)
.eq(ProcessNodeParam::getNodeId, nodeId)
.eq(ProcessNodeParam::getProcessDefinitionId, processDefinitionId)
.one();
if (nodeParam == null || nodeParam.getParamJson() == null) {
return null;
}
JSONObject paramJsonObject = JSONObject.parseObject(nodeParam.getParamJson());
return paramJsonObject.getLong("outputDirId");
}
/**
* 查询目录下的文件列表(只查文件,不含子目录)
*/
private List<FileMetadataInfoResp> queryDirFiles(Long dirId) {
QueryDirReq req = new QueryDirReq();
req.setFileId(dirId);
req.setQueryTarget(2); // 只查文件
req.setCurrent(1);
req.setSize(QUERY_DIR_PAGE_SIZE);
SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> response = dataFeignClient.queryDir(req);
checkResponse(response, "查询目录文件失败dirId:" + dirId);
// 处理空数据情况
if (response.getData() == null || response.getData().getData() == null) {
return Collections.emptyList();
}
return response.getData().getData();
}
/**
* 解析输出目录信息
*/
private OutputDirInfo resolveOutputDirInfo(String runId, String currentNodeId, String processDefinitionId) {
ProcessNodeParam nodeParam = processNodeParamService.lambdaQuery()
.eq(ProcessNodeParam::getRunId, runId)
.eq(ProcessNodeParam::getNodeId, currentNodeId)
.eq(ProcessNodeParam::getProcessDefinitionId, processDefinitionId)
.one();
String paramJson = nodeParam.getParamJson();
log.info("当前节点配置参数:{}", paramJson);
JSONObject paramJsonObject = JSONObject.parseObject(paramJson);
Long outputDirId = paramJsonObject.getLong("outputDirId");
FileMetadataInfoResp fileMetadata = getFileBaseInfo(outputDirId);
String outputDirPath = FlowableConfig.FLOWABLE_SIMULATION_BASEDIR + fileMetadata.getObjectKey();
log.info("当前节点输出文件夹:{}", outputDirPath);
return new OutputDirInfo(outputDirId, outputDirPath);
}
/**
* 生成并上传报告
*/
private void generateAndUploadReport(ProjecInfoReq projecInfoReq,
List<SimulationPerformance> performanceList,
List<Long> keyResultImageFileIds,
OutputDirInfo outputDirInfo) {
// 构建报告请求
SpdmReportReq req = new SpdmReportReq();
req.setProjecInfoReq(projecInfoReq);
req.setOutPutDirPath(outputDirInfo.getPath());
req.setImageFileIdList(keyResultImageFileIds);
req.setPerformanceList(performanceList);
// 调用生成报告接口
SdmResponse<Void> response = simulationRunFeignClient.generateReportInternal(req);
checkResponse(response, "生成自动化报告失败");
// 上传报告文件
String reportPath = outputDirInfo.getPath() + "report.docx";
log.info("报告路径:{}", reportPath);
uploadResultFileToMinio(reportPath, outputDirInfo.getDirId());
}
/**
* 统一校验远程调用响应
*/
private <T> void checkResponse(SdmResponse<T> response, String errorMessage) {
if (!response.isSuccess()) {
log.error(errorMessage);
throw new RuntimeException(errorMessage+""+response.getMessage());
}
}
/**
* 输出目录信息内部类
*/
private static class OutputDirInfo {
private final Long dirId;
private final String path;
public OutputDirInfo(Long dirId, String path) {
this.dirId = dirId;
this.path = path;
}
public Long getDirId() {
return dirId;
}
public String getPath() {
return path;
}
}
private static ProjecInfoReq buildprojectInfoReq(Map<String, Object> params) {
ProjecInfoReq projectInfoReq = new ProjecInfoReq();
projectInfoReq.setDepartment((String)params.get("department"));