fix(接口测试): 资源池获取执行脚本重试,导致报告重复生成

This commit is contained in:
AgAngle 2024-11-21 11:41:11 +08:00 committed by Craftsman
parent 417d17f99c
commit 14d364a902
19 changed files with 156 additions and 150 deletions

View File

@ -1,7 +1,6 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.domain.ApiTestCase; import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.domain.ApiTestCaseRecord;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker; import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.mapper.ApiTestCaseMapper; import io.metersphere.api.mapper.ApiTestCaseMapper;
import io.metersphere.api.service.definition.ApiTestCaseBatchRunService; import io.metersphere.api.service.definition.ApiTestCaseBatchRunService;
@ -15,11 +14,11 @@ import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -68,12 +67,17 @@ public class ApiCaseExecuteCallbackService implements ApiExecuteCallbackService
private String initReport(GetRunScriptRequest request, ApiTestCase apiTestCase) { private String initReport(GetRunScriptRequest request, ApiTestCase apiTestCase) {
TaskItem taskItem = request.getTaskItem(); TaskItem taskItem = request.getTaskItem();
String reportId = taskItem.getReportId(); String reportId = taskItem.getReportId();
if (BooleanUtils.isTrue(request.getBatch()) && !request.getRunModeConfig().isIntegratedReport()) { try {
// 批量执行生成独立报告 if (BooleanUtils.isTrue(request.getBatch()) && !request.getRunModeConfig().isIntegratedReport()) {
reportId = apiTestCaseBatchRunService.initApiReport(taskItem.getId(), request.getRunModeConfig(), apiTestCase, request.getUserId()); // 批量执行生成独立报告
} else if (BooleanUtils.isFalse(request.getBatch()) && !ApiExecuteRunMode.isDebug(request.getRunMode())) { reportId = apiTestCaseBatchRunService.initApiReport(taskItem.getId(), taskItem.getReportId(), request.getRunModeConfig(), apiTestCase, request.getUserId());
// 单用例执行非调试生成报告 } else if (BooleanUtils.isFalse(request.getBatch()) && !ApiExecuteRunMode.isDebug(request.getRunMode())) {
return apiTestCaseRunService.initApiReport(taskItem.getId(), apiTestCase, request); // 单用例执行非调试生成报告
return apiTestCaseRunService.initApiReport(taskItem.getId(), apiTestCase, request);
}
} catch (DuplicateKeyException e) {
// 避免重试报告ID重复导致执行失败
LogUtils.error(e);
} }
return reportId; return reportId;
} }

View File

