From cfa6bee5427d260877c97d8f39b24981db65a549 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Mon, 14 Oct 2024 16:21:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=B8=AD=E5=BF=83-=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --task=1016475 --user=陈建星 批量执行优化 https://www.tapd.cn/55049933/s/1589205 --- .../plugin/api/dto/ParameterConfig.java | 7 + .../sdk/constants/ApiExecuteRunMode.java | 12 +- .../sdk/dto/api/task/GetRunScriptRequest.java | 10 + .../sdk/dto/api/task/TaskInfo.java | 12 + .../sdk/dto/api/task/TaskItem.java | 6 + .../sdk/dto/queue/ExecutionQueue.java | 5 + .../sdk/dto/queue/ExecutionQueueDetail.java | 5 + .../sdk/dto/queue/TestPlanExecutionQueue.java | 6 +- .../src/main/resources/i18n/api.properties | 2 + .../main/resources/i18n/api_en_US.properties | 3 + .../main/resources/i18n/api_zh_CN.properties | 3 + .../main/resources/i18n/api_zh_TW.properties | 3 + .../ApiExecuteResourceController.java | 18 +- .../metersphere/api/dto/ApiParamConfig.java | 4 - .../api/job/ApiScenarioScheduleJob.java | 28 ++- .../api/mapper/ExtApiScenarioMapper.java | 2 + .../api/mapper/ExtApiScenarioMapper.xml | 9 +- .../api/mapper/ExtApiTestCaseMapper.java | 3 +- .../api/mapper/ExtApiTestCaseMapper.xml | 8 + .../jmeter/JmeterTestElementParser.java | 2 +- .../api/service/ApiBatchRunBaseService.java | 54 +++- .../api/service/ApiCommonService.java | 29 +++ .../service/ApiExecuteResourceService.java | 52 ++-- .../api/service/ApiExecuteService.java | 64 ++++- .../api/service/debug/ApiDebugService.java | 3 + .../definition/ApiDefinitionService.java | 2 + .../service/definition/ApiReportService.java | 4 + .../ApiTestCaseBatchRunService.java | 221 +++++++++++++---- .../definition/ApiTestCaseService.java | 32 ++- .../queue/ApiExecutionQueueService.java | 14 ++ .../scenario/ApiScenarioBatchRunService.java | 197 +++++++++++---- .../scenario/ApiScenarioRunService.java | 49 +++- .../service/scenario/ApiScenarioService.java | 6 - .../ApiExecuteResourceControllerTest.java | 23 +- .../system/mapper/ExtExecTaskItemMapper.java | 4 +- .../system/mapper/ExtExecTaskItemMapper.xml | 8 + .../system/service/BaseTaskHubService.java | 45 +++- .../BaseTaskHubControllerTests.java | 9 +- .../plan/dto/TestPlanApiCaseBatchRunDTO.java | 12 + .../plan/dto/TestPlanApiCaseDTO.java | 8 - .../dto/TestPlanApiScenarioBatchRunDTO.java | 12 + .../plan/dto/TestPlanReportGenPreParam.java | 3 + .../plan/mapper/ExtTestPlanApiCaseMapper.java | 10 +- .../plan/mapper/ExtTestPlanApiCaseMapper.xml | 21 +- .../mapper/ExtTestPlanApiScenarioMapper.java | 11 +- .../mapper/ExtTestPlanApiScenarioMapper.xml | 24 +- .../PlanRunTestPlanApiCaseService.java | 26 +- .../PlanRunTestPlanApiScenarioService.java | 55 ++++- .../TestPlanApiCaseBatchRunService.java | 220 ++++++++++++----- .../plan/service/TestPlanApiCaseService.java | 49 ++-- .../TestPlanApiScenarioBatchRunService.java | 232 +++++++++++------- .../service/TestPlanApiScenarioService.java | 52 ++-- .../plan/service/TestPlanExecuteService.java | 50 +++- .../plan/service/TestPlanReportService.java | 60 ++++- 54 files changed, 1348 insertions(+), 461 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanApiCaseBatchRunDTO.java delete mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanApiCaseDTO.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanApiScenarioBatchRunDTO.java diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java index 196b7abb16..d0367c574c 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java @@ -11,6 +11,13 @@ import java.util.Map; */ @Data public abstract class ParameterConfig { + /** + * 任务项的唯一ID + */ + private String taskItemId; + /** + * 报告ID + */ private String reportId; /** * 解析时,是否解析 enable 为 false 的组件 diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApiExecuteRunMode.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApiExecuteRunMode.java index 4ec68658a3..88ee4f511c 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApiExecuteRunMode.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApiExecuteRunMode.java @@ -1,5 +1,7 @@ package io.metersphere.sdk.constants; +import org.apache.commons.lang3.StringUtils; + /** * 接口执行时的资源类型 * @@ -27,5 +29,13 @@ public enum ApiExecuteRunMode { /** * 定时任务 */ - SCHEDULE + SCHEDULE; + + public static boolean isDebug(String runMode) { + return StringUtils.equalsAny(runMode, ApiExecuteRunMode.FRONTEND_DEBUG.name(), ApiExecuteRunMode.BACKEND_DEBUG.name()); + } + + public static boolean isFrontendDebug(String runMode) { + return StringUtils.equals(runMode, ApiExecuteRunMode.FRONTEND_DEBUG.name()); + } } diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java index 8ed73f0cda..5641295bb4 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java @@ -38,4 +38,14 @@ public class GetRunScriptRequest implements Serializable { * @see io.metersphere.sdk.constants.ApiExecuteResourceType */ private String resourceType; + /** + * 是否需要解析脚本 + * 接口详情页面,需要传试试详情,会其他解析脚本,needParseScript 为 false + * 不传详情执行时,通过 task-runner 发起解析脚本请求,needParseScript 为 true + */ + private Boolean needParseScript = false; + /** + * 线程ID + */ + private String threadId; } diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java index e31eefca05..e756dcc4a3 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java @@ -16,9 +16,21 @@ import java.util.List; public class TaskInfo implements Serializable { @Serial private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + private String taskId; private String msUrl; private String kafkaConfig; private String minioConfig; + /** + * 单个任务的并发数 + */ + private int perTaskSize; + /** + * 资源池的并发数 + */ private int poolSize; /** * 批量执行时的队列ID diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskItem.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskItem.java index d3cce51f52..123e2eeebd 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskItem.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskItem.java @@ -14,6 +14,12 @@ public class TaskItem implements Serializable { @Serial private static final long serialVersionUID = 1L; + /** + * 任务项ID + */ + @NotBlank + private String id; + @NotBlank private String reportId; /** diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueue.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueue.java index c5ef75fa49..8203f56896 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueue.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueue.java @@ -13,6 +13,11 @@ public class ExecutionQueue implements Serializable { */ private String queueId; + /** + * taskId + */ + private String taskId; + /** * 执行人 */ diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueueDetail.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueueDetail.java index 7b029b18c6..ad7aee0602 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueueDetail.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/ExecutionQueueDetail.java @@ -15,6 +15,11 @@ public class ExecutionQueueDetail implements Serializable { */ private String resourceId; + /** + * 资源id,每个资源在同一个运行队列中唯一 + */ + private String taskItemId; + /** * 排序 */ diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/TestPlanExecutionQueue.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/TestPlanExecutionQueue.java index ae232a50e3..f8a1908a1b 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/TestPlanExecutionQueue.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/queue/TestPlanExecutionQueue.java @@ -32,13 +32,16 @@ public class TestPlanExecutionQueue { private String prepareReportId; // 测试集Json private String testPlanCollectionJson; + // 任务ID + private String taskId; // 是否是队列的最后一个 private boolean isLastOne = false; // 是否执行完毕 private boolean executeFinish = false; - public TestPlanExecutionQueue(long pos, String createUser, long createTime, String queueId, String queueType, String parentQueueId, String parentQueueType, String sourceID, String runMode, String executionSource, String prepareReportId) { + public TestPlanExecutionQueue(long pos, String createUser, long createTime, String queueId, String queueType, String parentQueueId, String parentQueueType, String sourceID, String runMode, + String executionSource, String prepareReportId, String taskId) { this.pos = pos; this.createUser = createUser; this.createTime = createTime; @@ -50,5 +53,6 @@ public class TestPlanExecutionQueue { this.runMode = runMode; this.executionSource = executionSource; this.prepareReportId = prepareReportId; + this.taskId = taskId; } } diff --git a/backend/framework/sdk/src/main/resources/i18n/api.properties b/backend/framework/sdk/src/main/resources/i18n/api.properties index 2a544dd87d..9b5d51d44b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api.properties @@ -448,6 +448,8 @@ api_definition.status.continuous=连调中 api_test_case.clear.api_change=忽略本次变更差异 api_test_case.ignore.api_change=忽略全部变更差异 +api_batch_task_name=用例批量执行任务 +api_scenario_batch_task_name=场景批量执行 # api doc share i18n api_doc_share.not_exist=接口文档分享不存在 api_doc_share.id.not_blank=主键不能为空 diff --git a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties index 9ec1d1de8b..9bc9a1c1bd 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties @@ -458,6 +458,9 @@ curl_script_is_empty=Curl script cannot be empty curl_script_is_invalid=Curl script is invalid curl_raw_content_is_invalid=Raw content is invalid +api_batch_task_name=Api cases batch task +api_scenario_batch_task_name=Scenarios batch task + # api doc share i18n api_doc_share.not_exist=api doc share not exist api_doc_share.id.not_blank=id cannot be empty diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties index f104287307..ca428203ba 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties @@ -426,6 +426,9 @@ curl_script_is_empty=cURL脚本不能为空 curl_script_is_invalid=cURL脚本格式不正确 curl_raw_content_is_invalid=raw内容格式不正确 +api_batch_task_name=用例批量执行任务 +api_scenario_batch_task_name=场景批量执行 + # api doc share i18n api_doc_share.not_exist=接口文档分享不存在 api_doc_share.id.not_blank=主键不能为空 diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties index e028092ea4..5e44302fad 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties @@ -426,6 +426,9 @@ curl_script_is_empty=curl脚本不能爲空 curl_script_is_invalid=curl脚本格式不正確 curl_raw_content_is_invalid=raw内容格式不正確 +api_batch_task_name=用例批量執行任務 +api_scenario_batch_task_name=場景批量執行 + # api doc share i18n api_doc_share.not_exist=接口文檔分享不存在 api_doc_share.id.not_blank=主键不能為空 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiExecuteResourceController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiExecuteResourceController.java index d56ef14604..7192d21d06 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiExecuteResourceController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiExecuteResourceController.java @@ -24,19 +24,6 @@ public class ApiExecuteResourceController { @Resource private ApiExecuteResourceService apiExecuteResourceService; - /** - * 获取执行脚本 - * - * @param reportId - * @param testId - * @return - */ - @GetMapping("script") - @Operation(summary = "获取执行脚本") - public String getScript(@RequestParam("reportId") String reportId, @RequestParam("testId") String testId) { - return apiExecuteResourceService.getRunScript(reportId, testId); - } - /** * 获取执行脚本 * @@ -55,11 +42,10 @@ public class ApiExecuteResourceController { */ @PostMapping("/file") @Operation(summary = "下载执行所需的文件") - public void downloadFile(@RequestParam("reportId") String reportId, - @RequestParam("testId") String testId, + public void downloadFile(@RequestParam("taskItemId") String taskItemId, @RequestBody FileRequest fileRequest, HttpServletResponse response) throws Exception { - apiExecuteService.downloadFile(reportId, testId, fileRequest, response); + apiExecuteService.downloadFile(taskItemId, fileRequest, response); } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java index abcd457a8c..cb60a7208d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java @@ -17,10 +17,6 @@ import java.util.Map; */ @Data public class ApiParamConfig extends ParameterConfig { - /** - * 报告ID - */ - private String reportId; /** * 使用全局cookie */ diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/job/ApiScenarioScheduleJob.java b/backend/services/api-test/src/main/java/io/metersphere/api/job/ApiScenarioScheduleJob.java index 52b2cfffc4..5e428651f8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/job/ApiScenarioScheduleJob.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/job/ApiScenarioScheduleJob.java @@ -7,11 +7,12 @@ import io.metersphere.api.dto.debug.ApiResourceRunRequest; import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.scenario.ApiScenarioDetail; import io.metersphere.api.dto.scenario.ApiScenarioParseParam; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.scenario.ApiScenarioRunService; -import io.metersphere.sdk.constants.ApiBatchRunMode; -import io.metersphere.sdk.constants.ApiExecuteRunMode; -import io.metersphere.sdk.constants.TaskTriggerMode; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO; import io.metersphere.sdk.dto.api.task.TaskInfo; import io.metersphere.sdk.dto.api.task.TaskItem; @@ -19,6 +20,8 @@ import io.metersphere.sdk.dto.api.task.TaskRequestDTO; import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.schedule.BaseScheduleJob; import io.metersphere.system.uid.IDGenerator; import org.apache.commons.lang3.StringUtils; @@ -31,6 +34,8 @@ public class ApiScenarioScheduleJob extends BaseScheduleJob { protected void businessExecute(JobExecutionContext context) { ApiExecuteService apiExecuteService = CommonBeanFactory.getBean(ApiExecuteService.class); ApiScenarioRunService apiScenarioRunService = CommonBeanFactory.getBean(ApiScenarioRunService.class); + ApiCommonService apiCommonService = CommonBeanFactory.getBean(ApiCommonService.class); + ProjectMapper projectMapper = CommonBeanFactory.getBean(ProjectMapper.class); ApiRunModeConfigDTO apiRunModeConfigDTO = JSON.parseObject(context.getJobDetail().getJobDataMap().get("config").toString(), ApiRunModeConfigDTO.class); ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(resourceId); @@ -58,18 +63,35 @@ public class ApiScenarioScheduleJob extends BaseScheduleJob { ApiResourceRunRequest runRequest = apiScenarioRunService.getApiResourceRunRequest(msScenario, tmpParam); + Project project = projectMapper.selectByPrimaryKey(apiScenarioDetail.getProjectId()); + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(1L); + execTask.setTaskName(apiScenarioDetail.getName()); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.SCHEDULE.name()); + execTask.setTaskType(ExecTaskType.API_SCENARIO.name()); + + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.API_SCENARIO.name()); + execTaskItem.setResourceId(apiScenarioDetail.getId()); + execTaskItem.setResourceName(apiScenarioDetail.getName()); + TaskRequestDTO taskRequest = apiScenarioRunService.getTaskRequest(IDGenerator.nextStr(), apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.SCHEDULE.name()); TaskInfo taskInfo = taskRequest.getTaskInfo(); TaskItem taskItem = taskRequest.getTaskItem(); taskInfo.getRunModeConfig().setPoolId(apiRunModeConfigDTO.getPoolId()); + taskInfo.setTaskId(execTask.getId()); taskInfo.setSaveResult(true); taskInfo.setRealTime(false); taskInfo.setUserId(userId); taskInfo.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId()); taskItem.setRequestCount(tmpParam.getRequestCount().get()); + taskItem.setId(execTaskItem.getId()); ApiScenarioParamConfig parseConfig = apiScenarioRunService.getApiScenarioParamConfig(msScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo()); parseConfig.setReportId(taskItem.getReportId()); + parseConfig.setTaskItemId(taskItem.getId()); // 初始化报告 ApiScenarioReport scenarioReport = apiScenarioRunService.getScenarioReport(userId); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.java index c4eac081eb..50aa5ded8f 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.java @@ -96,4 +96,6 @@ public interface ExtApiScenarioMapper { List getListBySelectIds(@Param("projectId") String projectId, @Param("ids") List ids, @Param("testPlanId") String testPlanId); List selectBaseInfoByModuleId(String id); + + List getNameInfo(@Param("ids") List ids); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.xml index 7ccdfe4ffc..f0caef69e8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioMapper.xml @@ -72,7 +72,14 @@ #{id} - + + \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java index 266c27f081..ab4d5afd22 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java @@ -111,7 +111,7 @@ public class JmeterTestElementParser implements TestElementParser { ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setEnabled(true); - threadGroup.setName(config.getReportId()); + threadGroup.setName(config.getTaskItemId()); threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); threadGroup.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ThreadGroupGui")); threadGroup.setNumThreads(1); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java index 47065ac180..455e1cb9cd 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java @@ -9,6 +9,7 @@ import io.metersphere.sdk.dto.api.task.TaskInfo; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.ExecTaskItem; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -24,15 +25,15 @@ import java.util.concurrent.atomic.AtomicInteger; public class ApiBatchRunBaseService { @Resource private ApiExecutionQueueService apiExecutionQueueService; + /** * 初始化执行队列 * * @param resourceIds - * @param runModeConfig * @return */ - public ExecutionQueue initExecutionqueue(List resourceIds, ApiRunModeConfigDTO runModeConfig, String resourceType, String userId) { - return initExecutionqueue(resourceIds, runModeConfig, resourceType, null, userId); + public ExecutionQueue initExecutionqueue(List resourceIds, String resourceType, String userId) { + return initExecutionqueue(resourceIds, null, resourceType, null, userId); } /** @@ -59,22 +60,44 @@ public class ApiBatchRunBaseService { queue.setQueueId(queueId); } queue.setParentQueueId(parentQueueId); - List queueDetails = getExecutionQueueDetails(resourceIds); + List queueDetails = getExecutionQueueDetailsByIds(resourceIds); apiExecutionQueueService.insertQueue(queue, queueDetails); return queue; } + public ExecutionQueue initExecutionQueue(String taskId, ApiRunModeConfigDTO runModeConfig, String resourceType, String parentQueueId, String userId) { + return initExecutionQueue(taskId, null, runModeConfig, resourceType, parentQueueId, userId); + } + + /** + * 初始化执行队列 + * + * @param runModeConfig + * @return + */ + public ExecutionQueue initExecutionQueue(String taskId, String queueId, ApiRunModeConfigDTO runModeConfig, String resourceType, String parentQueueId, String userId) { + ExecutionQueue queue = getExecutionQueue(runModeConfig, resourceType, userId); + queue.setTaskId(taskId); + if (StringUtils.isNotBlank(queueId)) { + queue.setQueueId(queueId); + } + queue.setParentQueueId(parentQueueId); + apiExecutionQueueService.insertQueue(queue); + return queue; + } + /** * 初始化执行队列 * - * @param resourceIds + * @param * @return */ - public ExecutionQueue initExecutionqueue(List resourceIds, String resourceType, String userId) { - return initExecutionqueue(resourceIds, null, resourceType, null, userId); + public void initExecutionQueueDetails(String queueId, List execTaskItems) { + List queueDetails = getExecutionQueueDetails(execTaskItems); + apiExecutionQueueService.insertQueueDetail(queueId, queueDetails); } - public List getExecutionQueueDetails(List resourceIds) { + public List getExecutionQueueDetailsByIds(List resourceIds) { List queueDetails = new ArrayList<>(); AtomicInteger sort = new AtomicInteger(1); for (String resourceId : resourceIds) { @@ -86,6 +109,19 @@ public class ApiBatchRunBaseService { return queueDetails; } + public List getExecutionQueueDetails(List execTaskItems) { + List queueDetails = new ArrayList<>(); + AtomicInteger sort = new AtomicInteger(1); + execTaskItems.forEach(execTaskItem -> { + ExecutionQueueDetail queueDetail = new ExecutionQueueDetail(); + queueDetail.setResourceId(execTaskItem.getResourceId()); + queueDetail.setTaskItemId(execTaskItem.getId()); + queueDetail.setSort(sort.getAndIncrement()); + queueDetails.add(queueDetail); + }); + return queueDetails; + } + private ExecutionQueue getExecutionQueue(ApiRunModeConfigDTO runModeConfig, String resourceType, String userId) { ExecutionQueue queue = new ExecutionQueue(); queue.setQueueId(UUID.randomUUID().toString()); @@ -96,7 +132,7 @@ public class ApiBatchRunBaseService { return queue; } - public ApiScenarioReport computeRequestRate(ApiScenarioReport report , long total) { + public ApiScenarioReport computeRequestRate(ApiScenarioReport report, long total) { // 计算各个概率 double successRate = calculateRate(report.getSuccessCount(), total); double errorRate = calculateRate(report.getErrorCount(), total); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java index d68d39be7e..6467c08aa9 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiCommonService.java @@ -25,8 +25,14 @@ import io.metersphere.project.dto.CommonScriptInfo; import io.metersphere.project.service.CustomFunctionService; import io.metersphere.project.service.FileAssociationService; import io.metersphere.project.service.FileMetadataService; +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.constants.ExecStatus; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.JSON; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.uid.NumGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; @@ -389,4 +395,27 @@ public class ApiCommonService { public void setApiDefinitionExecuteInfo(AbstractMsTestElement msTestElement, ApiDefinition apiDefinition) { setApiDefinitionExecuteInfo(msTestElement, BeanUtils.copyBean(new ApiDefinitionExecuteInfo(), apiDefinition)); } + + public ExecTask newExecTask(String projectId, String userId) { + ExecTask execTask = new ExecTask(); + execTask.setNum(NumGenerator.nextNum(projectId, ApplicationNumScope.TASK)); + execTask.setProjectId(projectId); + execTask.setId(IDGenerator.nextStr()); + execTask.setCreateTime(System.currentTimeMillis()); + execTask.setCreateUser(userId); + execTask.setStatus(ExecStatus.PENDING.name()); + return execTask; + } + + public ExecTaskItem newExecTaskItem(String taskId, String projectId, String userId) { + ExecTaskItem execTaskItem = new ExecTaskItem(); + execTaskItem.setId(IDGenerator.nextStr()); + execTaskItem.setTaskId(taskId); + execTaskItem.setProjectId(projectId); + execTaskItem.setExecutor(userId); + execTaskItem.setStatus(ExecStatus.PENDING.name()); + execTaskItem.setResourcePoolId(StringUtils.EMPTY); + execTaskItem.setResourcePoolNode(StringUtils.EMPTY); + return execTaskItem; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java index 1702e06cdc..c80e9570d1 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java @@ -4,6 +4,7 @@ import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker; import io.metersphere.api.service.definition.ApiReportService; import io.metersphere.api.service.scenario.ApiScenarioReportService; import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.constants.ApiExecuteRunMode; import io.metersphere.sdk.constants.ExecStatus; import io.metersphere.sdk.dto.api.task.GetRunScriptRequest; import io.metersphere.sdk.dto.api.task.GetRunScriptResult; @@ -11,7 +12,10 @@ 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.system.domain.ExecTaskItem; +import io.metersphere.system.mapper.ExecTaskItemMapper; import jakarta.annotation.Resource; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -24,7 +28,7 @@ import java.util.Optional; public class ApiExecuteResourceService { @Resource - private ApiExecuteService apiExecuteService; + private ExecTaskItemMapper execTaskItemMapper; @Resource private ApiReportService apiReportService; @Resource @@ -35,30 +39,40 @@ public class ApiExecuteResourceService { public GetRunScriptResult getRunScript(GetRunScriptRequest request) { TaskItem taskItem = request.getTaskItem(); + String taskItemId = taskItem.getId(); String reportId = taskItem.getReportId(); - String key = apiExecuteService.getTaskKey(reportId, taskItem.getResourceId()); - LogUtils.info("生成并获取执行脚本: {}", key); + LogUtils.info("生成并获取执行脚本: {}", taskItem.getId()); ApiExecuteResourceType apiExecuteResourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, request.getResourceType()); - switch (apiExecuteResourceType) { - case API_SCENARIO, TEST_PLAN_API_SCENARIO, PLAN_RUN_API_SCENARIO -> - apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); - case API_CASE, TEST_PLAN_API_CASE, PLAN_RUN_API_CASE -> - apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); - default -> throw new MSException("不支持的资源类型: " + request.getResourceType()); + if (!ApiExecuteRunMode.isDebug(request.getRunMode())) { + // 更新任务项状态 + ExecTaskItem execTaskItem = new ExecTaskItem(); + execTaskItem.setId(taskItem.getId()); + execTaskItem.setStartTime(System.currentTimeMillis()); + execTaskItem.setStatus(ExecStatus.RUNNING.name()); + execTaskItem.setThreadId(request.getThreadId()); + execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem); + + // 非调试执行,更新报告状态 + switch (apiExecuteResourceType) { + case API_SCENARIO, TEST_PLAN_API_SCENARIO, PLAN_RUN_API_SCENARIO -> + apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); + case API_CASE, TEST_PLAN_API_CASE, PLAN_RUN_API_CASE -> + apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); + default -> throw new MSException("不支持的资源类型: " + request.getResourceType()); + } + } + + if (BooleanUtils.isFalse(request.getNeedParseScript())) { + // 已经生成过脚本,直接获取 + String script = stringRedisTemplate.opsForValue().get(taskItemId); + stringRedisTemplate.delete(taskItemId); + GetRunScriptResult result = new GetRunScriptResult(); + result.setScript(Optional.ofNullable(script).orElse(StringUtils.EMPTY)); + return result; } return ApiExecuteCallbackServiceInvoker.getRunScript(request.getResourceType(), request); } - - public String getRunScript(String reportId, String testId) { - String key = apiExecuteService.getTaskKey(reportId, testId); - LogUtils.info("获取执行脚本: {}", key); - String script = stringRedisTemplate.opsForValue().get(key); - stringRedisTemplate.delete(key); - apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); - apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name()); - return Optional.ofNullable(script).orElse(StringUtils.EMPTY); - } } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java index f3ccad2ba5..1c2ecec356 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java @@ -31,10 +31,12 @@ import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.util.*; import io.metersphere.system.config.MinioProperties; import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.domain.TestResourcePool; import io.metersphere.system.dto.pool.TestResourceDTO; import io.metersphere.system.dto.pool.TestResourceNodeDTO; import io.metersphere.system.dto.pool.TestResourcePoolReturnDTO; +import io.metersphere.system.mapper.ExecTaskItemMapper; import io.metersphere.system.service.*; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; @@ -43,7 +45,11 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; import org.apache.jmeter.util.JMeterUtils; +import org.mybatis.spring.SqlSessionUtils; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.MediaType; @@ -99,6 +105,10 @@ public class ApiExecuteService { private ApiCommonService apiCommonService; @Resource private JdbcDriverPluginService jdbcDriverPluginService; + @Resource + private ExecTaskItemMapper execTaskItemMapper; + @Resource + private SqlSessionFactory sqlSessionFactory; @PostConstruct private void init() { @@ -122,10 +132,6 @@ public class ApiExecuteService { } } - public String getTaskKey(String reportId, String testId) { - return reportId + "_" + testId; - } - public TaskRequestDTO execute(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest, ApiParamConfig parameterConfig) { TaskInfo taskInfo = taskRequest.getTaskInfo(); TaskItem taskItem = taskRequest.getTaskItem(); @@ -136,12 +142,12 @@ public class ApiExecuteService { taskInfo.setNeedParseScript(false); // 将测试脚本缓存到 redis - String scriptRedisKey = getTaskKey(taskItem.getReportId(), taskItem.getResourceId()); + String scriptRedisKey = taskItem.getId(); stringRedisTemplate.opsForValue().set(scriptRedisKey, executeScript, 1, TimeUnit.DAYS); setTaskItemFileParam(runRequest, taskItem); - if (StringUtils.equals(taskInfo.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name())) { + if (ApiExecuteRunMode.isFrontendDebug(taskInfo.getRunMode())) { taskInfo = setTaskRequestParams(taskInfo); // 清空mino和kafka配置信息,避免前端获取 taskInfo.setMinioConfig(null); @@ -215,7 +221,6 @@ public class ApiExecuteService { // 获取资源池 TestResourcePoolReturnDTO testResourcePoolDTO = getGetResourcePoolNodeDTO(taskInfo.getRunModeConfig(), taskInfo.getProjectId()); - if (StringUtils.isNotBlank(testResourcePoolDTO.getServerUrl())) { // 如果资源池配置了当前站点,则使用资源池的 taskInfo.setMsUrl(testResourcePoolDTO.getServerUrl()); @@ -223,7 +228,7 @@ public class ApiExecuteService { // 判断是否为 K8S 资源池 boolean isK8SResourcePool = StringUtils.equals(testResourcePoolDTO.getType(), ResourcePoolTypeEnum.K8S.name()); - boolean isDebugMode = StringUtils.equalsAny(taskInfo.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name(), ApiExecuteRunMode.BACKEND_DEBUG.name()); + boolean isDebugMode = ApiExecuteRunMode.isDebug(taskInfo.getRunMode()); if (isK8SResourcePool) { TestResourceDTO testResourceDTO = new TestResourceDTO(); @@ -237,6 +242,10 @@ public class ApiExecuteService { } } else { TestResourceNodeDTO testResourceNodeDTO = getNextExecuteNode(testResourcePoolDTO); + if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) { + updateTaskItemNodeInfo(taskItem, testResourcePoolDTO, testResourceNodeDTO, execTaskItemMapper); + } + taskInfo.setPerTaskSize(Optional.ofNullable(testResourceNodeDTO.getSingleTaskConcurrentNumber()).orElse(3)); taskInfo.setPoolSize(testResourceNodeDTO.getConcurrentNumber()); String endpoint = MsHttpClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort()); @@ -274,6 +283,15 @@ public class ApiExecuteService { return taskRequest; } + private void updateTaskItemNodeInfo(TaskItem taskItem, TestResourcePoolReturnDTO testResourcePoolDTO, TestResourceNodeDTO testResourceNodeDTO, ExecTaskItemMapper execTaskItemMapper) { + // 非调试模式,更新任务项的资源池信息 + ExecTaskItem execTaskItem = new ExecTaskItem(); + execTaskItem.setId(taskItem.getId()); + execTaskItem.setResourcePoolId(testResourcePoolDTO.getId()); + execTaskItem.setResourcePoolNode(StringUtils.join(testResourceNodeDTO.getIp(), ":", testResourceNodeDTO.getPort())); + execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem); + } + /** * 发送执行任务 */ @@ -321,9 +339,14 @@ public class ApiExecuteService { TestResourceNodeDTO testResourceNode = nodesList.get(i); TaskBatchRequestDTO subTaskRequest = distributeTasks.get(i); String endpoint = MsHttpClient.getEndpoint(testResourceNode.getIp(), testResourceNode.getPort()); + + if (!ApiExecuteRunMode.isDebug(taskInfo.getRunMode())) { + batchUpdateTaskItemNodeInfo(testResourcePool, testResourceNode, subTaskRequest); + } + try { List taskKeys = subTaskRequest.getTaskItems().stream() - .map(taskItem -> taskItem.getReportId() + "_" + taskItem.getResourceId()) + .map(TaskItem::getId) .toList(); LogUtils.info("开始发送批量任务到 {} 节点执行:\n" + taskKeys, endpoint); MsHttpClient.batchRunApi(endpoint, subTaskRequest); @@ -335,6 +358,22 @@ public class ApiExecuteService { } } + private void batchUpdateTaskItemNodeInfo(TestResourcePoolReturnDTO testResourcePool, TestResourceNodeDTO testResourceNode, TaskBatchRequestDTO batchRequest) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + try { + if (CollectionUtils.isNotEmpty(batchRequest.getTaskItems())) { + ExecTaskItemMapper batchExecTaskItemMapper = sqlSession.getMapper(ExecTaskItemMapper.class); + batchRequest.getTaskItems().forEach(taskItem -> { + // 非调试模式,更新任务项的资源池信息 + updateTaskItemNodeInfo(taskItem, testResourcePool, testResourceNode, batchExecTaskItemMapper); + }); + } + } finally { + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } + protected static boolean validate() { try { LicenseService licenseService = CommonBeanFactory.getBean(LicenseService.class); @@ -419,11 +458,13 @@ public class ApiExecuteService { // 设置执行参数 TaskRequestDTO taskRequest = getTaskRequest(reportId, testId, runRequest.getProjectId()); TaskInfo taskInfo = taskRequest.getTaskInfo(); + taskInfo.setTaskId(reportId); setServerInfoParam(taskInfo); taskInfo.setRealTime(true); taskInfo.setSaveResult(false); taskInfo.setResourceType(ApiExecuteResourceType.API_DEBUG.name()); taskInfo.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name()); + taskRequest.getTaskItem().setId(reportId); return execute(apiRunRequest, taskRequest, new ApiParamConfig()); } @@ -644,9 +685,8 @@ public class ApiExecuteService { return (String) configMap.get(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); } - public void downloadFile(String reportId, String testId, FileRequest fileRequest, HttpServletResponse response) throws Exception { - String key = getTaskKey(reportId, testId); - if (BooleanUtils.isTrue(stringRedisTemplate.hasKey(key))) { + public void downloadFile(String taskItemId, FileRequest fileRequest, HttpServletResponse response) throws Exception { + if (BooleanUtils.isTrue(stringRedisTemplate.hasKey(taskItemId))) { FileRepository repository = StringUtils.isBlank(fileRequest.getStorage()) ? FileCenter.getDefaultRepository() : FileCenter.getRepository(fileRequest.getStorage()); write2Response(repository.getFileAsStream(fileRequest), response); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java index 26ebb9ddb5..acbf29a031 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java @@ -215,10 +215,13 @@ public class ApiDebugService extends MoveNodeService { TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId()); TaskInfo taskInfo = taskRequest.getTaskInfo(); + taskInfo.setTaskId(request.getReportId()); taskInfo.setSaveResult(false); taskInfo.setRealTime(true); taskInfo.setResourceType(ApiResourceType.API_DEBUG.name()); taskInfo.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug())); + taskRequest.getTaskItem().setId(request.getReportId()); + apiParamConfig.setTaskItemId(taskRequest.getTaskItem().getId()); return apiExecuteService.apiExecute(runRequest, taskRequest, apiParamConfig); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java index 5218762339..93248ded85 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java @@ -1195,10 +1195,12 @@ public class ApiDefinitionService extends MoveNodeService { TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId()); TaskInfo taskInfo = taskRequest.getTaskInfo(); + taskInfo.setTaskId(request.getReportId()); taskInfo.setSaveResult(false); taskInfo.setRealTime(true); taskInfo.setResourceType(ApiResourceType.API.name()); taskInfo.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug())); + taskRequest.getTaskItem().setId(request.getReportId()); AbstractMsTestElement msTestElement = runRequest.getTestElement(); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java index dde71746a4..08023e2c20 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiReportService.java @@ -66,6 +66,10 @@ public class ApiReportService { @Resource private ApiReportNoticeService apiReportNoticeService; + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + public void insertApiReport(ApiReport report) { + apiReportMapper.insertSelective(report); + } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void insertApiReport(List reports, List records) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java index 93ff4d8420..f2d5055bd3 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java @@ -4,27 +4,27 @@ import io.metersphere.api.domain.*; import io.metersphere.api.dto.definition.ApiTestCaseBatchRunRequest; import io.metersphere.api.mapper.*; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionSetService; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; -import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.DateUtils; -import io.metersphere.sdk.util.LogUtils; -import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -39,6 +39,8 @@ public class ApiTestCaseBatchRunService { @Resource private ApiTestCaseService apiTestCaseService; @Resource + private ApiCommonService apiCommonService; + @Resource private ApiExecuteService apiExecuteService; @Resource private ApiExecutionQueueService apiExecutionQueueService; @@ -54,6 +56,12 @@ public class ApiTestCaseBatchRunService { private ApiReportDetailMapper apiReportDetailMapper; @Resource private ApiReportStepMapper apiReportStepMapper; + @Resource + private ProjectMapper projectMapper; + @Resource + private BaseTaskHubService baseTaskHubService; + + public static final int TASK_BATCH_SIZE = 600; /** * 异步批量执行 @@ -92,24 +100,38 @@ public class ApiTestCaseBatchRunService { List ids = apiTestCaseService.doSelectIds(request, false); ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + + // 初始化任务 + ExecTask execTask = initExecTask(ids, runModeConfig, project, userId); + if (runModeConfig.isIntegratedReport()) { // 初始化集成报告 - initIntegratedReport(runModeConfig, ids, userId, request.getProjectId()); - - List apiTestCases = new ArrayList<>(ids.size()); - - // 分批查询 - SubListUtils.dealForSubList(ids, 100, subIds -> apiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); - - Map apiCaseMap = apiTestCases.stream() - .collect(Collectors.toMap(ApiTestCase::getId, Function.identity())); - - // 初始化集成报告步骤 - initApiReportSteps(ids, apiCaseMap, runModeConfig.getCollectionReport().getReportId()); + initIntegratedReport(runModeConfig, userId, request.getProjectId()); } // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_CASE.name(), userId); + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(execTask.getId(), runModeConfig, ApiExecuteResourceType.API_CASE.name(), null, userId); + + // 分批查询 + SubListUtils.dealForSubList(ids, TASK_BATCH_SIZE, subIds -> { + List apiTestCases = getOrderApiTestCases(subIds, runModeConfig); + + // 初始化任务项 + List execTaskItems = initExecTaskItem(apiTestCases, userId, project, execTask); + + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + + if (runModeConfig.isIntegratedReport()) { + String reportId = runModeConfig.getCollectionReport().getReportId(); + // 初始化集成报告和用例的关联关系 + initIntegratedReportCaseRecord(reportId, runModeConfig, subIds); + + // 初始化集成报告步骤 + initApiReportSteps(apiTestCases, reportId); + } + }); // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); @@ -120,6 +142,35 @@ public class ApiTestCaseBatchRunService { executeNextTask(queue, nextDetail); } + private List initExecTaskItem(List apiTestCases, String userId, Project project, ExecTask execTask) { + List execTaskItems = new ArrayList<>(apiTestCases.size()); + for (ApiTestCase apiTestCase : apiTestCases) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.API_CASE.name()); + execTaskItem.setResourceId(apiTestCase.getId()); + execTaskItem.setResourceName(apiTestCase.getName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + return execTaskItems; + } + + private ExecTask initExecTask(List ids, ApiRunModeConfigDTO runModeConfig, Project project, String userId) { + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(Long.valueOf(ids.size())); + if (runModeConfig.isIntegratedReport()) { + execTask.setTaskName(runModeConfig.getCollectionReport().getReportName()); + } else { + execTask.setTaskName(Translator.get("api_batch_task_name")); + } + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.API_CASE_BATCH.name()); + baseTaskHubService.insertExecTask(execTask); + return execTask; + } + /** * 并行批量执行 * @@ -137,25 +188,65 @@ public class ApiTestCaseBatchRunService { apiExecutionSetService.initSet(apiReport.getId(), ids); } - List apiTestCases = new ArrayList<>(ids.size()); + // todo + // 集成报告,执行前先设置成 RUNNING + setRunningIntegrateReport(runModeConfig); + + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + + // 初始化任务 + ExecTask execTask = initExecTask(ids, runModeConfig, project, userId); // 分批查询 - SubListUtils.dealForSubList(ids, 100, subIds -> apiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); + SubListUtils.dealForSubList(ids, TASK_BATCH_SIZE, subIds -> { + List apiTestCases = getOrderApiTestCases(subIds, runModeConfig); + // 初始化任务项 + Map resourceExecTaskItemMap = initExecTaskItem(apiTestCases, userId, project, execTask) + .stream() + .collect(Collectors.toMap(ExecTaskItem::getResourceId, ExecTaskItem::getId)); + // 初始化报告,返回用例和报告的 map + Map caseReportMap = initParallelReport(runModeConfig, apiTestCases, userId); + + List taskItems = new ArrayList<>(subIds.size()); + + for (ApiTestCase apiTestCase : apiTestCases) { + // 如果是集成报告则生成唯一的虚拟ID,非集成报告使用单用例的报告ID + String reportId = runModeConfig.isIntegratedReport() + ? runModeConfig.getCollectionReport().getReportId() + IDGenerator.nextStr() + : caseReportMap.get(apiTestCase.getId()); + + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, apiTestCase.getId()); + taskItem.setId(resourceExecTaskItemMap.get(apiTestCase.getId())); + taskItem.setRequestCount(1L); + taskItems.add(taskItem); + } + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); + taskRequest.setTaskItems(taskItems); + apiExecuteService.batchExecute(taskRequest); + }); + } + + /** + * 获取有序的用例 + * @param ids + * @return + */ + private List getOrderApiTestCases(List ids, ApiRunModeConfigDTO runModeConfig) { + List apiTestCases = new ArrayList<>(TASK_BATCH_SIZE); + // 分批查询 + List finalApiTestCases = apiTestCases; + SubListUtils.dealForSubList(ids, 200, subIds -> finalApiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); Map apiCaseMap = apiTestCases.stream() .collect(Collectors.toMap(ApiTestCase::getId, Function.identity())); - // 初始化报告,返回用例和报告的 map - Map caseReportMap = initParallelReport(ids, runModeConfig, apiTestCases, apiCaseMap, userId); + apiTestCases = new ArrayList<>(ids.size()); - // 集成报告,执行前先设置成 RUNNING - setRunningIntegrateReport(runModeConfig); - List taskItems = new ArrayList<>(ids.size()); - - // 这里ID顺序和队列的ID顺序保持一致 for (String id : ids) { + // 按照ID顺序排序 ApiTestCase apiTestCase = apiCaseMap.get(id); - if (apiTestCase == null) { if (runModeConfig.isIntegratedReport()) { // 用例不存在,则在执行集合中删除 @@ -164,22 +255,12 @@ public class ApiTestCaseBatchRunService { LogUtils.info("当前执行任务的用例已删除 {}", id); break; } - - // 如果是集成报告则生成唯一的虚拟ID,非集成报告使用单用例的报告ID - String reportId = runModeConfig.isIntegratedReport() - ? runModeConfig.getCollectionReport().getReportId() + IDGenerator.nextStr() - : caseReportMap.get(id); - - TaskItem taskItem = apiExecuteService.getTaskItem(reportId, id); - taskItem.setRequestCount(1L); - taskItems.add(taskItem); + apiTestCases.add(apiTestCase); } - - TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig); - taskRequest.setTaskItems(taskItems); - apiExecuteService.batchExecute(taskRequest); + return apiTestCases; } + /** * 集成报告,执行前先设置成 RUNNING * @@ -191,12 +272,12 @@ public class ApiTestCaseBatchRunService { } } - private Map initParallelReport(List ids, ApiRunModeConfigDTO runModeConfig, List apiTestCases, Map apiCaseMap, String userId) { + private Map initParallelReport(ApiRunModeConfigDTO runModeConfig, List apiTestCases, String userId) { // 先初始化所有报告 if (runModeConfig.isIntegratedReport()) { // 获取集成报告ID String integratedReportId = runModeConfig.getCollectionReport().getReportId(); - initApiReportSteps(ids, apiCaseMap, integratedReportId); + initApiReportSteps(apiTestCases, integratedReportId); return null; } else { // 初始化非集成报告 @@ -209,14 +290,12 @@ public class ApiTestCaseBatchRunService { /** * 初始化集成报告的报告步骤 * - * @param ids - * @param apiCaseMap * @param reportId */ - private void initApiReportSteps(List ids, Map apiCaseMap, String reportId) { + private void initApiReportSteps(List apiTestCases, String reportId) { AtomicLong sort = new AtomicLong(1); - List apiReportSteps = ids.stream() - .map(id -> getApiReportStep(apiCaseMap.get(id), reportId, sort.getAndIncrement())) + List apiReportSteps = apiTestCases.stream() + .map(apiTestCase -> getApiReportStep(apiTestCase, reportId, sort.getAndIncrement())) .collect(Collectors.toList()); apiReportService.insertApiReportStep(apiReportSteps); } @@ -265,6 +344,43 @@ public class ApiTestCaseBatchRunService { return apiReport; } + /** + * 预生成用例的执行报告 + * + * @param runModeConfig + * @return + */ + private ApiReport initIntegratedReport(ApiRunModeConfigDTO runModeConfig, String userId, String projectId) { + ApiReport apiReport = getApiReport(runModeConfig, userId); + apiReport.setName(runModeConfig.getCollectionReport().getReportName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiReport.setIntegrated(true); + apiReport.setProjectId(projectId); + apiReportService.insertApiReport(apiReport); + // 设置集成报告执行参数 + runModeConfig.getCollectionReport().setReportId(apiReport.getId()); + return apiReport; + } + + /** + * 预生成用例的执行报告 + * + * @param runModeConfig + * @param ids + * @return + */ + private void initIntegratedReportCaseRecord(String reportId, ApiRunModeConfigDTO runModeConfig, List ids) { + // 初始化集成报告与用例的关联关系 + List records = ids.stream().map(id -> { + ApiTestCaseRecord record = new ApiTestCaseRecord(); + record.setApiReportId(reportId); + record.setApiTestCaseId(id); + return record; + }).toList(); + apiReportService.insertApiReport(List.of(), records); + // 设置集成报告执行参数 + runModeConfig.getCollectionReport().setReportId(reportId); + } + /** * 执行串行的下一个任务 * @@ -274,6 +390,7 @@ public class ApiTestCaseBatchRunService { public void executeNextTask(ExecutionQueue queue, ExecutionQueueDetail queueDetail) { ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); String resourceId = queueDetail.getResourceId(); + String taskItemId = queueDetail.getTaskItemId(); ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(resourceId); @@ -291,9 +408,11 @@ public class ApiTestCaseBatchRunService { } TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); taskRequest.getTaskItem().setRequestCount(1L); + taskRequest.getTaskItem().setId(taskItemId); apiExecuteService.execute(taskRequest); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java index 5a28a3d1ed..9ff3017aaa 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java @@ -32,6 +32,8 @@ import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.domain.User; import io.metersphere.system.dto.OperationHistoryDTO; import io.metersphere.system.dto.request.OperationHistoryRequest; @@ -40,6 +42,7 @@ import io.metersphere.system.dto.sdk.request.PosRequest; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.mapper.UserMapper; import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.service.OperationHistoryService; import io.metersphere.system.service.UserLoginService; import io.metersphere.system.uid.IDGenerator; @@ -116,6 +119,8 @@ public class ApiTestCaseService extends MoveNodeService { private FunctionalCaseTestMapper functionalCaseTestMapper; @Resource private UserMapper userMapper; + @Resource + private BaseTaskHubService baseTaskHubService; private static final String CASE_TABLE = "api_test_case"; @@ -719,6 +724,22 @@ public class ApiTestCaseService extends MoveNodeService { */ public TaskRequestDTO executeRun(ApiResourceRunRequest runRequest, ApiTestCase apiTestCase, String reportId, String userId) { String poolId = apiExecuteService.getProjectApiResourcePoolId(apiTestCase.getProjectId()); + Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId()); + + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(1L); + execTask.setTaskName(apiTestCase.getName()); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.API_CASE.name()); + + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.API_CASE.name()); + execTaskItem.setResourceId(apiTestCase.getId()); + execTaskItem.setResourceName(apiTestCase.getName()); + + baseTaskHubService.insertExecTaskAndDetail(execTask, execTaskItem); TaskRequestDTO taskRequest = getTaskRequest(reportId, apiTestCase.getId(), apiTestCase.getProjectId(), ApiExecuteRunMode.RUN.name()); TaskItem taskItem = taskRequest.getTaskItem(); @@ -726,6 +747,8 @@ public class ApiTestCaseService extends MoveNodeService { taskInfo.getRunModeConfig().setPoolId(poolId); taskInfo.setSaveResult(true); taskInfo.setUserId(userId); + taskInfo.setTaskId(execTask.getId()); + taskItem.setId(execTaskItem.getId()); if (StringUtils.isEmpty(taskItem.getReportId())) { taskInfo.setRealTime(false); @@ -752,9 +775,11 @@ public class ApiTestCaseService extends MoveNodeService { public TaskRequestDTO debug(ApiCaseRunRequest request, String userId) { TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(), request.getProjectId(), apiExecuteService.getDebugRunModule(request.getFrontendDebug())); + taskRequest.getTaskInfo().setTaskId(UUID.randomUUID().toString()); taskRequest.getTaskInfo().setSaveResult(false); taskRequest.getTaskInfo().setRealTime(true); taskRequest.getTaskInfo().setUserId(userId); + taskRequest.getTaskItem().setId(UUID.randomUUID().toString()); ApiResourceRunRequest runRequest = apiExecuteService.getApiResourceRunRequest(request); @@ -771,6 +796,7 @@ public class ApiTestCaseService extends MoveNodeService { apiParamConfig.setEnvConfig(environmentService.get(envId)); taskRequest.getTaskInfo().getRunModeConfig().setEnvironmentId(envId); + apiParamConfig.setTaskItemId(taskRequest.getTaskItem().getId()); // 设置 method 等信息 apiCommonService.setApiDefinitionExecuteInfo(runRequest.getTestElement(), apiDefinition); @@ -872,8 +898,12 @@ public class ApiTestCaseService extends MoveNodeService { } public ApiTestCaseRecord getApiTestCaseRecord(ApiTestCase apiTestCase, ApiReport apiReport) { + return getApiTestCaseRecord(apiTestCase.getId(), apiReport); + } + + public ApiTestCaseRecord getApiTestCaseRecord(String caseId, ApiReport apiReport) { ApiTestCaseRecord apiTestCaseRecord = new ApiTestCaseRecord(); - apiTestCaseRecord.setApiTestCaseId(apiTestCase.getId()); + apiTestCaseRecord.setApiTestCaseId(caseId); apiTestCaseRecord.setApiReportId(apiReport.getId()); return apiTestCaseRecord; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/queue/ApiExecutionQueueService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/queue/ApiExecutionQueueService.java index bc058a23be..4380799eb8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/queue/ApiExecutionQueueService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/queue/ApiExecutionQueueService.java @@ -34,6 +34,20 @@ public class ApiExecutionQueueService { stringRedisTemplate.expire(QUEUE_DETAIL_PREFIX + queue.getQueueId(), 1, TimeUnit.DAYS); } + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void insertQueue(ExecutionQueue queue) { + // 保存队列信息 + stringRedisTemplate.opsForValue().setIfAbsent(QUEUE_PREFIX + queue.getQueueId(), JSON.toJSONString(queue), 1, TimeUnit.DAYS); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void insertQueueDetail(String queueId, List queueDetails) { + // 保存队列详情信息 + List queueStrItems = queueDetails.stream().map(JSON::toJSONString).toList(); + stringRedisTemplate.opsForList().rightPushAll(QUEUE_DETAIL_PREFIX + queueId, queueStrItems); + stringRedisTemplate.expire(QUEUE_DETAIL_PREFIX + queueId, 1, TimeUnit.DAYS); + } + /** * 获取下一个节点 */ diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java index 16d88df379..88f8b033e2 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java @@ -10,17 +10,20 @@ import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.mapper.ApiScenarioReportMapper; import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionSetService; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; -import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.DateUtils; -import io.metersphere.sdk.util.LogUtils; -import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections4.CollectionUtils; @@ -59,6 +62,14 @@ public class ApiScenarioBatchRunService { private ApiScenarioMapper apiScenarioMapper; @Resource private ApiScenarioRunService apiScenarioRunService; + @Resource + private ProjectMapper projectMapper; + @Resource + private ApiCommonService apiCommonService; + @Resource + private BaseTaskHubService baseTaskHubService; + + public static final int TASK_BATCH_SIZE = 600; /** * 异步批量执行 @@ -96,19 +107,46 @@ public class ApiScenarioBatchRunService { public void serialExecute(ApiScenarioBatchRunRequest request, String userId) { List ids = apiScenarioService.doSelectIds(request, false); ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + + // 初始化任务 + ExecTask execTask = initExecTask(ids, runModeConfig, project, userId); + // 初始化集成报告 if (runModeConfig.isIntegratedReport()) { - initIntegratedReport(runModeConfig, ids, userId, request.getProjectId()); - - // 初始化集合报告步骤 - initReport(ids, runModeConfig, userId); + initIntegratedReport(runModeConfig, userId, request.getProjectId()); } + // 先初始化集成报告,设置好报告ID,再初始化执行队列 + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(execTask.getId(), runModeConfig, + ApiExecuteResourceType.API_SCENARIO.name(), null, userId); + + // 分批查询 + SubListUtils.dealForSubList(ids, TASK_BATCH_SIZE, subIds -> { + List apiScenarios = getOrderScenarios(subIds, runModeConfig); + + // 初始化任务项 + List execTaskItems = initExecTaskItem(subIds, apiScenarios, userId, project, execTask); + + if (runModeConfig.isIntegratedReport()) { + String reportId = runModeConfig.getCollectionReport().getReportId(); + // 初始化集合报告和场景的关联关系 + initIntegratedReportCaseRecord(reportId, subIds); + // 集合报告初始化一级步骤 + initApiScenarioReportStep(apiScenarios, reportId); + } else { + // 非集合报告,初始化独立报告,执行时初始化步骤 + initScenarioReport(runModeConfig, apiScenarios, userId); + } + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + }); + + // todo // 集成报告,执行前先设置成 RUNNING setRunningIntegrateReport(runModeConfig); - // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), userId); // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); executeNextTask(queue, nextDetail); @@ -124,63 +162,119 @@ public class ApiScenarioBatchRunService { ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + // 集成报告,执行前先设置成 RUNNING + setRunningIntegrateReport(runModeConfig); + + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + + // 初始化任务 + ExecTask execTask = initExecTask(ids, runModeConfig, project, userId); + if (runModeConfig.isIntegratedReport()) { // 初始化集成报告 - ApiScenarioReport apiScenarioReport = initIntegratedReport(runModeConfig, ids, userId, request.getProjectId()); + ApiScenarioReport apiScenarioReport = initIntegratedReport(runModeConfig, userId, request.getProjectId()); // 集成报告才需要初始化执行集合,用于统计整体执行情况 apiExecutionSetService.initSet(apiScenarioReport.getId(), ids); } - Map scenarioReportMap = initReport(ids, runModeConfig, userId); + // 分批查询 + SubListUtils.dealForSubList(ids, TASK_BATCH_SIZE, subIds -> { + List apiScenarios = getOrderScenarios(subIds, runModeConfig); + Map caseReportMap = null; + if (runModeConfig.isIntegratedReport()) { + // 集合报告初始化一级步骤 + initApiScenarioReportStep(apiScenarios, runModeConfig.getCollectionReport().getReportId()); + } else { + // 非集合报告,初始化独立报告,执行时初始化步骤 + caseReportMap = initScenarioReport(runModeConfig, apiScenarios, userId); + } - // 集成报告,执行前先设置成 RUNNING - setRunningIntegrateReport(runModeConfig); + // 初始化任务项 + Map resourceExecTaskItemMap = initExecTaskItem(subIds, apiScenarios, userId, project, execTask) + .stream() + .collect(Collectors.toMap(ExecTaskItem::getResourceId, ExecTaskItem::getId)); - List taskItems = ids.stream() - .map(id -> apiExecuteService.getTaskItem( - runModeConfig.isIntegratedReport() - ? runModeConfig.getCollectionReport().getReportId() + IDGenerator.nextStr() - : scenarioReportMap.get(id), id - )) - .toList(); + List taskItems = new ArrayList<>(ids.size()); - TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig); - taskRequest.setTaskItems(taskItems); - taskRequest.getTaskInfo().setUserId(userId); + for (ApiScenario apiScenario : apiScenarios) { + // 如果是集成报告则生成唯一的虚拟ID,非集成报告使用单用例的报告ID + String reportId = runModeConfig.isIntegratedReport() + ? runModeConfig.getCollectionReport().getReportId() + IDGenerator.nextStr() + : caseReportMap.get(apiScenario.getId()); - apiExecuteService.batchExecute(taskRequest); + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, apiScenario.getId()); + taskItem.setId(resourceExecTaskItemMap.get(apiScenario.getId())); + taskItem.setRequestCount(1L); + taskItems.add(taskItem); + } + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); + taskRequest.getTaskInfo().setUserId(userId); + taskRequest.setTaskItems(taskItems); + apiExecuteService.batchExecute(taskRequest); + }); } - - private Map initReport(List ids, ApiRunModeConfigDTO runModeConfig, String userId) { - List apiScenarios = new ArrayList<>(ids.size()); + /** + * 获取有序的用例 + * @param ids + * @return + */ + private List getOrderScenarios(List ids, ApiRunModeConfigDTO runModeConfig) { + List apiScenarios = new ArrayList<>(TASK_BATCH_SIZE); // 分批查询 List finalApiScenarios = apiScenarios; - SubListUtils.dealForSubList(ids, 100, subIds -> finalApiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); - + SubListUtils.dealForSubList(ids, 200, subIds -> finalApiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); Map apiScenarioMap = apiScenarios.stream() .collect(Collectors.toMap(ApiScenario::getId, Function.identity())); + apiScenarios = new ArrayList<>(ids.size()); for (String id : ids) { // 按照ID顺序排序 ApiScenario apiScenario = apiScenarioMap.get(id); if (apiScenario == null) { + if (runModeConfig.isIntegratedReport()) { + // 用例不存在,则在执行集合中删除 + apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id); + } + LogUtils.info("当前执行任务的用例已删除 {}", id); break; } apiScenarios.add(apiScenario); } - - if (runModeConfig.isIntegratedReport()) { - // 集合报告初始化一级步骤 - initApiScenarioReportStep(apiScenarios, runModeConfig.getCollectionReport().getReportId()); - return null; - } else { - // 非集合报告,初始化独立报告,执行时初始化步骤 - return initScenarioReport(runModeConfig, apiScenarios, userId); - } + return apiScenarios; } + private ExecTask initExecTask(List ids, ApiRunModeConfigDTO runModeConfig, Project project, String userId) { + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(Long.valueOf(ids.size())); + if (runModeConfig.isIntegratedReport()) { + execTask.setTaskName(runModeConfig.getCollectionReport().getReportName()); + } else { + execTask.setTaskName(Translator.get("api_scenario_batch_task_name")); + } + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.API_SCENARIO_BATCH.name()); + baseTaskHubService.insertExecTask(execTask); + return execTask; + } + + private List initExecTaskItem(List ids, List apiScenarios, String userId, Project project, ExecTask execTask) { + List execTaskItems = new ArrayList<>(ids.size()); + for (ApiScenario apiScenario : apiScenarios) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.API_SCENARIO.name()); + execTaskItem.setResourceId(apiScenario.getId()); + execTaskItem.setResourceName(apiScenario.getName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + return execTaskItems; + } /** * 集成报告,执行前先设置成 RUNNING @@ -223,27 +317,30 @@ public class ApiScenarioBatchRunService { * 预生成用例的执行报告 * * @param runModeConfig - * @param ids * @return */ - private ApiScenarioReport initIntegratedReport(ApiRunModeConfigDTO runModeConfig, List ids, String userId, String projectId) { + private ApiScenarioReport initIntegratedReport(ApiRunModeConfigDTO runModeConfig, String userId, String projectId) { ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, userId); apiScenarioReport.setName(runModeConfig.getCollectionReport().getReportName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); apiScenarioReport.setIntegrated(true); apiScenarioReport.setProjectId(projectId); - // 初始化集成报告与用例的关联关系 - List records = ids.stream().map(id -> { - ApiScenarioRecord scenarioRecord = new ApiScenarioRecord(); - scenarioRecord.setApiScenarioReportId(apiScenarioReport.getId()); - scenarioRecord.setApiScenarioId(id); - return scenarioRecord; - }).toList(); - apiScenarioReportService.insertApiScenarioReport(List.of(apiScenarioReport), records); + apiScenarioReportMapper.insertSelective(apiScenarioReport); // 设置集成报告执行参数 runModeConfig.getCollectionReport().setReportId(apiScenarioReport.getId()); return apiScenarioReport; } + private void initIntegratedReportCaseRecord(String reportId, List ids) { + // 初始化集成报告与用例的关联关系 + List records = ids.stream().map(id -> { + ApiScenarioRecord scenarioRecord = new ApiScenarioRecord(); + scenarioRecord.setApiScenarioReportId(reportId); + scenarioRecord.setApiScenarioId(id); + return scenarioRecord; + }).toList(); + apiScenarioReportService.insertApiScenarioReport(List.of(), records); + } + /** * 执行串行的下一个任务 * @@ -270,9 +367,11 @@ public class ApiScenarioBatchRunService { TaskRequestDTO taskRequest = getTaskRequestDTO(apiScenario.getProjectId(), queue.getRunModeConfig()); TaskItem taskItem = apiExecuteService.getTaskItem(reportId, queueDetail.getResourceId()); + taskItem.setId(queueDetail.getTaskItemId()); taskRequest.setTaskItem(taskItem); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); apiExecuteService.execute(taskRequest); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java index 53e2cfdd6c..ed4fb9728f 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioRunService.java @@ -24,10 +24,12 @@ import io.metersphere.api.service.queue.ApiExecutionSetService; import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.project.api.processor.MsProcessor; import io.metersphere.project.api.processor.TimeWaitingProcessor; +import io.metersphere.project.domain.Project; import io.metersphere.project.dto.environment.EnvironmentInfoDTO; import io.metersphere.project.dto.environment.http.HttpConfig; import io.metersphere.project.dto.environment.http.HttpConfigModuleMatchRule; import io.metersphere.project.dto.environment.http.SelectModule; +import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.service.EnvironmentGroupService; import io.metersphere.project.service.EnvironmentService; import io.metersphere.sdk.constants.*; @@ -35,7 +37,10 @@ import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.util.DateUtils; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.service.ApiPluginService; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.MapUtils; @@ -81,6 +86,10 @@ public class ApiScenarioRunService { private ApiScenarioBlobMapper apiScenarioBlobMapper; @Resource private ApiExecutionSetService apiExecutionSetService; + @Resource + private ProjectMapper projectMapper; + @Resource + private BaseTaskHubService baseTaskHubService; public TaskRequestDTO run(String id, String reportId, String userId) { ApiScenarioDetail apiScenarioDetail = getForRun(id); @@ -179,6 +188,23 @@ public class ApiScenarioRunService { String reportId, String userId) { + Project project = projectMapper.selectByPrimaryKey(apiScenario.getProjectId()); + + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(1L); + execTask.setTaskName(apiScenario.getName()); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.API_SCENARIO.name()); + + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.API_SCENARIO.name()); + execTaskItem.setResourceId(apiScenario.getId()); + execTaskItem.setResourceName(apiScenario.getName()); + + baseTaskHubService.insertExecTaskAndDetail(execTask, execTaskItem); + msScenario.setResourceId(apiScenario.getId()); // 解析生成场景树,并保存临时变量 @@ -191,6 +217,8 @@ public class ApiScenarioRunService { TaskRequestDTO taskRequest = getTaskRequest(reportId, apiScenario.getId(), apiScenario.getProjectId(), ApiExecuteRunMode.RUN.name()); TaskInfo taskInfo = taskRequest.getTaskInfo(); TaskItem taskItem = taskRequest.getTaskItem(); + taskItem.setId(execTaskItem.getId()); + taskInfo.setTaskId(execTask.getId()); taskInfo.getRunModeConfig().setPoolId(poolId); taskInfo.setSaveResult(true); taskInfo.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId()); @@ -208,6 +236,7 @@ public class ApiScenarioRunService { ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo()); parseConfig.setReportId(reportId); + parseConfig.setTaskItemId(taskItem.getId()); // 初始化报告 ApiScenarioReport scenarioReport = getScenarioReport(userId); @@ -314,6 +343,7 @@ public class ApiScenarioRunService { ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenarioDetail.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo()); parseConfig.setReportId(reportId); + parseConfig.setTaskItemId(taskItem.getId()); parseConfig.setRetryOnFail(request.getRunModeConfig().getRetryOnFail()); parseConfig.setRetryConfig(request.getRunModeConfig().getRetryConfig()); @@ -380,9 +410,11 @@ public class ApiScenarioRunService { msScenario.setProjectId(request.getProjectId()); msScenario.setResourceId(request.getId()); - List dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); - List csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); - apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); + if (!hasSave) { + List dbCsv = apiScenarioService.getApiScenarioCsv(apiScenario.getId()); + List csvVariables = apiScenarioService.getCsvVariables(msScenario.getScenarioConfig()); + apiScenarioService.handleRefUpgradeFile(csvVariables, dbCsv); + } // 处理特殊的步骤详情 apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails()); @@ -396,14 +428,16 @@ public class ApiScenarioRunService { TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(), request.getProjectId(), apiExecuteService.getDebugRunModule(request.getFrontendDebug())); TaskInfo taskInfo = taskRequest.getTaskInfo(); + taskInfo.setTaskId(request.getReportId()); TaskItem taskItem = taskRequest.getTaskItem(); + taskItem.setId(request.getReportId()); taskInfo.setSaveResult(false); taskInfo.setRealTime(true); taskItem.setRequestCount(tmpParam.getRequestCount().get()); ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(request.getProjectId(), request, tmpParam.getScenarioParseEnvInfo()); parseConfig.setReportId(request.getReportId()); - + parseConfig.setTaskItemId(taskItem.getId()); return apiExecuteService.execute(runRequest, taskRequest, parseConfig); } @@ -516,13 +550,16 @@ public class ApiScenarioRunService { } public ApiScenarioRecord getApiScenarioRecord(ApiScenario apiScenario, ApiScenarioReport scenarioReport) { + return getApiScenarioRecord(apiScenario.getId(), scenarioReport); + } + + public ApiScenarioRecord getApiScenarioRecord(String apiScenarioId, ApiScenarioReport scenarioReport) { ApiScenarioRecord scenarioRecord = new ApiScenarioRecord(); - scenarioRecord.setApiScenarioId(apiScenario.getId()); + scenarioRecord.setApiScenarioId(apiScenarioId); scenarioRecord.setApiScenarioReportId(scenarioReport.getId()); return scenarioRecord; } - public ApiScenarioReport getScenarioReport(String userId) { ApiScenarioReport scenarioReport = new ApiScenarioReport(); scenarioReport.setId(IDGenerator.nextStr()); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java index e289948ee2..51b465adca 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java @@ -1434,12 +1434,6 @@ public class ApiScenarioService extends MoveNodeService { */ public Map getPartialRefStepDetailMap(List steps) { - steps.forEach(step -> { - if (isPartialRef(step)) { - - } - }); - List needBlobStepIds = steps.stream() .filter(this::isPartialRef) .map(ApiScenarioStepCommonDTO::getId) diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiExecuteResourceControllerTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiExecuteResourceControllerTest.java index d0007ceda1..3fbdd50efe 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiExecuteResourceControllerTest.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiExecuteResourceControllerTest.java @@ -1,8 +1,6 @@ package io.metersphere.api.controller; -import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiFileResourceService; -import io.metersphere.api.service.debug.ApiDebugService; import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.StorageType; import io.metersphere.sdk.file.FileCopyRequest; @@ -33,15 +31,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. public class ApiExecuteResourceControllerTest extends BaseTest { private static final String BASE_PATH = "/api/execute/resource/"; - private static final String SCRIPT = "script?reportId={0}&testId={1}"; - private static final String FILE = "file?reportId={0}&testId={1}"; + private static final String FILE = "file?taskItemId={0}"; @Resource private StringRedisTemplate stringRedisTemplate; @Resource - private ApiExecuteService apiExecuteService; - @Resource - private ApiDebugService apiDebugService; - @Resource private ApiFileResourceService apiFileResourceService; @Override @@ -49,11 +42,6 @@ public class ApiExecuteResourceControllerTest extends BaseTest { return BASE_PATH; } - @Test - public void getScript() throws Exception { - this.requestGetWithOk(SCRIPT, "reportId", "testId"); - } - @Test public void downloadFile() throws Exception { String fileName = IDGenerator.nextStr() + "_file_upload.JPG"; @@ -62,18 +50,17 @@ public class ApiExecuteResourceControllerTest extends BaseTest { FileRequest fileRequest = new FileCopyRequest(); fileRequest.setFileName(fileName); fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId); - mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, "reportId", "testId")) + mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, "reportId")) .andExpect(status().isOk()); String reportId = UUID.randomUUID().toString(); - String testId = UUID.randomUUID().toString(); - String scriptRedisKey = apiExecuteService.getTaskKey(reportId, testId); + String scriptRedisKey = reportId; stringRedisTemplate.opsForValue().set(scriptRedisKey, "aaa"); - mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId, testId)) + mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId)) .andExpect(status().isOk()); fileRequest.setStorage(StorageType.MINIO.name()); - mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId, testId)) + mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId)) .andExpect(status().isOk()); } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.java b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.java index 1240f349db..debf3e89af 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.java @@ -1,8 +1,6 @@ package io.metersphere.system.mapper; import io.metersphere.system.domain.ExecTaskItem; -import io.metersphere.system.dto.sdk.BasePageRequest; -import io.metersphere.system.dto.taskhub.TaskHubDTO; import io.metersphere.system.dto.taskhub.TaskHubItemDTO; import io.metersphere.system.dto.taskhub.request.TaskHubItemRequest; import org.apache.ibatis.annotations.Param; @@ -18,4 +16,6 @@ public interface ExtExecTaskItemMapper { List selectItemByTaskIds(@Param("taskIds") List taskIds, @Param("orgId") String orgId, @Param("projectId") String projectId); List selectPoolNodeByIds(@Param("ids") List ids); + + List selectExecInfoByResourceIds(@Param("resourceIds") List resourceIds); } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.xml b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.xml index b1c5f3cbfd..efb59d548e 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.xml +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/mapper/ExtExecTaskItemMapper.xml @@ -33,6 +33,14 @@ + - - + SELECT t.id as id, t.test_plan_collection_id as test_plan_collection_id, atc.name as name, t.environment_id, atc.id as api_case_id FROM test_plan_api_case t INNER JOIN api_test_case atc ON t.api_case_id = atc.id INNER JOIN api_definition a ON atc.api_definition_id = a.id @@ -717,4 +716,20 @@ AND test_plan.status != 'ARCHIVED' + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java index fb164cc642..fcd99dfe27 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java @@ -3,10 +3,7 @@ package io.metersphere.plan.mapper; import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; import io.metersphere.functional.dto.ProjectOptionDTO; import io.metersphere.plan.domain.TestPlanApiScenario; -import io.metersphere.plan.dto.ApiScenarioModuleDTO; -import io.metersphere.plan.dto.ResourceSelectParam; -import io.metersphere.plan.dto.TestPlanCaseRunResultCount; -import io.metersphere.plan.dto.TestPlanResourceExecResultDTO; +import io.metersphere.plan.dto.*; import io.metersphere.plan.dto.request.BasePlanCaseBatchRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; @@ -68,7 +65,7 @@ public interface ExtTestPlanApiScenarioMapper { void batchUpdateExecutor(@Param("ids") List ids, @Param("userId") String userId); - List getSelectIdAndCollectionId(@Param("request") TestPlanApiScenarioBatchRunRequest request); + List getSelectIdAndCollectionId(@Param("request") TestPlanApiScenarioBatchRunRequest request); List getIdsByReportIdAndCollectionId(@Param("testPlanReportId") String testPlanReportId, @Param("collectionId") String collectionId); @@ -77,4 +74,8 @@ public interface ExtTestPlanApiScenarioMapper { List selectDistinctExecResult(String projectId); List selectDistinctExecResultByTestPlanIds(@Param("testPlanIds") List testPlanIds); + + List getBatchRunInfoByIds(@Param("ids") List ids); + + Integer countByPlanId(@Param("planId") String planId); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml index 17e409986f..783cb8d8de 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml @@ -456,9 +456,12 @@ AND api_scenario.deleted = #{deleted} - SELECT - test_plan_api_scenario.id as id, test_plan_api_scenario.test_plan_collection_id as testPlanCollectionId + test_plan_api_scenario.id as id, test_plan_api_scenario.test_plan_collection_id as testPlanCollectionId, + test_plan_api_scenario.environment_id as environmentId, + api_scenario.name as name, + api_scenario.id as apiScenarioId, FROM test_plan_api_scenario INNER JOIN api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id @@ -556,6 +559,15 @@ AND test_plan.status != 'ARCHIVED' + update test_plan_api_scenario @@ -565,4 +577,12 @@ #{id} + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiCaseService.java index 282599347c..6cc6a65617 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiCaseService.java @@ -28,6 +28,8 @@ import io.metersphere.sdk.dto.queue.TestPlanExecutionQueue; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.mapper.ExtExecTaskItemMapper; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import jodd.util.StringUtil; @@ -71,6 +73,8 @@ public class PlanRunTestPlanApiCaseService { private TestPlanApiBatchRunBaseService testPlanApiBatchRunBaseService; @Resource private ExtTestPlanReportApiCaseMapper extTestPlanReportApiCaseMapper; + @Resource + private ExtExecTaskItemMapper extExecTaskItemMapper; /** * 串行批量执行 @@ -89,9 +93,14 @@ public class PlanRunTestPlanApiCaseService { return true; } - String queueId = testPlanExecutionQueue.getPrepareReportId() + "_" + collection.getId(); // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(queueId, ids, runModeConfig, ApiExecuteResourceType.PLAN_RUN_API_CASE.name(), parentQueueId, userId); + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(testPlanExecutionQueue.getTaskId(), runModeConfig, ApiExecuteResourceType.PLAN_RUN_API_CASE.name(), parentQueueId, userId); + + SubListUtils.dealForSubList(ids, 100, subIds -> { + List execTaskItems = extExecTaskItemMapper.selectExecInfoByResourceIds(subIds); + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + }); // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); @@ -119,12 +128,19 @@ public class PlanRunTestPlanApiCaseService { } List apiTestCases = new ArrayList<>(testPlanReportApiCases.size()); + Map resourceTaskItemMap = new HashMap<>(); List caseIds = testPlanReportApiCases.stream() .map(TestPlanReportApiCase::getApiCaseId).collect(Collectors.toList()); // 分批查询 SubListUtils.dealForSubList(caseIds, 100, subIds -> apiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); + SubListUtils.dealForSubList(testPlanReportApiCases, 100, + subTestPlanReportApiCases-> { + List subIds = subTestPlanReportApiCases.stream().map(TestPlanReportApiCase::getId).toList(); + extExecTaskItemMapper.selectExecInfoByResourceIds(subIds) + .forEach(execTaskItem -> resourceTaskItemMap.put(execTaskItem.getResourceId(), execTaskItem.getId())); + }); Map apiCaseMap = apiTestCases.stream() .collect(Collectors.toMap(ApiTestCase::getId, Function.identity())); @@ -146,6 +162,7 @@ public class PlanRunTestPlanApiCaseService { continue; } TaskItem taskItem = apiExecuteService.getTaskItem(reportId, id); + taskItem.setId(resourceTaskItemMap.get(testPlanReportApiCase.getId())); taskItem.setRequestCount(1L); taskItems.add(taskItem); } @@ -158,6 +175,7 @@ public class PlanRunTestPlanApiCaseService { TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); TaskBatchRequestDTO taskRequest = apiTestCaseBatchRunService.getTaskBatchRequestDTO(testPlan.getProjectId(), runModeConfig); taskRequest.setTaskItems(taskItems); + taskRequest.getTaskInfo().setTaskId(testPlanExecutionQueue.getTaskId()); taskRequest.getTaskInfo().setParentQueueId(parentQueueId); taskRequest.getTaskInfo().setUserId(userId); taskRequest.getTaskInfo().setResourceType(ApiExecuteResourceType.PLAN_RUN_API_CASE.name()); @@ -197,11 +215,13 @@ public class PlanRunTestPlanApiCaseService { // 独立报告,执行到当前任务时初始化报告 String reportId = initApiReport(runModeConfig, List.of(testPlanReportApiCase), Map.of(apiTestCase.getId(), apiTestCase), queue.getUserId()).get(resourceId); TaskRequestDTO taskRequest = testPlanApiCaseBatchRunService.getTaskRequestDTO(reportId, testPlanReportApiCase.getId(), apiTestCase, runModeConfig); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setResourceType(ApiExecuteResourceType.PLAN_RUN_API_CASE.name()); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); taskRequest.getTaskInfo().setParentQueueId(queue.getParentQueueId()); taskRequest.getTaskItem().setRequestCount(1L); + taskRequest.getTaskItem().setId(queueDetail.getTaskItemId()); apiExecuteService.execute(taskRequest); } @@ -234,7 +254,7 @@ public class PlanRunTestPlanApiCaseService { // 创建报告和用例的关联关系 ApiTestCaseRecord apiTestCaseRecord = apiTestCaseService.getApiTestCaseRecord(apiTestCase, apiReport); apiTestCaseRecords.add(apiTestCaseRecord); - apiReportSteps.add(testPlanApiCaseBatchRunService.getApiReportStep(testPlanReportApiCase.getId(), apiTestCase, apiReport.getId(), 1)); + apiReportSteps.add(testPlanApiCaseBatchRunService.getApiReportStep(testPlanReportApiCase.getId(), apiTestCase.getName(), apiReport.getId(), 1)); resourceReportMap.put(testPlanReportApiCase.getId(), apiReport.getId()); } apiReportService.insertApiReport(apiReports, apiTestCaseRecords); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiScenarioService.java index 9d70050736..118fbc9148 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiScenarioService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/PlanRunTestPlanApiScenarioService.java @@ -12,21 +12,22 @@ import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionSetService; import io.metersphere.api.service.scenario.ApiScenarioReportService; import io.metersphere.api.service.scenario.ApiScenarioRunService; -import io.metersphere.plan.domain.TestPlan; -import io.metersphere.plan.domain.TestPlanCollection; -import io.metersphere.plan.domain.TestPlanReportApiScenario; -import io.metersphere.plan.domain.TestPlanReportApiScenarioExample; +import io.metersphere.plan.domain.*; import io.metersphere.plan.mapper.ExtTestPlanApiScenarioMapper; import io.metersphere.plan.mapper.TestPlanMapper; import io.metersphere.plan.mapper.TestPlanReportApiScenarioMapper; import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.constants.TaskTriggerMode; import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.dto.queue.TestPlanExecutionQueue; +import io.metersphere.sdk.util.DateUtils; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.mapper.ExtExecTaskItemMapper; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; @@ -66,6 +67,8 @@ public class PlanRunTestPlanApiScenarioService { private ExtApiScenarioMapper extApiScenarioMapper; @Resource private ApiScenarioReportService apiScenarioReportService; + @Resource + private ExtExecTaskItemMapper extExecTaskItemMapper; /** * 串行批量执行 @@ -84,10 +87,15 @@ public class PlanRunTestPlanApiScenarioService { return true; } - String queueId = testPlanExecutionQueue.getPrepareReportId() + "_" + collection.getId(); - // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(queueId, ids, runModeConfig, ApiExecuteResourceType.PLAN_RUN_API_SCENARIO.name(), parentQueueId, userId); + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(testPlanExecutionQueue.getTaskId(), runModeConfig, ApiExecuteResourceType.PLAN_RUN_API_SCENARIO.name(), parentQueueId, userId); + + SubListUtils.dealForSubList(ids, 100, subIds -> { + List execTaskItems = extExecTaskItemMapper.selectExecInfoByResourceIds(subIds); + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + }); + // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); executeNextTask(queue, nextDetail); @@ -109,18 +117,31 @@ public class PlanRunTestPlanApiScenarioService { TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(collection); TaskBatchRequestDTO taskRequest = testPlanApiScenarioBatchRunService.getTaskBatchRequestDTO(testPlan.getProjectId(), runModeConfig); + taskRequest.getTaskInfo().setTaskId(testPlanExecutionQueue.getTaskId()); taskRequest.getTaskInfo().setParentQueueId(parentQueueId); taskRequest.getTaskInfo().setUserId(userId); taskRequest.getTaskInfo().setResourceType(ApiExecuteResourceType.PLAN_RUN_API_SCENARIO.name()); + Map resourceTaskItemMap = new HashMap<>(); List testPlanReportApiScenarios = getTestPlanReportApiScenarios(testPlanReportId, collection); if (CollectionUtils.isEmpty(testPlanReportApiScenarios)) { return true; } + SubListUtils.dealForSubList(testPlanReportApiScenarios, 100, + subTestPlanReportApiCases-> { + List subIds = subTestPlanReportApiCases.stream().map(TestPlanReportApiScenario::getId).toList(); + extExecTaskItemMapper.selectExecInfoByResourceIds(subIds) + .forEach(execTaskItem -> resourceTaskItemMap.put(execTaskItem.getResourceId(), execTaskItem.getId())); + }); + Map scenarioReportMap = initReport(testPlanReportApiScenarios, runModeConfig, userId); List taskItems = testPlanReportApiScenarios.stream() - .map(item -> apiExecuteService.getTaskItem(scenarioReportMap.get(item.getId()), item.getId())).toList(); + .map(item -> { + TaskItem taskItem = apiExecuteService.getTaskItem(scenarioReportMap.get(item.getId()), item.getId()); + taskItem.setId(resourceTaskItemMap.get(item.getId())); + return taskItem; + }).toList(); // 如果有父队列,则初始化执行集合,以便判断是否执行完毕 apiExecutionSetService.initSet(parentQueueId, testPlanReportApiScenarios.stream().map(TestPlanReportApiScenario::getId).toList()); taskRequest.setTaskItems(taskItems); @@ -150,10 +171,11 @@ public class PlanRunTestPlanApiScenarioService { List apiScenarioReports = new ArrayList<>(testPlanReportApiScenarios.size()); List apiScenarioRecords = new ArrayList<>(testPlanReportApiScenarios.size()); Map resourceReportMap = new HashMap<>(); + String projectId = ""; for (TestPlanReportApiScenario testPlanReportApiScenario : testPlanReportApiScenarios) { ApiScenario apiScenario = apiScenarioMap.get(testPlanReportApiScenario.getApiScenarioId()); // 初始化报告 - ApiScenarioReport apiScenarioReport = testPlanApiScenarioBatchRunService.getScenarioReport(runModeConfig, apiScenario, userId); + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanReportApiScenario, projectId, userId); apiScenarioReport.setTestPlanScenarioId(testPlanReportApiScenario.getTestPlanApiScenarioId()); // 报告预生成,方便停止测试计划时直接更新报告状态 apiScenarioReport.setId(testPlanReportApiScenario.getApiScenarioExecuteReportId()); @@ -172,6 +194,19 @@ public class PlanRunTestPlanApiScenarioService { return resourceReportMap; } + private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanReportApiScenario testPlanReportApiScenario, String projectId, String userId) { + ApiScenarioReport apiScenarioReport = apiScenarioRunService.getScenarioReport(userId); + apiScenarioReport.setName(testPlanReportApiScenario.getApiScenarioName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiScenarioReport.setProjectId(projectId); + apiScenarioReport.setEnvironmentId(runModeConfig.getEnvironmentId()); + apiScenarioReport.setRunMode(runModeConfig.getRunMode()); + apiScenarioReport.setPoolId(runModeConfig.getPoolId()); + apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name()); + apiScenarioReport.setTestPlanScenarioId(testPlanReportApiScenario.getId()); + apiScenarioReport.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanReportApiScenario.getEnvironmentId())); + return apiScenarioReport; + } + private List getTestPlanReportApiScenarios(String testPlanReportId, TestPlanCollection collection) { TestPlanReportApiScenarioExample example = new TestPlanReportApiScenarioExample(); example.createCriteria() @@ -205,7 +240,9 @@ public class PlanRunTestPlanApiScenarioService { String reportId = initScenarioReport(runModeConfig, List.of(testPlanReportApiScenario), Map.of(apiScenario.getId(), apiScenario), queue.getUserId()).get(resourceId); TaskRequestDTO taskRequest = testPlanApiScenarioBatchRunService.getTaskRequestDTO(apiScenario.getProjectId(), queue.getRunModeConfig()); TaskItem taskItem = apiExecuteService.getTaskItem(reportId, queueDetail.getResourceId()); + taskItem.setId(queueDetail.getTaskItemId()); taskRequest.setTaskItem(taskItem); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); taskRequest.getTaskInfo().setParentQueueId(queue.getParentQueueId()); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java index d80dede0e1..bfa7a14ec6 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java @@ -7,6 +7,7 @@ import io.metersphere.api.domain.ApiTestCaseRecord; import io.metersphere.api.mapper.ApiTestCaseMapper; import io.metersphere.api.mapper.ExtApiTestCaseMapper; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.definition.ApiReportService; import io.metersphere.api.service.definition.ApiTestCaseBatchRunService; @@ -17,20 +18,24 @@ import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlanApiCase; import io.metersphere.plan.domain.TestPlanCollection; import io.metersphere.plan.domain.TestPlanCollectionExample; +import io.metersphere.plan.dto.TestPlanApiCaseBatchRunDTO; import io.metersphere.plan.dto.request.ApiExecutionMapService; +import io.metersphere.plan.dto.request.TestPlanApiCaseBatchRequest; import io.metersphere.plan.dto.request.TestPlanApiCaseBatchRunRequest; import io.metersphere.plan.mapper.*; -import io.metersphere.sdk.constants.ApiExecuteResourceType; -import io.metersphere.sdk.constants.CaseType; -import io.metersphere.sdk.constants.CommonConstants; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; -import io.metersphere.sdk.util.CommonBeanFactory; -import io.metersphere.sdk.util.LogUtils; -import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.service.BaseTaskHubService; import jakarta.annotation.Resource; import jodd.util.StringUtil; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -51,8 +56,6 @@ public class TestPlanApiCaseBatchRunService { @Resource private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; @Resource - private TestPlanApiCaseService testPlanApiCaseService; - @Resource private TestPlanCollectionMapper testPlanCollectionMapper; @Resource private ApiExecuteService apiExecuteService; @@ -75,7 +78,11 @@ public class TestPlanApiCaseBatchRunService { @Resource private TestPlanMapper testPlanMapper; @Resource - private ExtTestPlanMapper extTestPlanMapper; + private ProjectMapper projectMapper; + @Resource + private ApiCommonService apiCommonService; + @Resource + private BaseTaskHubService baseTaskHubService; /** * 异步批量执行 @@ -97,9 +104,9 @@ public class TestPlanApiCaseBatchRunService { */ private void batchRun(TestPlanApiCaseBatchRunRequest request, String userId) { try { - List testPlanApiCases = testPlanApiCaseService.getSelectIdAndCollectionId(request); + List testPlanApiCases = getSelectIdAndCollectionId(request); // 按照 testPlanCollectionId 分组, value 为测试计划用例 ID 列表 - Map> collectionMap = getCollectionMap(testPlanApiCases); + Map> collectionMap = getCollectionMap(testPlanApiCases); List testPlanCollections = getTestPlanCollections(request.getTestPlanId()); Iterator iterator = testPlanCollections.iterator(); @@ -122,18 +129,19 @@ public class TestPlanApiCaseBatchRunService { .collect(Collectors.toList()); TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); if (apiBatchRunBaseService.isParallel(rootCollection.getExecuteMethod())) { // 并行执行测试集 for (TestPlanCollection collection : testPlanCollections) { - List ids = collectionMap.get(collection.getId()); + List collectionCases = collectionMap.get(collection.getId()); ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(rootCollection, collection); if (apiBatchRunBaseService.isParallel(runModeConfig.getRunMode())) { // 并行执行测试集中的用例 - parallelExecute(ids, runModeConfig, null, testPlan.getProjectId(), userId); + parallelExecute(collectionCases, runModeConfig, null, project, userId); } else { // 串行执行测试集中的用例 - serialExecute(ids, runModeConfig, null, userId); + serialExecute(collectionCases, runModeConfig, null, project, userId); } } } else { @@ -142,8 +150,12 @@ public class TestPlanApiCaseBatchRunService { // 生成测试集队列 ExecutionQueue collectionQueue = apiBatchRunBaseService.initExecutionqueue(serialCollectionIds, ApiExecuteResourceType.TEST_PLAN_API_CASE.name(), userId); + + Map> collectionIdMap = collectionMap.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().map(TestPlanApiCaseBatchRunDTO::getId).toList())); + // 记录各测试集中要执行的用例 - apiExecutionMapService.initMap(collectionQueue.getQueueId(), collectionMap); + apiExecutionMapService.initMap(collectionQueue.getQueueId(), collectionIdMap); executeNextCollection(collectionQueue.getQueueId()); } @@ -163,14 +175,17 @@ public class TestPlanApiCaseBatchRunService { ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queueId); String collectionId = nextDetail.getResourceId(); List ids = apiExecutionMapService.getAndRemove(queueId, collectionId); + List testPlanApiCases = getBatchRunInfo(ids); TestPlanCollection collection = testPlanCollectionMapper.selectByPrimaryKey(collectionId); + + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(collection.getTestPlanId()); + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); + ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(collection); if (apiBatchRunBaseService.isParallel(runModeConfig.getRunMode())) { - String testPlanId = collection.getTestPlanId(); - TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); - parallelExecute(ids, runModeConfig, queueId, testPlan.getProjectId(), userId); + parallelExecute(testPlanApiCases, runModeConfig, queueId, project, userId); } else { - serialExecute(ids, runModeConfig, queueId, userId); + serialExecute(testPlanApiCases, runModeConfig, queueId, project, userId); } } @@ -178,14 +193,9 @@ public class TestPlanApiCaseBatchRunService { apiExecutionQueueService.deleteQueue(collectionQueueId); } - private Map> getCollectionMap(List testPlanApiCases) { - Map> collectionMap = new HashMap<>(); - for (TestPlanApiCase testPlanApiCase : testPlanApiCases) { - collectionMap.putIfAbsent(testPlanApiCase.getTestPlanCollectionId(), new ArrayList<>()); - List ids = collectionMap.get(testPlanApiCase.getTestPlanCollectionId()); - ids.add(testPlanApiCase.getId()); - } - return collectionMap; + private Map> getCollectionMap(List testPlanApiCases) { + return testPlanApiCases.stream() + .collect(Collectors.groupingBy(TestPlanApiCaseBatchRunDTO::getTestPlanCollectionId)); } private List getTestPlanCollections(String testPlanId) { @@ -200,9 +210,16 @@ public class TestPlanApiCaseBatchRunService { * 串行批量执行 * */ - public void serialExecute(List ids, ApiRunModeConfigDTO runModeConfig, String parentQueueId, String userId) { + public void serialExecute(List testPlanApiCases, ApiRunModeConfigDTO runModeConfig, String parentQueueId, Project project, String userId) { + // 初始化任务 + ExecTask execTask = initExecTask(testPlanApiCases.size(), runModeConfig, project, userId); + // 初始化任务项 + List execTaskItems = initExecTaskItem(testPlanApiCases, userId, project, execTask); // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.TEST_PLAN_API_CASE.name(), parentQueueId, userId); + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(execTask.getId(), runModeConfig, ApiExecuteResourceType.TEST_PLAN_API_CASE.name(), parentQueueId, userId); + + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); @@ -214,61 +231,121 @@ public class TestPlanApiCaseBatchRunService { * 并行批量执行 * */ - public void parallelExecute(List ids, ApiRunModeConfigDTO runModeConfig, String parentQueueId, String projectId, String userId) { - - List testPlanApiCases = new ArrayList<>(ids.size()); - List apiTestCases = new ArrayList<>(ids.size()); - - // 分批查询 - SubListUtils.dealForSubList(ids, 100, subIds -> testPlanApiCases.addAll(extTestPlanApiCaseMapper.getApiCaseExecuteInfoByIds(subIds))); - - List caseIds = testPlanApiCases.stream() - .map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList()); - - // 分批查询 - SubListUtils.dealForSubList(caseIds, 100, subIds -> apiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); - - Map apiCaseMap = apiTestCases.stream() - .collect(Collectors.toMap(ApiTestCase::getId, Function.identity())); - + public void parallelExecute(List testPlanApiCases, ApiRunModeConfigDTO runModeConfig, String parentQueueId, Project project, String userId) { // 初始化报告,返回用例和报告的 map - Map caseReportMap = initApiReport(runModeConfig, testPlanApiCases, apiCaseMap, userId); + Map caseReportMap = initApiReport(runModeConfig, testPlanApiCases, project.getId(), userId); - List taskItems = new ArrayList<>(ids.size()); + List taskItems = new ArrayList<>(testPlanApiCases.size()); + + // 初始化任务 + ExecTask execTask = initExecTask(testPlanApiCases.size(), runModeConfig, project, userId); + + // 初始化任务项 + Map resourceExecTaskItemMap = initExecTaskItem(testPlanApiCases, userId, project, execTask) + .stream() + .collect(Collectors.toMap(ExecTaskItem::getResourceId, ExecTaskItem::getId)); // 这里ID顺序和队列的ID顺序保持一致 - - Iterator iterator = ids.iterator(); + Iterator iterator = testPlanApiCases.iterator(); while (iterator.hasNext()) { - String id = iterator.next(); - String reportId = caseReportMap.get(id); + TestPlanApiCaseBatchRunDTO testPlanApiCase = iterator.next(); + String reportId = caseReportMap.get(testPlanApiCase.getId()); if (StringUtil.isBlank(reportId)) { iterator.remove(); continue; } - TaskItem taskItem = apiExecuteService.getTaskItem(reportId, id); + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, testPlanApiCase.getId()); taskItem.setRequestCount(1L); + taskItem.setId(resourceExecTaskItemMap.get(testPlanApiCase.getId())); taskItems.add(taskItem); } if (StringUtils.isNotBlank(parentQueueId)) { // 如果有父队列,则初始化执行集合,以便判断是否执行完毕 - apiExecutionSetService.initSet(parentQueueId, ids); + apiExecutionSetService.initSet(parentQueueId, testPlanApiCases.stream().map(TestPlanApiCaseBatchRunDTO::getId).toList()); } - TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(projectId, runModeConfig); + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(project.getId(), runModeConfig); taskRequest.setTaskItems(taskItems); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); taskRequest.getTaskInfo().setParentQueueId(parentQueueId); taskRequest.getTaskInfo().setUserId(userId); apiExecuteService.batchExecute(taskRequest); } - public ApiReportStep getApiReportStep(String resourceId, ApiTestCase apiTestCase, String reportId, long sort) { + private ExecTask initExecTask(int caseSize, ApiRunModeConfigDTO runModeConfig, Project project, String userId) { + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(Long.valueOf(caseSize)); + if (runModeConfig.isIntegratedReport()) { + execTask.setTaskName(runModeConfig.getCollectionReport().getReportName()); + } else { + execTask.setTaskName(Translator.get("api_batch_task_name")); + } + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.TEST_PLAN_API_CASE.name()); + baseTaskHubService.insertExecTask(execTask); + return execTask; + } + + public List getSelectIdAndCollectionId(TestPlanApiCaseBatchRequest request) { + if (request.isSelectAll()) { + List testPlanApiCases = extTestPlanApiCaseMapper.getSelectIdAndCollectionId(request); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + testPlanApiCases.removeAll(request.getExcludeIds()); + } + return testPlanApiCases; + } else { + return getBatchRunInfo(request.getSelectIds()); + } + } + + private List getBatchRunInfo(List ids) { + List testPlanApiCases = new ArrayList<>(); + SubListUtils.dealForSubList(ids, 200, (subIds) -> testPlanApiCases.addAll(extTestPlanApiCaseMapper.getBatchRunInfoByIds(subIds))); + + // 查询用例名称信息 + List caseIds = testPlanApiCases.stream().map(TestPlanApiCaseBatchRunDTO::getApiCaseId).collect(Collectors.toList()); + Map apiTestCaseNameMap = extApiTestCaseMapper.getNameInfo(caseIds) + .stream() + .collect(Collectors.toMap(ApiTestCase::getId, ApiTestCase::getName)); + + Map testPlanApiCaseMap = testPlanApiCases + .stream() + .collect(Collectors.toMap(TestPlanApiCaseBatchRunDTO::getId, Function.identity())); + + testPlanApiCases.clear(); + // 按ID的顺序排序 + for (String id : ids) { + TestPlanApiCaseBatchRunDTO testPlanApiCase = testPlanApiCaseMap.get(id); + if (testPlanApiCase != null) { + testPlanApiCase.setName(apiTestCaseNameMap.get(testPlanApiCase.getApiCaseId())); + testPlanApiCases.add(testPlanApiCase); + } + } + return testPlanApiCases; + } + + private List initExecTaskItem(List apiTestCases, String userId, Project project, ExecTask execTask) { + List execTaskItems = new ArrayList<>(apiTestCases.size()); + for (TestPlanApiCaseBatchRunDTO apiTestCase : apiTestCases) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_CASE.name()); + execTaskItem.setResourceId(apiTestCase.getId()); + execTaskItem.setResourceName(apiTestCase.getName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + return execTaskItems; + } + + public ApiReportStep getApiReportStep(String resourceId, String name, String reportId, long sort) { ApiReportStep apiReportStep = new ApiReportStep(); apiReportStep.setReportId(reportId); apiReportStep.setStepId(resourceId); apiReportStep.setSort(sort); - apiReportStep.setName(apiTestCase.getName()); + apiReportStep.setName(name); apiReportStep.setStepType(ApiExecuteResourceType.API_CASE.name()); return apiReportStep; } @@ -291,13 +368,18 @@ public class TestPlanApiCaseBatchRunService { } ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId()); + String testPlanId = testPlanApiCase.getTestPlanId(); + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); // 独立报告,执行到当前任务时初始化报告 - String reportId = initApiReport(runModeConfig, List.of(testPlanApiCase), Map.of(apiTestCase.getId(), apiTestCase), queue.getUserId()).get(testPlanApiCase.getId()); + String reportId = initApiReport(runModeConfig, List.of(BeanUtils.copyBean(new TestPlanApiCaseBatchRunDTO(), testPlanApiCase)), + testPlan.getProjectId(), queue.getUserId()).get(testPlanApiCase.getId()); TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, testPlanApiCase.getId(), apiTestCase, runModeConfig); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setParentQueueId(queue.getParentQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); + taskRequest.getTaskItem().setId(queueDetail.getTaskItemId()); taskRequest.getTaskItem().setRequestCount(1L); apiExecuteService.execute(taskRequest); @@ -331,22 +413,21 @@ public class TestPlanApiCaseBatchRunService { * @param runModeConfig * @return */ - public Map initApiReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiCases, - Map caseMap, String userId) { + public Map initApiReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiCases, + String projectId, String userId) { List apiReports = new ArrayList<>(); List apiTestCaseRecords = new ArrayList<>(); List apiReportSteps = new ArrayList<>(); Map resourceReportMap = new HashMap<>(); - for (TestPlanApiCase testPlanApiCase : testPlanApiCases) { - ApiTestCase apiTestCase = caseMap.get(testPlanApiCase.getApiCaseId()); + for (TestPlanApiCaseBatchRunDTO testPlanApiCase : testPlanApiCases) { // 初始化报告 - ApiReport apiReport = getApiReport(runModeConfig, testPlanApiCase, apiTestCase, userId); + ApiReport apiReport = getApiReport(runModeConfig, testPlanApiCase, projectId, userId); apiReports.add(apiReport); // 创建报告和用例的关联关系 - ApiTestCaseRecord apiTestCaseRecord = apiTestCaseService.getApiTestCaseRecord(apiTestCase, apiReport); + ApiTestCaseRecord apiTestCaseRecord = apiTestCaseService.getApiTestCaseRecord(testPlanApiCase.getApiCaseId(), apiReport); apiTestCaseRecords.add(apiTestCaseRecord); - apiReportSteps.add(getApiReportStep(testPlanApiCase.getId(), apiTestCase, apiReport.getId(), 1)); + apiReportSteps.add(getApiReportStep(testPlanApiCase.getId(), testPlanApiCase.getName(), apiReport.getId(), 1)); resourceReportMap.put(testPlanApiCase.getId(), apiReport.getId()); } apiReportService.insertApiReport(apiReports, apiTestCaseRecords); @@ -354,9 +435,12 @@ public class TestPlanApiCaseBatchRunService { return resourceReportMap; } - private ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase, String userId) { - ApiReport apiReport = apiTestCaseBatchRunService.getApiReport(runModeConfig, apiTestCase, userId); - apiReport.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanApiCase.getEnvironmentId())); + private ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiCaseBatchRunDTO testPlanApiCase, String projectId, String userId) { + ApiReport apiReport = apiTestCaseBatchRunService.getApiReport(runModeConfig, userId); + apiReport.setEnvironmentId(apiTestCaseService.getEnvId(runModeConfig, testPlanApiCase.getEnvironmentId())); + apiReport.setName(testPlanApiCase.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiReport.setProjectId(projectId); + apiReport.setTriggerMode(TaskTriggerMode.BATCH.name()); apiReport.setTestPlanCaseId(testPlanApiCase.getId()); return apiReport; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java index 51f6f102a6..db0ba0fb01 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java @@ -10,6 +10,7 @@ import io.metersphere.api.mapper.ApiTestCaseMapper; import io.metersphere.api.mapper.ExtApiDefinitionModuleMapper; import io.metersphere.api.mapper.ExtApiTestCaseMapper; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionService; @@ -45,6 +46,8 @@ import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.SubListUtils; import io.metersphere.sdk.util.Translator; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.dto.LogInsertModule; import io.metersphere.system.dto.ModuleSelectDTO; import io.metersphere.system.dto.sdk.BaseTreeNode; @@ -122,6 +125,8 @@ public class TestPlanApiCaseService extends TestPlanResourceService { private static final String DEBUG_MODULE_COUNT_ALL = "all"; @Resource private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ApiCommonService apiCommonService; private static final String EXECUTOR = "executeUserName"; @@ -557,32 +562,6 @@ public class TestPlanApiCaseService extends TestPlanResourceService { } } - public List getSelectIdAndCollectionId(TestPlanApiCaseBatchRequest request) { - if (request.isSelectAll()) { - List testPlanApiCases = extTestPlanApiCaseMapper.getSelectIdAndCollectionId(request); - if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { - testPlanApiCases.removeAll(request.getExcludeIds()); - } - return testPlanApiCases; - } else { - TestPlanApiCaseExample example = new TestPlanApiCaseExample(); - example.createCriteria().andIdIn(request.getSelectIds()); - Map testPlanApiCaseMap = testPlanApiCaseMapper.selectByExample(example) - .stream() - .collect(Collectors.toMap(TestPlanApiCase::getId, Function.identity())); - List testPlanApiCases = new ArrayList<>(request.getSelectIds().size()); - // 按ID的顺序排序 - for (String id : request.getSelectIds()) { - TestPlanApiCase testPlanApiCase = testPlanApiCaseMap.get(id); - if (testPlanApiCase != null) { - testPlanApiCases.add(testPlanApiCase); - } - } - return testPlanApiCases; - } - } - - /** * 批量更新执行人 * @@ -772,12 +751,30 @@ public class TestPlanApiCaseService extends TestPlanResourceService { runModeConfig.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanApiCase.getEnvironmentId())); runModeConfig.setRunMode(ApiBatchRunMode.PARALLEL.name()); TaskRequestDTO taskRequest = getTaskRequest(reportId, id, apiTestCase.getProjectId(), ApiExecuteRunMode.RUN.name()); + + Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId()); + + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(1L); + execTask.setTaskName(apiTestCase.getName()); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.TEST_PLAN_API_CASE.name()); + + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_CASE.name()); + execTaskItem.setResourceId(apiTestCase.getId()); + execTaskItem.setResourceName(apiTestCase.getName()); + TaskInfo taskInfo = taskRequest.getTaskInfo(); TaskItem taskItem = taskRequest.getTaskItem(); + taskInfo.setTaskId(execTask.getId()); taskInfo.setRunModeConfig(runModeConfig); taskInfo.setSaveResult(true); taskInfo.setRealTime(true); taskInfo.setUserId(userId); + taskItem.setId(execTaskItem.getId()); if (StringUtils.isEmpty(taskItem.getReportId())) { taskInfo.setRealTime(false); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java index a5b70e2e1c..d3a5b779bd 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java @@ -6,6 +6,7 @@ import io.metersphere.api.domain.ApiScenarioReport; import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.scenario.ApiScenarioBatchRunService; @@ -15,22 +16,23 @@ import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.domain.TestPlanCollection; import io.metersphere.plan.domain.TestPlanCollectionExample; +import io.metersphere.plan.dto.TestPlanApiScenarioBatchRunDTO; import io.metersphere.plan.dto.request.ApiExecutionMapService; import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest; import io.metersphere.plan.mapper.*; -import io.metersphere.sdk.constants.ApiExecuteResourceType; -import io.metersphere.sdk.constants.CaseType; -import io.metersphere.sdk.constants.CommonConstants; -import io.metersphere.sdk.constants.TaskTriggerMode; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.*; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; -import io.metersphere.sdk.util.CommonBeanFactory; -import io.metersphere.sdk.util.DateUtils; -import io.metersphere.sdk.util.LogUtils; -import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -43,8 +45,6 @@ import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) public class TestPlanApiScenarioBatchRunService { - @Resource - private TestPlanApiScenarioService testPlanApiScenarioService; @Resource private ApiExecuteService apiExecuteService; @Resource @@ -68,6 +68,8 @@ public class TestPlanApiScenarioBatchRunService { @Resource private TestPlanMapper testPlanMapper; @Resource + private ProjectMapper projectMapper; + @Resource private TestPlanApiBatchRunBaseService testPlanApiBatchRunBaseService; @Resource private ApiExecutionMapService apiExecutionMapService; @@ -75,6 +77,10 @@ public class TestPlanApiScenarioBatchRunService { private TestPlanCollectionMapper testPlanCollectionMapper; @Resource private ExtTestPlanMapper extTestPlanMapper; + @Resource + private ApiCommonService apiCommonService; + @Resource + private BaseTaskHubService baseTaskHubService; /** * 异步批量执行 @@ -96,9 +102,9 @@ public class TestPlanApiScenarioBatchRunService { */ private void batchRun(TestPlanApiScenarioBatchRunRequest request, String userId) { try { - List testPlanApiCases = testPlanApiScenarioService.getSelectIdAndCollectionId(request); + List testPlanApiScenarios = getSelectIdAndCollectionId(request); // 按照 testPlanCollectionId 分组, value 为测试计划用例 ID 列表 - Map> collectionMap = getCollectionMap(testPlanApiCases); + Map> collectionMap = getCollectionMap(testPlanApiScenarios); List testPlanCollections = getTestPlanCollections(request.getTestPlanId()); Iterator iterator = testPlanCollections.iterator(); @@ -121,18 +127,19 @@ public class TestPlanApiScenarioBatchRunService { .collect(Collectors.toList()); TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); if (apiBatchRunBaseService.isParallel(rootCollection.getExecuteMethod())) { // 并行执行测试集 for (TestPlanCollection collection : testPlanCollections) { - List ids = collectionMap.get(collection.getId()); + List collectionCases = collectionMap.get(collection.getId()); ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(rootCollection, collection); if (apiBatchRunBaseService.isParallel(runModeConfig.getRunMode())) { // 并行执行测试集中的用例 - parallelExecute(ids, runModeConfig, testPlan.getProjectId(), null, userId); + parallelExecute(collectionCases, runModeConfig, null, project, userId); } else { // 串行执行测试集中的用例 - serialExecute(ids, runModeConfig, null, userId); + serialExecute(collectionCases, runModeConfig, null, project, userId); } } } else { @@ -141,8 +148,12 @@ public class TestPlanApiScenarioBatchRunService { // 生成测试集队列 ExecutionQueue collectionQueue = apiBatchRunBaseService.initExecutionqueue(serialCollectionIds, ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name(), userId); + + Map> collectionIdMap = collectionMap.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().map(TestPlanApiScenarioBatchRunDTO::getId).toList())); + // 记录各测试集中要执行的用例 - apiExecutionMapService.initMap(collectionQueue.getQueueId(), collectionMap); + apiExecutionMapService.initMap(collectionQueue.getQueueId(), collectionIdMap); executeNextCollection(collectionQueue.getQueueId()); } @@ -160,12 +171,12 @@ public class TestPlanApiScenarioBatchRunService { return testPlanCollections; } - private Map> getCollectionMap(List testPlanApiScenarios) { - Map> collectionMap = new HashMap<>(); - for (TestPlanApiScenario testPlanApiScenario : testPlanApiScenarios) { + private Map> getCollectionMap(List testPlanApiScenarios) { + Map> collectionMap = new HashMap<>(); + for (TestPlanApiScenarioBatchRunDTO testPlanApiScenario : testPlanApiScenarios) { collectionMap.putIfAbsent(testPlanApiScenario.getTestPlanCollectionId(), new ArrayList<>()); - List ids = collectionMap.get(testPlanApiScenario.getTestPlanCollectionId()); - ids.add(testPlanApiScenario.getId()); + collectionMap.get(testPlanApiScenario.getTestPlanCollectionId()) + .add(testPlanApiScenario); } return collectionMap; } @@ -180,14 +191,17 @@ public class TestPlanApiScenarioBatchRunService { ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(collectionQueueId); String collectionId = nextDetail.getResourceId(); List ids = apiExecutionMapService.getAndRemove(collectionQueueId, collectionId); + List testPlanApiScenarios = getBatchRunInfo(ids); + TestPlanCollection collection = testPlanCollectionMapper.selectByPrimaryKey(collectionId); + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(collection.getTestPlanId()); + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); + ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(collection); if (apiBatchRunBaseService.isParallel(runModeConfig.getRunMode())) { - String testPlanId = collection.getTestPlanId(); - TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); - parallelExecute(ids, runModeConfig, testPlan.getProjectId(), collectionQueueId, userId); + parallelExecute(testPlanApiScenarios, runModeConfig, collectionQueueId, project, userId); } else { - serialExecute(ids, runModeConfig, collectionQueueId, userId); + serialExecute(testPlanApiScenarios, runModeConfig, collectionQueueId, project, userId); } } @@ -199,9 +213,15 @@ public class TestPlanApiScenarioBatchRunService { * 串行批量执行 * */ - public void serialExecute(List ids, ApiRunModeConfigDTO runModeConfig, String parentQueueId, String userId) { + public void serialExecute(List testPlanApiScenarios, ApiRunModeConfigDTO runModeConfig, String parentQueueId, Project project, String userId) { + // 初始化任务 + ExecTask execTask = initExecTask(testPlanApiScenarios.size(), runModeConfig, project, userId); + // 初始化任务项 + List execTaskItems = initExecTaskItem(testPlanApiScenarios, userId, project, execTask); // 先初始化集成报告,设置好报告ID,再初始化执行队列 - ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name(), parentQueueId, userId); + ExecutionQueue queue = apiBatchRunBaseService.initExecutionQueue(execTask.getId(), runModeConfig, ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name(), parentQueueId, userId); + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); // 执行第一个任务 ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); executeNextTask(queue, nextDetail); @@ -211,65 +231,76 @@ public class TestPlanApiScenarioBatchRunService { * 并行批量执行 * */ - public void parallelExecute(List ids, ApiRunModeConfigDTO runModeConfig, String projectId, String parentQueueId, String userId) { + public void parallelExecute(List testPlanApiScenarios, ApiRunModeConfigDTO runModeConfig, String parentQueueId, Project project, String userId) { - Map scenarioReportMap = initReport(ids, runModeConfig, userId); + Map scenarioReportMap = initReport(testPlanApiScenarios, runModeConfig, project.getId(), userId); - List taskItems = ids.stream() - .map(id -> apiExecuteService.getTaskItem(scenarioReportMap.get(id), id)).toList(); + // 初始化任务 + ExecTask execTask = initExecTask(testPlanApiScenarios.size(), runModeConfig, project, userId); - TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(projectId, runModeConfig); + // 初始化任务项 + Map resourceExecTaskItemMap = initExecTaskItem(testPlanApiScenarios, userId, project, execTask) + .stream() + .collect(Collectors.toMap(ExecTaskItem::getResourceId, ExecTaskItem::getId)); + + List taskItems = testPlanApiScenarios.stream() + .map(testPlanApiScenario -> { + String id = testPlanApiScenario.getId(); + TaskItem taskItem = apiExecuteService.getTaskItem(scenarioReportMap.get(id), id); + taskItem.setId(resourceExecTaskItemMap.get(id)); + return taskItem; + }).toList(); + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(project.getId(), runModeConfig); taskRequest.setTaskItems(taskItems); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); taskRequest.getTaskInfo().setUserId(userId); taskRequest.getTaskInfo().setParentQueueId(parentQueueId); apiExecuteService.batchExecute(taskRequest); } - private Map initReport(List ids, ApiRunModeConfigDTO runModeConfig, String userId) { - List testPlanApiScenarios = new ArrayList<>(ids.size()); - - List apiScenarios = new ArrayList<>(ids.size()); - // 分批查询 - List finalTestPlanApiScenarios = testPlanApiScenarios; - SubListUtils.dealForSubList(ids, 100, subIds -> finalTestPlanApiScenarios.addAll(extTestPlanApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); - - List caseIds = testPlanApiScenarios.stream().map(TestPlanApiScenario::getApiScenarioId).toList(); - List finalApiScenarios = apiScenarios; - SubListUtils.dealForSubList(caseIds, 100, subIds -> finalApiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); - - Map apiScenarioMap = apiScenarios.stream() - .collect(Collectors.toMap(ApiScenario::getId, Function.identity())); - - Map testPlanApiScenarioMap = testPlanApiScenarios.stream() - .collect(Collectors.toMap(TestPlanApiScenario::getId, Function.identity())); - - testPlanApiScenarios = new ArrayList<>(ids.size()); - for (String id : ids) { - // 按照ID顺序排序 - TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMap.get(id); - if (testPlanApiScenario == null) { - break; - } - testPlanApiScenarios.add(testPlanApiScenario); + private ExecTask initExecTask(int caseSize, ApiRunModeConfigDTO runModeConfig, Project project, String userId) { + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(Long.valueOf(caseSize)); + if (runModeConfig.isIntegratedReport()) { + execTask.setTaskName(runModeConfig.getCollectionReport().getReportName()); + } else { + execTask.setTaskName(Translator.get("api_batch_task_name")); } - // 初始化独立报告,执行时初始化步骤 - return initScenarioReport(runModeConfig, testPlanApiScenarios, apiScenarioMap, userId); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.TEST_PLAN_API_SCENARIO.name()); + baseTaskHubService.insertExecTask(execTask); + return execTask; } - public Map initScenarioReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiScenarios, - Map apiScenarioMap, String userId) { + private List initExecTaskItem(List apiTestCases, String userId, Project project, ExecTask execTask) { + List execTaskItems = new ArrayList<>(apiTestCases.size()); + for (TestPlanApiScenarioBatchRunDTO testPlanApiScenario : apiTestCases) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name()); + execTaskItem.setResourceId(testPlanApiScenario.getId()); + execTaskItem.setResourceName(testPlanApiScenario.getName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + return execTaskItems; + } + + public Map initReport( List testPlanApiScenarios, + ApiRunModeConfigDTO runModeConfig, String projectId, String userId) { List apiScenarioReports = new ArrayList<>(testPlanApiScenarios.size()); List apiScenarioRecords = new ArrayList<>(testPlanApiScenarios.size()); Map resourceReportMap = new HashMap<>(); - for (TestPlanApiScenario testPlanApiScenario : testPlanApiScenarios) { - ApiScenario apiScenario = apiScenarioMap.get(testPlanApiScenario.getApiScenarioId()); + for (TestPlanApiScenarioBatchRunDTO testPlanApiScenario : testPlanApiScenarios) { // 初始化报告 - ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, userId); + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, projectId, userId); apiScenarioReport.setId(IDGenerator.nextStr()); apiScenarioReports.add(apiScenarioReport); // 创建报告和用例的关联关系 - ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport); + ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(testPlanApiScenario.getApiScenarioId(), apiScenarioReport); apiScenarioRecords.add(apiScenarioRecord); resourceReportMap.put(testPlanApiScenario.getId(), apiScenarioReport.getId()); } @@ -287,14 +318,19 @@ public class TestPlanApiScenarioBatchRunService { ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(queueDetail.getResourceId()); ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(testPlanApiScenario.getApiScenarioId()); + String testPlanId = testPlanApiScenario.getTestPlanId(); + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); // 独立报告,执行到当前任务时初始化报告 - String reportId = initScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, queue.getUserId()).getApiScenarioReportId(); + String reportId = initScenarioReport(runModeConfig, BeanUtils.copyBean(new TestPlanApiScenarioBatchRunDTO(), testPlanApiScenario), testPlan.getId(), queue.getUserId()) + .getApiScenarioReportId(); TaskRequestDTO taskRequest = getTaskRequestDTO(apiScenario.getProjectId(), queue.getRunModeConfig()); TaskItem taskItem = apiExecuteService.getTaskItem(reportId, queueDetail.getResourceId()); + taskItem.setId(queueDetail.getTaskItemId()); taskRequest.setTaskItem(taskItem); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); + taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); taskRequest.getTaskInfo().setParentQueueId(queue.getParentQueueId()); @@ -308,6 +344,44 @@ public class TestPlanApiScenarioBatchRunService { return taskRequest; } + public List getSelectIdAndCollectionId(TestPlanApiScenarioBatchRunRequest request) { + if (request.isSelectAll()) { + List testPlanApiCases = extTestPlanApiScenarioMapper.getSelectIdAndCollectionId(request); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + testPlanApiCases.removeAll(request.getExcludeIds()); + } + return testPlanApiCases; + } else { + return getBatchRunInfo(request.getSelectIds()); + } + } + + private List getBatchRunInfo(List ids) { + List testPlanApiScenarios = new ArrayList<>(); + SubListUtils.dealForSubList(ids, 200, (subIds) -> testPlanApiScenarios.addAll(extTestPlanApiScenarioMapper.getBatchRunInfoByIds(subIds))); + + // 查询用例名称信息 + List caseIds = testPlanApiScenarios.stream().map(TestPlanApiScenarioBatchRunDTO::getApiScenarioId).collect(Collectors.toList()); + Map apiScenarioNameMap = extApiScenarioMapper.getNameInfo(caseIds) + .stream() + .collect(Collectors.toMap(ApiScenario::getId, ApiScenario::getName)); + + Map testPlanApiCaseMap = testPlanApiScenarios + .stream() + .collect(Collectors.toMap(TestPlanApiScenarioBatchRunDTO::getId, Function.identity())); + + testPlanApiScenarios.clear(); + // 按ID的顺序排序 + for (String id : ids) { + TestPlanApiScenarioBatchRunDTO testPlanApiCase = testPlanApiCaseMap.get(id); + if (testPlanApiCase != null) { + testPlanApiCase.setName(apiScenarioNameMap.get(testPlanApiCase.getApiScenarioId())); + testPlanApiScenarios.add(testPlanApiCase); + } + } + return testPlanApiScenarios; + } + public TaskBatchRequestDTO getTaskBatchRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskBatchRequestDTO taskRequest = new TaskBatchRequestDTO(); TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig); @@ -325,35 +399,29 @@ public class TestPlanApiScenarioBatchRunService { * 预生成用例的执行报告 * * @param runModeConfig - * @param apiScenario + * @param testPlanApiScenario * @return */ - public ApiScenarioRecord initScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenario testPlanApiScenario, - ApiScenario apiScenario, String userId) { + public ApiScenarioRecord initScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenarioBatchRunDTO testPlanApiScenario, String projectId, String userId) { // 初始化报告 - ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, userId); + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, projectId, userId); apiScenarioReport.setId(IDGenerator.nextStr()); // 创建报告和用例的关联关系 - ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport); + ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(testPlanApiScenario.getApiScenarioId(), apiScenarioReport); apiScenarioReportService.insertApiScenarioReport(List.of(apiScenarioReport), List.of(apiScenarioRecord)); return apiScenarioRecord; } - private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenario testPlanApiScenario, ApiScenario apiScenario, String userId) { - ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, apiScenario, userId); - apiScenarioReport.setTestPlanScenarioId(testPlanApiScenario.getId()); - apiScenarioReport.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanApiScenario.getEnvironmentId())); - return apiScenarioReport; - } - - public ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario, String userId) { + private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenarioBatchRunDTO testPlanApiScenario, String projectId, String userId) { ApiScenarioReport apiScenarioReport = apiScenarioRunService.getScenarioReport(userId); - apiScenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); - apiScenarioReport.setProjectId(apiScenario.getProjectId()); + apiScenarioReport.setName(testPlanApiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiScenarioReport.setProjectId(projectId); apiScenarioReport.setEnvironmentId(runModeConfig.getEnvironmentId()); apiScenarioReport.setRunMode(runModeConfig.getRunMode()); apiScenarioReport.setPoolId(runModeConfig.getPoolId()); apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name()); + apiScenarioReport.setTestPlanScenarioId(testPlanApiScenario.getId()); + apiScenarioReport.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanApiScenario.getEnvironmentId())); return apiScenarioReport; } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java index 3d223db8a0..a9ef9302f9 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java @@ -8,6 +8,7 @@ import io.metersphere.api.mapper.ApiScenarioModuleMapper; import io.metersphere.api.mapper.ApiScenarioReportMapper; import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.scenario.ApiScenarioModuleService; import io.metersphere.api.service.scenario.ApiScenarioReportService; @@ -43,6 +44,8 @@ import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.SubListUtils; import io.metersphere.sdk.util.Translator; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.dto.LogInsertModule; import io.metersphere.system.dto.ModuleSelectDTO; import io.metersphere.system.dto.sdk.BaseTreeNode; @@ -113,6 +116,8 @@ public class TestPlanApiScenarioService extends TestPlanResourceService { private ExtApiScenarioMapper extApiScenarioMapper; @Resource private TestPlanConfigService testPlanConfigService; + @Resource + private ApiCommonService apiCommonService; private static final String EXECUTOR = "executeUserName"; @@ -331,13 +336,33 @@ public class TestPlanApiScenarioService extends TestPlanResourceService { ApiRunModeConfigDTO runModeConfig = testPlanApiBatchRunBaseService.getApiRunModeConfig(testPlanApiScenario.getTestPlanCollectionId()); runModeConfig.setEnvironmentId(apiBatchRunBaseService.getEnvId(runModeConfig, testPlanApiScenario.getEnvironmentId())); TaskRequestDTO taskRequest = getTaskRequest(reportId, id, apiScenario.getProjectId(), ApiExecuteRunMode.RUN.name()); + + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanApiScenario.getTestPlanId()); + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); + + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setCaseCount(1L); + execTask.setTaskName(apiScenario.getName()); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.TEST_PLAN_API_SCENARIO.name()); + + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(execTask.getId(), project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name()); + execTaskItem.setResourceId(testPlanApiScenario.getId()); + execTaskItem.setResourceName(apiScenario.getName()); + TaskInfo taskInfo = taskRequest.getTaskInfo(); - TaskItem taskItem = taskRequest.getTaskItem(); + taskInfo.setTaskId(execTask.getId()); taskInfo.setRunModeConfig(runModeConfig); taskInfo.setSaveResult(true); taskInfo.setRealTime(true); taskInfo.setUserId(userId); + TaskItem taskItem = taskRequest.getTaskItem(); + taskItem.setId(execTaskItem.getId()); + if (StringUtils.isEmpty(taskItem.getReportId())) { taskInfo.setRealTime(false); reportId = IDGenerator.nextStr(); @@ -748,31 +773,6 @@ public class TestPlanApiScenarioService extends TestPlanResourceService { SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } - public List getSelectIdAndCollectionId(TestPlanApiScenarioBatchRunRequest request) { - if (request.isSelectAll()) { - List testPlanApiCases = extTestPlanApiScenarioMapper.getSelectIdAndCollectionId(request); - if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { - testPlanApiCases.removeAll(request.getExcludeIds()); - } - return testPlanApiCases; - } else { - TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); - example.createCriteria().andIdIn(request.getSelectIds()); - Map testPlanApiScenarioMap = testPlanApiScenarioMapper.selectByExample(example) - .stream() - .collect(Collectors.toMap(TestPlanApiScenario::getId, Function.identity())); - List testPlanApiScenarios = new ArrayList<>(request.getSelectIds().size()); - // 按ID的顺序排序 - for (String id : request.getSelectIds()) { - TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMap.get(id); - if (testPlanApiScenario != null) { - testPlanApiScenarios.add(testPlanApiScenario); - } - } - return testPlanApiScenarios; - } - } - /** * 处理执行人为空过滤参数 * diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanExecuteService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanExecuteService.java index b23ff63de2..24ee611e5f 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanExecuteService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanExecuteService.java @@ -1,16 +1,21 @@ package io.metersphere.plan.service; import com.esotericsoftware.minlog.Log; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.plan.domain.*; import io.metersphere.plan.dto.request.TestPlanBatchExecuteRequest; import io.metersphere.plan.dto.request.TestPlanExecuteRequest; import io.metersphere.plan.dto.request.TestPlanReportGenRequest; import io.metersphere.plan.mapper.*; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.queue.TestPlanExecutionQueue; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections4.CollectionUtils; @@ -52,6 +57,16 @@ public class TestPlanExecuteService { private TestPlanReportMapper testPlanReportMapper; @Resource private TestPlanExecuteSupportService testPlanExecuteSupportService; + @Resource + private ApiCommonService apiCommonService; + @Resource + private BaseTaskHubService baseTaskHubService; + @Resource + private ProjectMapper projectMapper; + @Resource + private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; + @Resource + private ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper; // 停止测试计划的执行 public void stopTestPlanRunning(String testPlanReportId) { @@ -139,7 +154,8 @@ public class TestPlanExecuteService { request.getExecuteId(), request.getRunMode(), request.getExecutionSource(), - reportId + reportId, + IDGenerator.nextStr() ); testPlanExecuteSupportService.setRedisForList( @@ -180,6 +196,7 @@ public class TestPlanExecuteService { testPlanId, runMode, TaskTriggerMode.BATCH.name(), + IDGenerator.nextStr(), IDGenerator.nextStr() ) ); @@ -208,6 +225,12 @@ public class TestPlanExecuteService { throw new MSException("test_plan.error"); } + Project project = projectMapper.selectByPrimaryKey(testPlan.getProjectId()); + Integer caseTotal = extTestPlanApiCaseMapper.countByPlanId(testPlan.getId()) + extTestPlanApiScenarioMapper.countByPlanId(testPlan.getId()); + + // 初始化任务 + ExecTask execTask = initExecTask(executionQueue.getTaskId(), caseTotal, testPlan.getName(), project, executionQueue.getCreateUser(), executionQueue.getExecutionSource()); + TestPlanReportGenRequest genReportRequest = new TestPlanReportGenRequest(); genReportRequest.setTriggerMode(executionQueue.getExecutionSource()); genReportRequest.setTestPlanId(executionQueue.getSourceID()); @@ -216,7 +239,7 @@ public class TestPlanExecuteService { List children = testPlanService.selectNotArchivedChildren(testPlan.getId()); // 预生成计划组报告 - Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(), genReportRequest, executionQueue.getCreateUser()); + Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(), execTask.getId(), genReportRequest, executionQueue.getCreateUser()); long pos = 0; List childrenQueue = new ArrayList<>(); @@ -235,7 +258,8 @@ public class TestPlanExecuteService { child.getId(), executionQueue.getRunMode(), executionQueue.getExecutionSource(), - reportMap.get(child.getId()) + reportMap.get(child.getId()), + executionQueue.getTaskId() ) ); } @@ -265,7 +289,7 @@ public class TestPlanExecuteService { return executionQueue.getPrepareReportId(); } else { - Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(), genReportRequest, executionQueue.getCreateUser()); + Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(), execTask.getId(), genReportRequest, executionQueue.getCreateUser()); executionQueue.setPrepareReportId(reportMap.get(executionQueue.getSourceID())); testPlanService.setExecuteConfig(executionQueue.getSourceID(), executionQueue.getPrepareReportId()); this.executeTestPlan(executionQueue); @@ -273,6 +297,18 @@ public class TestPlanExecuteService { } } + private ExecTask initExecTask(String taskId, int caseSize, String name, Project project, String userId, String triggerMode) { + ExecTask execTask = apiCommonService.newExecTask(project.getId(), userId); + execTask.setId(taskId); + execTask.setCaseCount(Long.valueOf(caseSize)); + execTask.setTaskName(name); + execTask.setOrganizationId(project.getOrganizationId()); + execTask.setTriggerMode(TaskTriggerMode.MANUAL.name()); + execTask.setTaskType(ExecTaskType.TEST_PLAN.name()); + baseTaskHubService.insertExecTask(execTask); + return execTask; + } + //执行测试计划里不同类型的用例 回调:caseTypeExecuteQueueFinish @Transactional(propagation = Propagation.NOT_SUPPORTED) public void executeTestPlan(TestPlanExecutionQueue executionQueue) { @@ -310,7 +346,8 @@ public class TestPlanExecuteService { collection.getId(), runMode, executionQueue.getExecutionSource(), - executionQueue.getPrepareReportId()) + executionQueue.getPrepareReportId(), + executionQueue.getTaskId()) ); } LogUtils.info("测试计划执行节点 --- 队列ID[{}],队列类型[{}],父队列ID[{}],父队列类型[{}],执行模式[{}]", queueId, queueType, executionQueue.getParentQueueId(), executionQueue.getParentQueueType(), runMode); @@ -362,7 +399,8 @@ public class TestPlanExecuteService { collection.getId(), runMode, executionQueue.getExecutionSource(), - executionQueue.getPrepareReportId()) {{ + executionQueue.getPrepareReportId(), + executionQueue.getTaskId()) {{ this.setTestPlanCollectionJson(JSON.toJSONString(collection)); }} ); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanReportService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanReportService.java index 9675d31bcd..a2b44f8599 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanReportService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanReportService.java @@ -1,6 +1,7 @@ package io.metersphere.plan.service; import com.google.common.collect.Maps; +import io.metersphere.api.service.ApiCommonService; import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.service.BugCommonService; import io.metersphere.plan.constants.AssociateCaseType; @@ -16,6 +17,8 @@ import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType; import io.metersphere.plan.mapper.*; import io.metersphere.plan.utils.CountUtils; import io.metersphere.plugin.platform.dto.SelectOption; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.file.FileCenter; @@ -23,11 +26,13 @@ import io.metersphere.sdk.file.FileCopyRequest; import io.metersphere.sdk.file.FileRepository; import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.domain.User; import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.mapper.BaseUserMapper; import io.metersphere.system.mapper.UserMapper; import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.service.CommonFileService; import io.metersphere.system.service.FileService; import io.metersphere.system.service.SimpleUserService; @@ -110,6 +115,12 @@ public class TestPlanReportService { private TestPlanReportComponentMapper componentMapper; @Resource private CommonFileService commonFileService; + @Resource + private ApiCommonService apiCommonService; + @Resource + private BaseTaskHubService baseTaskHubService; + @Resource + private ProjectMapper projectMapper; private static final int MAX_REPORT_NAME_LENGTH = 300; @@ -308,8 +319,13 @@ public class TestPlanReportService { * @param currentUser 当前用户 */ @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) - public Map genReportByExecution(String prepareReportId, TestPlanReportGenRequest request, String currentUser) { - return genReport(prepareReportId, request, false, currentUser, null); + public Map genReportByExecution(String prepareReportId, String taskId, TestPlanReportGenRequest request, String currentUser) { + return genReport(prepareReportId, taskId, request, false, currentUser, null); + } + + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + public Map genReport(String prepareReportId, TestPlanReportGenRequest request, boolean manual, String currentUser, String manualReportName) { + return genReport(prepareReportId, null, request, manual, currentUser, manualReportName); } /** @@ -321,7 +337,7 @@ public class TestPlanReportService { * @param currentUser 当前用户 * @param manualReportName 手动生成报告名称 */ - public Map genReport(String prepareReportId, TestPlanReportGenRequest request, boolean manual, String currentUser, String manualReportName) { + public Map genReport(String prepareReportId, String taskId, TestPlanReportGenRequest request, boolean manual, String currentUser, String manualReportName) { Map preReportMap = Maps.newHashMapWithExpectedSize(8); TestPlanReportManualParam reportManualParam = TestPlanReportManualParam.builder().manualName(manualReportName).targetId(request.getTestPlanId()).build(); try { @@ -345,6 +361,7 @@ public class TestPlanReportService { genPreParam.setStartTime(null); } genPreParam.setUseManual(manual); + genPreParam.setTaskId(taskId); // 如果是测试计划的独立报告,使用参数中的预生成的报告id。否则只有测试计划组报告使用该id String prepareItemReportId = isGroupReports ? IDGenerator.nextStr() : prepareReportId; TestPlanReport preReport = preGenReport(prepareItemReportId, genPreParam, currentUser, childPlanIds, reportManualParam); @@ -437,6 +454,8 @@ public class TestPlanReportService { List bugCountList = extTestPlanReportBugMapper.countPlanBug(genParam.getTestPlanId()); Map bugCountMap = bugCountList.stream().collect(Collectors.toMap(ReportBugCountDTO::getRefCaseId, ReportBugCountDTO::getBugCount)); + Project project = projectMapper.selectByPrimaryKey(genParam.getProjectId()); + AtomicLong funcCaseCount = new AtomicLong(); AtomicLong apiCaseCount = new AtomicLong(); AtomicLong apiScenarioCount = new AtomicLong(); @@ -525,6 +544,11 @@ public class TestPlanReportService { TestPlanReportApiCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportApiCaseMapper.class); batchMapper.batchInsert(reportApiCases); sqlSession.flushStatements(); + + if (StringUtils.isNotBlank(genParam.getTaskId())) { + reportApiCases.sort(Comparator.comparing(TestPlanReportApiCase::getPos).reversed()); + initApiCaseExecTaskItem(genParam.getTaskId(), reportApiCases, report.getCreateUser(), project); + } }); } testPlanReportApiCaseIdList = null; @@ -563,6 +587,11 @@ public class TestPlanReportService { TestPlanReportApiScenarioMapper batchMapper = sqlSession.getMapper(TestPlanReportApiScenarioMapper.class); batchMapper.batchInsert(reportApiScenarios); sqlSession.flushStatements(); + + if (StringUtils.isNotBlank(genParam.getTaskId())) { + reportApiScenarios.sort(Comparator.comparing(TestPlanReportApiScenario::getPos).reversed()); + initScenarioExecTaskItem(genParam.getTaskId(), reportApiScenarios, report.getCreateUser(), project); + } }); } reportApiScenarioIdList = null; @@ -611,6 +640,31 @@ public class TestPlanReportService { .functionCaseCount(funcCaseCount.get()).apiCaseCount(apiCaseCount.get()).apiScenarioCount(apiScenarioCount.get()).bugCount(bugCount.get()).build(); } + private void initApiCaseExecTaskItem(String taskId, List apiTestCases, String userId, Project project) { + List execTaskItems = new ArrayList<>(apiTestCases.size()); + for (TestPlanReportApiCase apiTestCase : apiTestCases) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(taskId, project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.PLAN_RUN_API_CASE.name()); + execTaskItem.setResourceId(apiTestCase.getId()); + execTaskItem.setResourceName(apiTestCase.getApiCaseName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + } + + private void initScenarioExecTaskItem(String taskId, List testPlanReportApiScenarios, String userId, Project project) { + List execTaskItems = new ArrayList<>(testPlanReportApiScenarios.size()); + for (TestPlanReportApiScenario testPlanReportApiScenario : testPlanReportApiScenarios) { + ExecTaskItem execTaskItem = apiCommonService.newExecTaskItem(taskId, project.getId(), userId); + execTaskItem.setOrganizationId(project.getOrganizationId()); + execTaskItem.setResourceType(ApiExecuteResourceType.PLAN_RUN_API_SCENARIO.name()); + execTaskItem.setResourceId(testPlanReportApiScenario.getId()); + execTaskItem.setResourceName(testPlanReportApiScenario.getApiScenarioName()); + execTaskItems.add(execTaskItem); + } + baseTaskHubService.insertExecTaskDetail(execTaskItems); + } /** * 报告结果后置处理 (汇总操作结束后调用)