1、生成自动化脚本中使用动态模板
This commit is contained in:
@@ -4,7 +4,6 @@ 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.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
@@ -15,7 +14,6 @@ import com.sdm.common.entity.enums.ApproveTypeEnum;
|
||||
import com.sdm.common.entity.enums.DirTypeEnum;
|
||||
import com.sdm.common.entity.enums.NodeTypeEnum;
|
||||
import com.sdm.common.entity.flowable.dto.FlowElementDTO;
|
||||
import com.sdm.common.entity.flowable.dto.NodeStructureInfo;
|
||||
import com.sdm.common.entity.flowable.dto.ProcessDefinitionDTO;
|
||||
import com.sdm.common.entity.req.capability.FlowNodeDto;
|
||||
import com.sdm.common.entity.req.data.*;
|
||||
@@ -35,9 +33,6 @@ import com.sdm.common.feign.impl.data.DataClientFeignClientImpl;
|
||||
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.feign.inter.capability.ISimulationFlowFeignClient;
|
||||
import com.sdm.common.feign.inter.data.IDataFeignClient;
|
||||
import com.sdm.common.feign.inter.flowable.IFlowableFeignClient;
|
||||
import com.sdm.common.utils.PageUtils;
|
||||
import com.sdm.common.utils.RandomUtil;
|
||||
import com.sdm.project.common.KeyResultTypeEnum;
|
||||
@@ -48,7 +43,6 @@ import com.sdm.project.dao.SimulationProjectMapper;
|
||||
import com.sdm.project.dao.SimulationRunMapper;
|
||||
import com.sdm.project.model.bo.ApprovalDeliverableContentsModel;
|
||||
import com.sdm.project.model.bo.CurveParamDto;
|
||||
import com.sdm.project.model.bo.TaskNode;
|
||||
import com.sdm.project.model.bo.TaskNodeTag;
|
||||
import com.sdm.project.model.entity.*;
|
||||
import com.sdm.project.model.po.NodeAllBase;
|
||||
@@ -58,12 +52,8 @@ import com.sdm.project.model.po.TaskNodePo;
|
||||
import com.sdm.project.model.req.*;
|
||||
import com.sdm.project.model.resp.KeyResultAndTaskInfoResp;
|
||||
import com.sdm.project.model.resp.RunVersionInfoResp;
|
||||
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 com.sdm.project.service.*;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
@@ -75,11 +65,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -136,7 +126,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
|
||||
private static final String TEMP_REPORT_PATH = "/opt/report/";
|
||||
|
||||
private static final String TEMPLATE_PATH = " /opt/script/template staticsAnalyse";
|
||||
private static final String TEMPLATE_PATH = " /opt/script/template ";
|
||||
|
||||
// 临时文件存储目录
|
||||
private final String tempFileDir = System.getProperty("user.dir") + "/csv_uploads/";
|
||||
@@ -564,7 +554,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
.eq(SimulationPerformance::getTaskId, simulationTask.getUuid())
|
||||
.isNull(SimulationPerformance::getRunId)
|
||||
.list();
|
||||
if(CollectionUtils.isNotEmpty(simulationTaskPerformances)) {
|
||||
if (CollectionUtils.isNotEmpty(simulationTaskPerformances)) {
|
||||
List<SimulationPerformance> runPerformanceList = new ArrayList<>();
|
||||
for (SimulationPerformance performanceBean : simulationTaskPerformances) {
|
||||
SimulationPerformance runPerformance = new SimulationPerformance();
|
||||
@@ -716,7 +706,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
}).collect(Collectors.toList());
|
||||
return SdmResponse.success(buildRunTreeWithMap(runRespList));
|
||||
return SdmResponse.success(buildRunTreeWithMap(runRespList));
|
||||
}
|
||||
return SdmResponse.success();
|
||||
}
|
||||
@@ -909,7 +899,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public SdmResponse<List<BatchAddFileInfoResp>> batchAddSimulationKeyResult(KeyResultReq req) {
|
||||
if (CollectionUtils.isNotEmpty(req.getKeyResultList())) {
|
||||
UploadFilesReq filesReq = new UploadFilesReq();
|
||||
UploadFilesReq filesReq = new UploadFilesReq();
|
||||
BeanUtils.copyProperties(req, filesReq);
|
||||
filesReq.setUuid(req.getTaskId());
|
||||
filesReq.setSourceFiles(req.getKeyResultList().stream().map(i -> new UploadFilesReq(i.getFileName(), i.getFileSize(), i.getFileType())).toList());
|
||||
@@ -949,7 +939,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
contentsModel.setTaskId(req.getTaskId());
|
||||
contentsModel.setDifficult(req.getDifficult());
|
||||
// 发起审批
|
||||
String cidFlowId = launchParamApprove(req.getTemplateId(),req.getTemplateName(), JSONObject.toJSONString(contentsModel),1);
|
||||
String cidFlowId = launchParamApprove(req.getTemplateId(), req.getTemplateName(), JSONObject.toJSONString(contentsModel), 1);
|
||||
if (StringUtils.isEmpty(cidFlowId)) {
|
||||
return SdmResponse.failed("发起评审失败");
|
||||
}
|
||||
@@ -962,17 +952,15 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
|
||||
@Override
|
||||
public SdmResponse deliverableApproveCallback(LaunchApproveReq req) {
|
||||
SdmResponse response = SdmResponse.success();
|
||||
SdmResponse response = SdmResponse.success();
|
||||
int approveStatus = req.approveStatus;
|
||||
String approveContent = req.approveContents;
|
||||
JSONObject contentObj = JSONObject.parseObject(approveContent);
|
||||
if(contentObj != null && contentObj.containsKey("taskId"))
|
||||
{
|
||||
if (contentObj != null && contentObj.containsKey("taskId")) {
|
||||
String taskId = contentObj.getString("taskId");
|
||||
Float difficult = contentObj.getFloat("difficult");
|
||||
SimulationTask simulationTask = simulationTaskService.lambdaQuery().eq(SimulationTask::getUuid, taskId).one();
|
||||
if(simulationTask != null)
|
||||
{
|
||||
if (simulationTask != null) {
|
||||
// 审批通过 部署流程 保存部署流程部署id和流程定义id
|
||||
if (NumberConstants.TWO == approveStatus) {
|
||||
simulationTask.setDifficult(difficult);
|
||||
@@ -985,7 +973,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
return response;
|
||||
}
|
||||
|
||||
private String launchParamApprove(String templateId, String templateName,String approveContents,int approveAction) {
|
||||
private String launchParamApprove(String templateId, String templateName, String approveContents, int approveAction) {
|
||||
LaunchApproveReq req = new LaunchApproveReq();
|
||||
req.templateId = templateId;
|
||||
req.templateName = templateName;
|
||||
@@ -996,9 +984,9 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
req.approveTitle = "交付物评审";
|
||||
req.tenantId = ThreadLocalContext.getTenantId();
|
||||
req.userId = ThreadLocalContext.getUserId();
|
||||
req.creator = ThreadLocalContext.getUserId();
|
||||
req.creator = ThreadLocalContext.getUserId();
|
||||
SdmResponse response = approveFeignClient.launchApproval(req);
|
||||
if(response.isSuccess()){
|
||||
if (response.isSuccess()) {
|
||||
// 成功
|
||||
String cidFlowId = Optional.ofNullable(response.getData())
|
||||
.map(Object::toString)
|
||||
@@ -1050,7 +1038,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
// 删除结果
|
||||
simulationKeyResultService.lambdaUpdate().eq(SimulationRunKeyResult::getUuid, req.getUuid()).remove();
|
||||
// 删除文件
|
||||
DelDirReq delDirReq = new DelDirReq();
|
||||
DelDirReq delDirReq = new DelDirReq();
|
||||
delDirReq.setDelDirId(req.getFileId());
|
||||
log.info("删除关键结果调用删除文件夹的参数为:{}", req);
|
||||
SdmResponse response = dataFeignClient.delDir(delDirReq);
|
||||
@@ -1079,7 +1067,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
queryDirReq.setUuid(req.getRunId());
|
||||
queryDirReq.setCurrent(1);
|
||||
queryDirReq.setSize(10);
|
||||
SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> sdmResponse = queryRunDir(queryDirReq);
|
||||
SdmResponse<PageDataResp<List<FileMetadataInfoResp>>> sdmResponse = queryRunDir(queryDirReq);
|
||||
if (sdmResponse.getData() != null) {
|
||||
List<FileMetadataInfoResp> fileMetadataInfoRespList = sdmResponse.getData().getData();
|
||||
if (CollectionUtils.isNotEmpty(fileMetadataInfoRespList)) {
|
||||
@@ -1093,112 +1081,111 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
|
||||
@Override
|
||||
public void generateReport(SpdmReportReq req, HttpServletResponse response) {
|
||||
log.info("生成自动化报告参数为:{}",req);
|
||||
log.info("生成自动化报告参数为:{}", req);
|
||||
// 父节点信息
|
||||
ProjecInfoReq projecInfoReq = req.getProjecInfoReq();
|
||||
if (ObjectUtils.isEmpty(projecInfoReq) || StringUtils.isBlank(projecInfoReq.getLoadcaseName())) {
|
||||
log.error("父节点信息不能为空");
|
||||
return;
|
||||
}
|
||||
// 根据文件id下载文件到临时目录
|
||||
List<Long> imageFileIdList = req.getImageFileIdList();
|
||||
String randomId = RandomUtil.generateString(16);
|
||||
if (CollectionUtils.isNotEmpty(imageFileIdList)) {
|
||||
String randomId = RandomUtil.generateString(16);
|
||||
log.info("临时路径为:{}" , randomId);
|
||||
String commands = "python /opt/script/exportWord.py " + TEMP_REPORT_PATH + randomId + File.separator + TEMPLATE_PATH;
|
||||
for (Long fileId : imageFileIdList) {
|
||||
dataFeignClient.downloadFileToLocal(fileId, TEMP_REPORT_PATH + randomId);
|
||||
}
|
||||
// 生成projectInfo.json
|
||||
// 父节点信息
|
||||
ProjecInfoReq projecInfoReq = req.getProjecInfoReq();
|
||||
if (ObjectUtils.isNotEmpty(projecInfoReq)) {
|
||||
projecInfoReq.setReportCommand(commands);
|
||||
String jsonString = JSON.toJSONString(projecInfoReq);
|
||||
FileOutputStream projectInfoOutputStream = null;
|
||||
try {
|
||||
projectInfoOutputStream = new FileOutputStream(TEMP_REPORT_PATH + randomId + File.separator + "projectInfo.json");
|
||||
projectInfoOutputStream.write(jsonString.getBytes(StandardCharsets.UTF_8));
|
||||
projectInfoOutputStream.flush();
|
||||
projectInfoOutputStream.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// 生成performance.json
|
||||
List<SimulationPerformance> performanceList = req.getPerformanceList();
|
||||
// List<JSONObject> exportPerformanceList = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(performanceList)) {
|
||||
JSONObject allPerformanceInfoJson = new JSONObject();
|
||||
for (SimulationPerformance performance : performanceList) {
|
||||
PerformanceInfoReq performanceInfoReq = new PerformanceInfoReq();
|
||||
performanceInfoReq.setValue(StringUtils.isNotBlank(performance.getTargetValue()) ? performance.getTargetValue() : "");
|
||||
performanceInfoReq.setEnglishName(StringUtils.isNotBlank(performance.getEnglishName()) ? performance.getEnglishName() : "");
|
||||
performanceInfoReq.setHighValue(StringUtils.isNotBlank(performance.getHighValue()) ? performance.getHighValue() : "");
|
||||
performanceInfoReq.setPerformanceName(StringUtils.isNotBlank(performance.getPerformanceName()) ? performance.getPerformanceName() : "");
|
||||
performanceInfoReq.setMethod(StringUtils.isNotBlank(performance.getMethod()) ? performance.getMethod() : "");
|
||||
performanceInfoReq.setUnit(StringUtils.isNotBlank(performance.getUnit()) ? performance.getUnit() : "");
|
||||
allPerformanceInfoJson.put(performanceInfoReq.getEnglishName(),performanceInfoReq);
|
||||
}
|
||||
allPerformanceInfoJson.put("type","insert");
|
||||
// exportPerformanceList.add(jsonObject);
|
||||
String jsonString = JSON.toJSONString(allPerformanceInfoJson);
|
||||
FileOutputStream performanceOutputStream = null;
|
||||
try {
|
||||
performanceOutputStream = new FileOutputStream(TEMP_REPORT_PATH + randomId + File.separator + "performance.json");
|
||||
performanceOutputStream.write(jsonString.getBytes(StandardCharsets.UTF_8));
|
||||
performanceOutputStream.flush();
|
||||
performanceOutputStream.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 调用脚本
|
||||
log.info("调用脚本中。。。。。。");
|
||||
log.info("command:" + commands);
|
||||
List<String> 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" + File.separator + "report.docx");
|
||||
byte[] fileData = fileInputStream.readAllBytes();
|
||||
// 设置响应头
|
||||
response.reset();
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
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));
|
||||
}
|
||||
log.info("临时路径为:{}", randomId);
|
||||
String commands = "python /opt/script/exportWord.py " + TEMP_REPORT_PATH + randomId + File.separator + TEMPLATE_PATH + projecInfoReq.getLoadcaseName() + "Analyse";
|
||||
// 生成projectInfo.json
|
||||
projecInfoReq.setReportCommand(commands);
|
||||
FileOutputStream projectInfoOutputStream = null;
|
||||
try {
|
||||
projectInfoOutputStream = new FileOutputStream(TEMP_REPORT_PATH + randomId + File.separator + "projectInfo.json");
|
||||
projectInfoOutputStream.write(JSON.toJSONString(projecInfoReq).getBytes(StandardCharsets.UTF_8));
|
||||
projectInfoOutputStream.flush();
|
||||
projectInfoOutputStream.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// 生成performance.json
|
||||
List<SimulationPerformance> performanceList = req.getPerformanceList();
|
||||
if (CollectionUtils.isNotEmpty(performanceList)) {
|
||||
JSONObject allPerformanceInfoJson = new JSONObject();
|
||||
for (SimulationPerformance performance : performanceList) {
|
||||
PerformanceInfoReq performanceInfoReq = new PerformanceInfoReq();
|
||||
performanceInfoReq.setValue(StringUtils.isNotBlank(performance.getTargetValue()) ? performance.getTargetValue() : "");
|
||||
performanceInfoReq.setEnglishName(StringUtils.isNotBlank(performance.getEnglishName()) ? performance.getEnglishName() : "");
|
||||
performanceInfoReq.setHighValue(StringUtils.isNotBlank(performance.getHighValue()) ? performance.getHighValue() : "");
|
||||
performanceInfoReq.setPerformanceName(StringUtils.isNotBlank(performance.getPerformanceName()) ? performance.getPerformanceName() : "");
|
||||
performanceInfoReq.setMethod(StringUtils.isNotBlank(performance.getMethod()) ? performance.getMethod() : "");
|
||||
performanceInfoReq.setUnit(StringUtils.isNotBlank(performance.getUnit()) ? performance.getUnit() : "");
|
||||
allPerformanceInfoJson.put(performanceInfoReq.getEnglishName(), performanceInfoReq);
|
||||
}
|
||||
allPerformanceInfoJson.put("type", "insert");
|
||||
FileOutputStream performanceOutputStream = null;
|
||||
try {
|
||||
performanceOutputStream = new FileOutputStream(TEMP_REPORT_PATH + randomId + File.separator + "performance.json");
|
||||
performanceOutputStream.write(JSON.toJSONString(allPerformanceInfoJson).getBytes(StandardCharsets.UTF_8));
|
||||
performanceOutputStream.flush();
|
||||
performanceOutputStream.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 调用脚本
|
||||
log.info("调用脚本中。。。。。。");
|
||||
log.info("command:" + commands);
|
||||
List<String> 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" + File.separator + "report.docx");
|
||||
byte[] fileData = fileInputStream.readAllBytes();
|
||||
// 设置响应头
|
||||
response.reset();
|
||||
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1218,7 +1205,7 @@ public class SimulationRunServiceImpl extends ServiceImpl<SimulationRunMapper, S
|
||||
|
||||
@Override
|
||||
public SdmResponse saveNodeParams(SpdmNodeParamReq req) {
|
||||
SimulationRun simulationRun = this.lambdaQuery().eq(SimulationRun::getUuid, req.getRunId()).one();
|
||||
SimulationRun simulationRun = this.lambdaQuery().eq(SimulationRun::getUuid, req.getRunId()).one();
|
||||
SdmResponse<FlowTemplateResp> flowTemplateResp = flowFeignClient.queryFlowTemplateInfo(simulationRun.getFlowTemplate());
|
||||
if (flowTemplateResp.getData() != null) {
|
||||
ProcessDefinitionDTO definitionDTO = JSON.parseObject(flowTemplateResp.getData().getTemplateContent(), ProcessDefinitionDTO.class);
|
||||
|
||||
Reference in New Issue
Block a user