@ -19,7 +19,9 @@ public interface ApiExecuteCallbackService {
/** /**
* 解析并返回执行脚本 * 解析并返回执行脚本
*/ */
String initReport(GetRunScriptRequest request); default String initReport(GetRunScriptRequest request) {
return request.getTaskItem().getReportId();
}
/** /**
* 串行时执行下一个任务 * 串行时执行下一个任务

View File

@ -1,16 +1,11 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker; import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.api.utils.TaskRunningCache; import io.metersphere.api.utils.TaskRunningCache;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ApiExecuteRunMode; import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest; import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult; import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem; import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.EnumValidator;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -24,11 +19,6 @@ import java.util.Optional;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class ApiExecuteResourceService { public class ApiExecuteResourceService {
@Resource
private ApiReportService apiReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource @Resource
private StringRedisTemplate stringRedisTemplate; private StringRedisTemplate stringRedisTemplate;
@Resource @Resource
@ -64,12 +54,8 @@ public class ApiExecuteResourceService {
} }
private void updateRunningReportStatus(GetRunScriptRequest request) { private void updateRunningReportStatus(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
String taskId = request.getTaskId(); String taskId = request.getTaskId();
String reportId = taskItem.getReportId();
ApiExecuteResourceType apiExecuteResourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, request.getResourceType());
// 重跑不更新任务状态 // 重跑不更新任务状态
if (BooleanUtils.isFalse(request.getRerun())) { if (BooleanUtils.isFalse(request.getRerun())) {
if (request.getBatch()) { if (request.getBatch()) {
@ -86,14 +72,5 @@ public class ApiExecuteResourceService {
// 更新任务项状态 // 更新任务项状态
apiCommonService.updateTaskItemRunningStatus(request); apiCommonService.updateTaskItemRunningStatus(request);
// 非调试执行更新报告状态
switch (apiExecuteResourceType) {
case API_SCENARIO, TEST_PLAN_API_SCENARIO, PLAN_RUN_API_SCENARIO ->
apiScenarioReportService.updateReportRunningStatus(reportId);
case API_CASE, TEST_PLAN_API_CASE, PLAN_RUN_API_CASE ->
apiReportService.updateReportRunningStatus(reportId);
default -> throw new MSException("不支持的资源类型: " + request.getResourceType());
}
} }
} }

View File

@ -40,6 +40,7 @@ import io.metersphere.system.dto.pool.TestResourceNodeDTO;
import io.metersphere.system.dto.pool.TestResourcePoolReturnDTO; import io.metersphere.system.dto.pool.TestResourcePoolReturnDTO;
import io.metersphere.system.mapper.ExecTaskItemMapper; import io.metersphere.system.mapper.ExecTaskItemMapper;
import io.metersphere.system.service.*; import io.metersphere.system.service.*;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@ -214,6 +215,7 @@ public class ApiExecuteService {
*/ */
public TaskRequestDTO execute(TaskRequestDTO taskRequest) { public TaskRequestDTO execute(TaskRequestDTO taskRequest) {
TaskInfo taskInfo = taskRequest.getTaskInfo(); TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
try { try {
taskInfo = setTaskRequestParams(taskInfo); taskInfo = setTaskRequestParams(taskInfo);
@ -226,6 +228,11 @@ public class ApiExecuteService {
} }
taskInfo.setPoolId(testResourcePoolDTO.getId()); taskInfo.setPoolId(testResourcePoolDTO.getId());
if (StringUtils.isBlank(taskItem.getReportId())) {
// 预先生成报告ID避免资源池获取执行脚本时超时重试导致数据重复创建
taskItem.setReportId(IDGenerator.nextStr());
}
// 判断是否为 K8S 资源池 // 判断是否为 K8S 资源池
boolean isK8SResourcePool = StringUtils.equals(testResourcePoolDTO.getType(), ResourcePoolTypeEnum.K8S.getName()); boolean isK8SResourcePool = StringUtils.equals(testResourcePoolDTO.getType(), ResourcePoolTypeEnum.K8S.getName());
if (isK8SResourcePool) { if (isK8SResourcePool) {
@ -360,6 +367,12 @@ public class ApiExecuteService {
taskInfo.setMsUrl(testResourcePool.getServerUrl()); taskInfo.setMsUrl(testResourcePool.getServerUrl());
} }
taskInfo.setPoolId(testResourcePool.getId()); taskInfo.setPoolId(testResourcePool.getId());
taskRequest.getTaskItems().forEach(taskItem -> {
if (StringUtils.isBlank(taskItem.getReportId())) {
// 预先生成报告ID避免资源池获取执行脚本时超时重试导致数据重复创建
taskItem.setReportId(IDGenerator.nextStr());
}
});
// 判断是否为 K8S 资源池 // 判断是否为 K8S 资源池
boolean isK8SResourcePool = StringUtils.equals(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.getName()); boolean isK8SResourcePool = StringUtils.equals(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.getName());

View File

@ -12,9 +12,10 @@ import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem; import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -46,34 +47,33 @@ public class ApiScenarioExecuteCallbackService implements ApiExecuteCallbackServ
return result; return result;
} }
@Override
public String initReport(GetRunScriptRequest request) {
String reportId = request.getTaskItem().getReportId();
// reportId 不为空则已经预生成了报告
if (StringUtils.isBlank(reportId)) {
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(request.getTaskItem().getResourceId());
return initReport(request, apiScenarioDetail);
}
return reportId;
}
private String initReport(GetRunScriptRequest request, ApiScenarioDetail apiScenarioDetail) { private String initReport(GetRunScriptRequest request, ApiScenarioDetail apiScenarioDetail) {
TaskItem taskItem = request.getTaskItem(); TaskItem taskItem = request.getTaskItem();
String reportId = taskItem.getReportId(); String reportId = taskItem.getReportId();
if (BooleanUtils.isTrue(request.getBatch())) { try {
if (request.getRunModeConfig().isIntegratedReport()) { if (BooleanUtils.isTrue(request.getBatch())) {
// 集合报告生成一级步骤的子步骤 if (request.getRunModeConfig().isIntegratedReport()) {
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), reportId); // 集合报告生成一级步骤的子步骤
} else { apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), reportId);
// 批量执行生成独立报告 } else {
reportId = apiScenarioBatchRunService.initScenarioReport(taskItem.getId(), request.getRunModeConfig(), apiScenarioDetail, request.getUserId()); // 批量执行生成独立报告
reportId = apiScenarioBatchRunService.initScenarioReport(taskItem.getId(), reportId, request.getRunModeConfig(), apiScenarioDetail, request.getUserId());
// 初始化报告步骤
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}
} else if (!ApiExecuteRunMode.isDebug(request.getRunMode())) {
reportId = apiScenarioRunService.initApiScenarioReport(taskItem.getId(), apiScenarioDetail, request);
// 初始化报告步骤 // 初始化报告步骤
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId); apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
} }
} else if (!ApiExecuteRunMode.isDebug(request.getRunMode())) { } catch (DuplicateKeyException e) {
reportId = apiScenarioRunService.initApiScenarioReport(taskItem.getId(), apiScenarioDetail, request); if (!request.getRunModeConfig().isIntegratedReport()) {
// 初始化报告步骤 // 步骤中的 stepId 是执行时时随机生成的如果重试需要删除原有的步骤重新生成跟执行脚本匹配
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId); apiScenarioRunService.deleteStepsByReportId(reportId);
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}
// 避免重试报告ID重复导致执行失败
LogUtils.error(e);
} }
return reportId; return reportId;
} }

View File

@ -24,6 +24,7 @@ import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
@ -427,10 +428,12 @@ public class ApiTestCaseBatchRunService {
* @param apiTestCase * @param apiTestCase
* @return * @return
*/ */
public String initApiReport(String taskItemId, ApiRunModeConfigDTO runModeConfig, @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiReport(String taskItemId, String reportId, ApiRunModeConfigDTO runModeConfig,
ApiTestCase apiTestCase, String userId) { ApiTestCase apiTestCase, String userId) {
// 初始化报告 // 初始化报告
ApiReport apiReport = getApiReport(runModeConfig, apiTestCase, userId); ApiReport apiReport = getApiReport(runModeConfig, apiTestCase, userId);
apiReport.setId(reportId);
apiReportService.insertApiReport(apiReport); apiReportService.insertApiReport(apiReport);
return apiTestCaseRunService.initApiReportDetail(taskItemId, apiTestCase, apiReport.getId()); return apiTestCaseRunService.initApiReportDetail(taskItemId, apiTestCase, apiReport.getId());
} }

View File

@ -24,6 +24,7 @@ import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.UUID; import java.util.UUID;
@ -252,6 +253,7 @@ public class ApiTestCaseRunService {
* @param apiTestCase * @param apiTestCase
* @return * @return
*/ */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiReport(String taskItemId, ApiTestCase apiTestCase, GetRunScriptRequest request) { public String initApiReport(String taskItemId, ApiTestCase apiTestCase, GetRunScriptRequest request) {
// 初始化报告 // 初始化报告
ApiReport apiReport = getApiReport(apiTestCase, request); ApiReport apiReport = getApiReport(apiTestCase, request);

View File

@ -25,12 +25,12 @@ import io.metersphere.system.domain.ExecTask;
import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.domain.ExecTaskItem;
import io.metersphere.system.mapper.ExtExecTaskItemMapper; import io.metersphere.system.mapper.ExtExecTaskItemMapper;
import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.service.BaseTaskHubService;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
@ -374,11 +374,12 @@ public class ApiScenarioBatchRunService {
return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo); return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo);
} }
public String initScenarioReport(String taskItemId, ApiRunModeConfigDTO runModeConfig, @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initScenarioReport(String taskItemId, String reportId, ApiRunModeConfigDTO runModeConfig,
ApiScenario apiScenario, String userId) { ApiScenario apiScenario, String userId) {
// 初始化报告 // 初始化报告
ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, apiScenario, userId); ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, apiScenario, userId);
apiScenarioReport.setId(IDGenerator.nextStr()); apiScenarioReport.setId(reportId);
apiScenarioReportService.insertApiScenarioReport(apiScenarioReport); apiScenarioReportService.insertApiScenarioReport(apiScenarioReport);
return apiScenarioRunService.initApiScenarioReportDetail(taskItemId, apiScenario.getId(), apiScenarioReport.getId()); return apiScenarioRunService.initApiScenarioReportDetail(taskItemId, apiScenario.getId(), apiScenarioReport.getId());
} }

View File

@ -414,33 +414,6 @@ public class ApiScenarioReportService {
return apiReportDetails; return apiReportDetails;
} }
/**
* 更新进行中的报告状态和开始执行时间
*
* @param reportId
*/
public void updateReportRunningStatus(String reportId) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(reportId);
scenarioReport.setExecStatus(ExecStatus.RUNNING.name());
scenarioReport.setStartTime(System.currentTimeMillis());
scenarioReport.setUpdateTime(System.currentTimeMillis());
apiScenarioReportMapper.updateByPrimaryKeySelective(scenarioReport);
}
/**
* 更新执行中的场景报告
*
* @param reportId
*/
public void updateReportStatus(String reportId, String status) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(reportId);
scenarioReport.setExecStatus(status);
scenarioReport.setUpdateTime(System.currentTimeMillis());
apiScenarioReportMapper.updateByPrimaryKeySelective(scenarioReport);
}
public List<ApiScenarioReport> getApiScenarioReportByIds(List<String> reportIds) { public List<ApiScenarioReport> getApiScenarioReportByIds(List<String> reportIds) {
if (CollectionUtils.isEmpty(reportIds)) { if (CollectionUtils.isEmpty(reportIds)) {
return List.of(); return List.of();

View File

@ -14,6 +14,7 @@ import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.ApiScenarioBlobMapper; import io.metersphere.api.mapper.ApiScenarioBlobMapper;
import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.mapper.ApiScenarioMapper;
import io.metersphere.api.mapper.ApiScenarioReportMapper; import io.metersphere.api.mapper.ApiScenarioReportMapper;
import io.metersphere.api.mapper.ApiScenarioReportStepMapper;
import io.metersphere.api.parser.step.StepParser; import io.metersphere.api.parser.step.StepParser;
import io.metersphere.api.parser.step.StepParserFactory; import io.metersphere.api.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiCommonService;
@ -50,6 +51,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
@ -92,6 +94,8 @@ public class ApiScenarioRunService {
private ProjectMapper projectMapper; private ProjectMapper projectMapper;
@Resource @Resource
private BaseTaskHubService baseTaskHubService; private BaseTaskHubService baseTaskHubService;
@Resource
private ApiScenarioReportStepMapper apiScenarioReportStepMapper;
public TaskRequestDTO run(String id, String reportId, String userId) { public TaskRequestDTO run(String id, String reportId, String userId) {
ApiScenarioDetail apiScenarioDetail = getForRun(id); ApiScenarioDetail apiScenarioDetail = getForRun(id);
@ -234,21 +238,21 @@ public class ApiScenarioRunService {
} else { } else {
// 如果传了报告ID则实时获取结果 // 如果传了报告ID则实时获取结果
taskInfo.setRealTime(true); taskInfo.setRealTime(true);
// 传了报告ID则预生成报告
ApiScenarioReport scenarioReport = getScenarioReport(apiScenario, userId);
scenarioReport.setId(reportId);
scenarioReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
scenarioReport.setWaitingTime(getGlobalWaitTime(parseParam.getScenarioConfig()));
initApiScenarioReport(taskItem.getId(), apiScenario, scenarioReport);
// 初始化报告步骤
initScenarioReportSteps(steps, taskItem.getReportId());
} }
// 传了报告ID则预生成报告
ApiScenarioReport scenarioReport = getScenarioReport(apiScenario, userId);
scenarioReport.setId(reportId);
scenarioReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
scenarioReport.setWaitingTime(getGlobalWaitTime(parseParam.getScenarioConfig()));
initApiScenarioReport(taskItem.getId(), apiScenario, scenarioReport);
// 初始化报告步骤
initScenarioReportSteps(steps, taskItem.getReportId());
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo()); ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setTaskItemId(taskItem.getId()); parseConfig.setTaskItemId(taskItem.getId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig); return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
@ -464,6 +468,7 @@ public class ApiScenarioRunService {
return waitTime; return waitTime;
} }
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiScenarioReport(String taskItemId, ApiScenario apiScenario, GetRunScriptRequest request) { public String initApiScenarioReport(String taskItemId, ApiScenario apiScenario, GetRunScriptRequest request) {
// 初始化报告 // 初始化报告
ApiScenarioReport scenarioReport = getScenarioReport(apiScenario, request); ApiScenarioReport scenarioReport = getScenarioReport(apiScenario, request);
@ -590,6 +595,9 @@ public class ApiScenarioRunService {
scenarioReport.setRunMode(request.getRunMode()); scenarioReport.setRunMode(request.getRunMode());
scenarioReport.setTriggerMode(request.getTriggerMode()); scenarioReport.setTriggerMode(request.getTriggerMode());
scenarioReport.setPoolId(request.getPoolId()); scenarioReport.setPoolId(request.getPoolId());
if (StringUtils.isNotBlank(request.getTaskItem().getReportId())) {
scenarioReport.setId(request.getTaskItem().getReportId());
}
return scenarioReport; return scenarioReport;
} }
@ -1116,4 +1124,10 @@ public class ApiScenarioRunService {
apiExecuteService.execute(taskRequest); apiExecuteService.execute(taskRequest);
} }
public void deleteStepsByReportId(String reportId) {
ApiScenarioReportStepExample example = new ApiScenarioReportStepExample();
example.createCriteria().andReportIdEqualTo(reportId);
apiScenarioReportStepMapper.deleteByExample(example);
}
} }

View File

@ -57,10 +57,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -213,7 +210,8 @@ public class BaseTaskHubService {
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample(); ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
example.createCriteria().andTaskResourceIdIn(resourceIds); example.createCriteria().andTaskResourceIdIn(resourceIds);
List<ApiReportRelateTask> reportRelateTasks = apiReportRelateTaskMapper.selectByExample(example); List<ApiReportRelateTask> reportRelateTasks = apiReportRelateTaskMapper.selectByExample(example);
Map<String, String> reportMap = reportRelateTasks.stream().collect(Collectors.toMap(ApiReportRelateTask::getTaskResourceId, ApiReportRelateTask::getReportId)); Map<String, String> reportMap = new HashMap<>();
reportRelateTasks.forEach(item -> reportMap.put(item.getTaskResourceId(), item.getReportId()));
reportTasks.forEach(task -> { reportTasks.forEach(task -> {
if (integratedTaskIds.contains(task.getId())) { if (integratedTaskIds.contains(task.getId())) {
task.setReportId(reportMap.get(task.getId())); task.setReportId(reportMap.get(task.getId()));

View File

@ -17,7 +17,9 @@ import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -67,15 +69,14 @@ public class PlanRunApiCaseExecuteCallbackService implements ApiExecuteCallbackS
return result; return result;
} }
@Override
public String initReport(GetRunScriptRequest request) {
TestPlanReportApiCase testPlanReportApiCase = testPlanReportApiCaseMapper.selectByPrimaryKey(request.getTaskItem().getResourceId());
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanReportApiCase.getApiCaseId());
return planRunTestPlanApiCaseService.initApiReport(request, testPlanReportApiCase, apiTestCase);
}
public String initReport(GetRunScriptRequest request, TestPlanReportApiCase testPlanReportApiCase, ApiTestCase apiTestCase) { public String initReport(GetRunScriptRequest request, TestPlanReportApiCase testPlanReportApiCase, ApiTestCase apiTestCase) {
return planRunTestPlanApiCaseService.initApiReport(request, testPlanReportApiCase, apiTestCase); try {
return planRunTestPlanApiCaseService.initApiReport(request, testPlanReportApiCase, apiTestCase);
} catch (DuplicateKeyException e) {
// 避免重试报告ID重复导致执行失败
LogUtils.error(e);
}
return request.getTaskItem().getReportId();
} }
/** /**

View File

@ -16,7 +16,9 @@ import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -62,23 +64,25 @@ public class PlanRunApiScenarioExecuteCallbackService implements ApiExecuteCallb
return result; return result;
} }
@Override
public String initReport(GetRunScriptRequest request) {
String resourceId = request.getTaskItem().getResourceId();
TestPlanReportApiScenario testPlanReportApiScenario = testPlanReportApiScenarioMapper.selectByPrimaryKey(resourceId);
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(testPlanReportApiScenario.getApiScenarioId());
return initReport(request, testPlanReportApiScenario, apiScenarioDetail);
}
public String initReport(GetRunScriptRequest request, TestPlanReportApiScenario testPlanReportApiScenario, ApiScenarioDetail apiScenarioDetail) { public String initReport(GetRunScriptRequest request, TestPlanReportApiScenario testPlanReportApiScenario, ApiScenarioDetail apiScenarioDetail) {
String reportId = planRunTestPlanApiScenarioService.initReport(request, testPlanReportApiScenario, apiScenarioDetail); String reportId = request.getTaskItem().getReportId();
// 初始化报告步骤 try {
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId); planRunTestPlanApiScenarioService.initReport(request, testPlanReportApiScenario, apiScenarioDetail);
return reportId; // 初始化报告步骤
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
} catch (DuplicateKeyException e) {
// 避免重试报告ID重复导致执行失败
// 步骤中的 stepId 是执行时时随机生成的如果重试需要删除原有的步骤重新生成跟执行脚本匹配
apiScenarioRunService.deleteStepsByReportId(reportId);
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
LogUtils.error(e);
}
return request.getTaskItem().getReportId();
} }
/** /**
* 串行时执行下一个任务 * 串行时执行下一个任务
*
* @param queue * @param queue
* @param queueDetail * @param queueDetail
*/ */
@ -90,6 +94,7 @@ public class PlanRunApiScenarioExecuteCallbackService implements ApiExecuteCallb
/** /**
* 批量串行的测试集执行时 * 批量串行的测试集执行时
* 测试集下用例执行完成时回调 * 测试集下用例执行完成时回调
*
* @param apiNoticeDTO * @param apiNoticeDTO
*/ */
@Override @Override

View File

@ -27,6 +27,7 @@ import io.metersphere.system.domain.ExecTaskItem;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
@ -182,6 +183,7 @@ public class PlanRunTestPlanApiCaseService {
* *
* @return * @return
*/ */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiReport(GetRunScriptRequest request, TestPlanReportApiCase testPlanReportApiCase, ApiTestCase apiTestCase) { public String initApiReport(GetRunScriptRequest request, TestPlanReportApiCase testPlanReportApiCase, ApiTestCase apiTestCase) {
// 初始化报告 // 初始化报告
ApiReport apiReport = apiTestCaseRunService.getApiReport(apiTestCase, request); ApiReport apiReport = apiTestCaseRunService.getApiReport(apiTestCase, request);

View File

@ -28,6 +28,7 @@ import io.metersphere.system.domain.ExecTaskItem;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
@ -138,6 +139,7 @@ public class PlanRunTestPlanApiScenarioService {
return false; return false;
} }
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initReport(GetRunScriptRequest request, public String initReport(GetRunScriptRequest request,
TestPlanReportApiScenario testPlanReportApiScenario, TestPlanReportApiScenario testPlanReportApiScenario,
ApiScenario apiScenario) { ApiScenario apiScenario) {

View File

@ -18,8 +18,10 @@ import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -64,20 +66,19 @@ public class TestPlanApiCaseExecuteCallbackService implements ApiExecuteCallback
return result; return result;
} }
@Override
public String initReport(GetRunScriptRequest request) {
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(request.getTaskItem().getResourceId());
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
return initReport(request, testPlanApiCase, apiTestCase);
}
public String initReport(GetRunScriptRequest request, TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase) { public String initReport(GetRunScriptRequest request, TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase) {
ApiTestCaseRecord apiTestCaseRecord = testPlanApiCaseService.initApiReport(apiTestCase, testPlanApiCase, request); try {
return apiTestCaseRecord.getApiReportId(); return testPlanApiCaseService.initApiReport(apiTestCase, testPlanApiCase, request);
} catch (DuplicateKeyException e) {
// 避免重试报告ID重复导致执行失败
LogUtils.error(e);
}
return request.getTaskItem().getReportId();
} }
/** /**
* 串行时执行下一个任务 * 串行时执行下一个任务
*
* @param queue * @param queue
* @param queueDetail * @param queueDetail
*/ */
@ -95,7 +96,7 @@ public class TestPlanApiCaseExecuteCallbackService implements ApiExecuteCallback
if (StringUtils.isNotBlank(apiNoticeDTO.getParentQueueId())) { if (StringUtils.isNotBlank(apiNoticeDTO.getParentQueueId())) {
testPlanApiCaseBatchRunService.executeNextCollection(apiNoticeDTO.getParentQueueId(), apiNoticeDTO.getRerun()); testPlanApiCaseBatchRunService.executeNextCollection(apiNoticeDTO.getParentQueueId(), apiNoticeDTO.getRerun());
} else if (StringUtils.isNotBlank(apiNoticeDTO.getParentSetId())) { } else if (StringUtils.isNotBlank(apiNoticeDTO.getParentSetId())) {
String queueIdOrSetId = StringUtils.isBlank(apiNoticeDTO.getQueueId()) ? apiNoticeDTO.getSetId() : apiNoticeDTO.getQueueId(); String queueIdOrSetId = StringUtils.isBlank(apiNoticeDTO.getQueueId()) ? apiNoticeDTO.getSetId() : apiNoticeDTO.getQueueId();
String[] setIdSplit = queueIdOrSetId.split("_"); String[] setIdSplit = queueIdOrSetId.split("_");
String collectionId = setIdSplit[setIdSplit.length - 1]; String collectionId = setIdSplit[setIdSplit.length - 1];
testPlanApiCaseBatchRunService.finishParallelCollection(apiNoticeDTO.getParentSetId(), collectionId); testPlanApiCaseBatchRunService.finishParallelCollection(apiNoticeDTO.getParentSetId(), collectionId);
@ -104,6 +105,7 @@ public class TestPlanApiCaseExecuteCallbackService implements ApiExecuteCallback
/** /**
* 失败停止时删除测试集合队列 * 失败停止时删除测试集合队列
*
* @param parentQueueId * @param parentQueueId
*/ */
@Override @Override

View File

@ -63,6 +63,7 @@ import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -831,7 +832,8 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
* *
* @return * @return
*/ */
public ApiTestCaseRecord initApiReport(ApiTestCase apiTestCase, TestPlanApiCase testPlanApiCase, GetRunScriptRequest request) { @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiReport(ApiTestCase apiTestCase, TestPlanApiCase testPlanApiCase, GetRunScriptRequest request) {
// 初始化报告 // 初始化报告
ApiRunModeConfigDTO runModeConfig = request.getRunModeConfig(); ApiRunModeConfigDTO runModeConfig = request.getRunModeConfig();
ApiReport apiReport = apiTestCaseRunService.getApiReport(apiTestCase, request); ApiReport apiReport = apiTestCaseRunService.getApiReport(apiTestCase, request);
@ -847,7 +849,7 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
ApiReportStep apiReportStep = getApiReportStep(testPlanApiCase, apiTestCase, apiReport.getId()); ApiReportStep apiReportStep = getApiReportStep(testPlanApiCase, apiTestCase, apiReport.getId());
apiReportService.insertApiReportDetail(apiReportStep, apiTestCaseRecord, apiReportRelateTask); apiReportService.insertApiReportDetail(apiReportStep, apiTestCaseRecord, apiReportRelateTask);
return apiTestCaseRecord; return apiTestCaseRecord.getApiReportId();
} }
public ApiReportStep getApiReportStep(TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase, String reportId) { public ApiReportStep getApiReportStep(TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase, String reportId) {

View File

@ -17,8 +17,10 @@ import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -64,20 +66,21 @@ public class TestPlanApiScenarioExecuteCallbackService implements ApiExecuteCall
return result; return result;
} }
@Override
public String initReport(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(taskItem.getResourceId());
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(testPlanApiScenario.getApiScenarioId());
return initReport(request, testPlanApiScenario, apiScenarioDetail);
}
public String initReport(GetRunScriptRequest request, TestPlanApiScenario testPlanApiScenario, ApiScenarioDetail apiScenarioDetail) { public String initReport(GetRunScriptRequest request, TestPlanApiScenario testPlanApiScenario, ApiScenarioDetail apiScenarioDetail) {
// 批量执行生成独立报告 String reportId = request.getTaskItem().getReportId();
String reportId = testPlanApiScenarioService.initApiScenarioReport(testPlanApiScenario, apiScenarioDetail, request); try {
// 初始化报告步骤 // 批量执行生成独立报告
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId); testPlanApiScenarioService.initApiScenarioReport(testPlanApiScenario, apiScenarioDetail, request);
return reportId; // 初始化报告步骤
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
} catch (DuplicateKeyException e) {
// 避免重试报告ID重复导致执行失败
// 步骤中的 stepId 是执行时时随机生成的如果重试需要删除原有的步骤重新生成跟执行脚本匹配
apiScenarioRunService.deleteStepsByReportId(reportId);
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
LogUtils.error(e);
}
return request.getTaskItem().getReportId();
} }
/** /**

View File

@ -60,6 +60,7 @@ import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -420,6 +421,7 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
* *
* @return * @return
*/ */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String initApiScenarioReport(TestPlanApiScenario testPlanApiScenario, ApiScenario apiScenario, GetRunScriptRequest request) { public String initApiScenarioReport(TestPlanApiScenario testPlanApiScenario, ApiScenario apiScenario, GetRunScriptRequest request) {
// 初始化报告 // 初始化报告
ApiRunModeConfigDTO runModeConfig = request.getRunModeConfig(); ApiRunModeConfigDTO runModeConfig = request.getRunModeConfig();