diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTask.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTask.java index 94e0908d45..58261a8564 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTask.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTask.java @@ -81,6 +81,15 @@ public class ExecTask implements Serializable { @NotNull(message = "{exec_task.parallel.not_blank}", groups = {Created.class}) private Boolean parallel; + @Schema(description = "用例批量执行环境ID") + private String environmentId; + + @Schema(description = "资源池ID") + private String poolId; + + @Schema(description = "是否是环境组") + private Boolean envGrouped; + private static final long serialVersionUID = 1L; public enum Column { @@ -101,7 +110,10 @@ public class ExecTask implements Serializable { startTime("start_time", "startTime", "BIGINT", false), endTime("end_time", "endTime", "BIGINT", false), deleted("deleted", "deleted", "BIT", false), - parallel("parallel", "parallel", "BIT", false); + parallel("parallel", "parallel", "BIT", false), + environmentId("environment_id", "environmentId", "VARCHAR", false), + poolId("pool_id", "poolId", "VARCHAR", false), + envGrouped("env_grouped", "envGrouped", "BIT", false); private static final String BEGINNING_DELIMITER = "`"; diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTaskExample.java b/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTaskExample.java index 62e2900a72..338af5c4f7 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTaskExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/system/domain/ExecTaskExample.java @@ -1283,6 +1283,206 @@ public class ExecTaskExample { addCriterion("parallel not between", value1, value2, "parallel"); return (Criteria) this; } + + public Criteria andEnvironmentIdIsNull() { + addCriterion("environment_id is null"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdIsNotNull() { + addCriterion("environment_id is not null"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdEqualTo(String value) { + addCriterion("environment_id =", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdNotEqualTo(String value) { + addCriterion("environment_id <>", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdGreaterThan(String value) { + addCriterion("environment_id >", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdGreaterThanOrEqualTo(String value) { + addCriterion("environment_id >=", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdLessThan(String value) { + addCriterion("environment_id <", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdLessThanOrEqualTo(String value) { + addCriterion("environment_id <=", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdLike(String value) { + addCriterion("environment_id like", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdNotLike(String value) { + addCriterion("environment_id not like", value, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdIn(List values) { + addCriterion("environment_id in", values, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdNotIn(List values) { + addCriterion("environment_id not in", values, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdBetween(String value1, String value2) { + addCriterion("environment_id between", value1, value2, "environmentId"); + return (Criteria) this; + } + + public Criteria andEnvironmentIdNotBetween(String value1, String value2) { + addCriterion("environment_id not between", value1, value2, "environmentId"); + return (Criteria) this; + } + + public Criteria andPoolIdIsNull() { + addCriterion("pool_id is null"); + return (Criteria) this; + } + + public Criteria andPoolIdIsNotNull() { + addCriterion("pool_id is not null"); + return (Criteria) this; + } + + public Criteria andPoolIdEqualTo(String value) { + addCriterion("pool_id =", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdNotEqualTo(String value) { + addCriterion("pool_id <>", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdGreaterThan(String value) { + addCriterion("pool_id >", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdGreaterThanOrEqualTo(String value) { + addCriterion("pool_id >=", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdLessThan(String value) { + addCriterion("pool_id <", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdLessThanOrEqualTo(String value) { + addCriterion("pool_id <=", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdLike(String value) { + addCriterion("pool_id like", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdNotLike(String value) { + addCriterion("pool_id not like", value, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdIn(List values) { + addCriterion("pool_id in", values, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdNotIn(List values) { + addCriterion("pool_id not in", values, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdBetween(String value1, String value2) { + addCriterion("pool_id between", value1, value2, "poolId"); + return (Criteria) this; + } + + public Criteria andPoolIdNotBetween(String value1, String value2) { + addCriterion("pool_id not between", value1, value2, "poolId"); + return (Criteria) this; + } + + public Criteria andEnvGroupedIsNull() { + addCriterion("env_grouped is null"); + return (Criteria) this; + } + + public Criteria andEnvGroupedIsNotNull() { + addCriterion("env_grouped is not null"); + return (Criteria) this; + } + + public Criteria andEnvGroupedEqualTo(Boolean value) { + addCriterion("env_grouped =", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedNotEqualTo(Boolean value) { + addCriterion("env_grouped <>", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedGreaterThan(Boolean value) { + addCriterion("env_grouped >", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedGreaterThanOrEqualTo(Boolean value) { + addCriterion("env_grouped >=", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedLessThan(Boolean value) { + addCriterion("env_grouped <", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedLessThanOrEqualTo(Boolean value) { + addCriterion("env_grouped <=", value, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedIn(List values) { + addCriterion("env_grouped in", values, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedNotIn(List values) { + addCriterion("env_grouped not in", values, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedBetween(Boolean value1, Boolean value2) { + addCriterion("env_grouped between", value1, value2, "envGrouped"); + return (Criteria) this; + } + + public Criteria andEnvGroupedNotBetween(Boolean value1, Boolean value2) { + addCriterion("env_grouped not between", value1, value2, "envGrouped"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/ExecTaskMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/ExecTaskMapper.xml index 864a155aad..2f2637d41b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/system/mapper/ExecTaskMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/system/mapper/ExecTaskMapper.xml @@ -20,6 +20,9 @@ + + + @@ -82,7 +85,7 @@ id, num, task_name, `status`, case_count, `result`, task_type, resource_id, trigger_mode, project_id, organization_id, integrated, create_time, create_user, start_time, end_time, - deleted, parallel + deleted, parallel, environment_id, pool_id, env_grouped @@ -306,6 +331,15 @@ parallel = #{record.parallel,jdbcType=BIT}, + + environment_id = #{record.environmentId,jdbcType=VARCHAR}, + + + pool_id = #{record.poolId,jdbcType=VARCHAR}, + + + env_grouped = #{record.envGrouped,jdbcType=BIT}, + @@ -330,7 +364,10 @@ start_time = #{record.startTime,jdbcType=BIGINT}, end_time = #{record.endTime,jdbcType=BIGINT}, deleted = #{record.deleted,jdbcType=BIT}, - parallel = #{record.parallel,jdbcType=BIT} + parallel = #{record.parallel,jdbcType=BIT}, + environment_id = #{record.environmentId,jdbcType=VARCHAR}, + pool_id = #{record.poolId,jdbcType=VARCHAR}, + env_grouped = #{record.envGrouped,jdbcType=BIT} @@ -389,6 +426,15 @@ parallel = #{parallel,jdbcType=BIT}, + + environment_id = #{environmentId,jdbcType=VARCHAR}, + + + pool_id = #{poolId,jdbcType=VARCHAR}, + + + env_grouped = #{envGrouped,jdbcType=BIT}, + where id = #{id,jdbcType=VARCHAR} @@ -410,14 +456,17 @@ start_time = #{startTime,jdbcType=BIGINT}, end_time = #{endTime,jdbcType=BIGINT}, deleted = #{deleted,jdbcType=BIT}, - parallel = #{parallel,jdbcType=BIT} + parallel = #{parallel,jdbcType=BIT}, + environment_id = #{environmentId,jdbcType=VARCHAR}, + pool_id = #{poolId,jdbcType=VARCHAR}, + env_grouped = #{envGrouped,jdbcType=BIT} where id = #{id,jdbcType=VARCHAR} insert into exec_task (id, num, task_name, `status`, case_count, `result`, task_type, resource_id, trigger_mode, project_id, organization_id, integrated, create_time, create_user, start_time, - end_time, deleted, parallel) + end_time, deleted, parallel, environment_id, pool_id, env_grouped) values (#{item.id,jdbcType=VARCHAR}, #{item.num,jdbcType=BIGINT}, #{item.taskName,jdbcType=VARCHAR}, @@ -425,7 +474,8 @@ #{item.taskType,jdbcType=VARCHAR}, #{item.resourceId,jdbcType=VARCHAR}, #{item.triggerMode,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.organizationId,jdbcType=VARCHAR}, #{item.integrated,jdbcType=BIT}, #{item.createTime,jdbcType=BIGINT}, #{item.createUser,jdbcType=VARCHAR}, #{item.startTime,jdbcType=BIGINT}, - #{item.endTime,jdbcType=BIGINT}, #{item.deleted,jdbcType=BIT}, #{item.parallel,jdbcType=BIT} + #{item.endTime,jdbcType=BIGINT}, #{item.deleted,jdbcType=BIT}, #{item.parallel,jdbcType=BIT}, + #{item.environmentId,jdbcType=VARCHAR}, #{item.poolId,jdbcType=VARCHAR}, #{item.envGrouped,jdbcType=BIT} ) @@ -493,6 +543,15 @@ #{item.parallel,jdbcType=BIT} + + #{item.environmentId,jdbcType=VARCHAR} + + + #{item.poolId,jdbcType=VARCHAR} + + + #{item.envGrouped,jdbcType=BIT} + ) diff --git a/backend/framework/domain/src/main/resources/migration/3.5.0/ddl/V3.5.0_2__ga_ddl.sql b/backend/framework/domain/src/main/resources/migration/3.5.0/ddl/V3.5.0_2__ga_ddl.sql index 506cf933d4..bec77cb592 100644 --- a/backend/framework/domain/src/main/resources/migration/3.5.0/ddl/V3.5.0_2__ga_ddl.sql +++ b/backend/framework/domain/src/main/resources/migration/3.5.0/ddl/V3.5.0_2__ga_ddl.sql @@ -88,5 +88,10 @@ ALTER TABLE exec_task_item ADD rerun bit(1) DEFAULT 0 NULL COMMENT '是否是重 -- 任务添加串并行字段 ALTER TABLE exec_task ADD parallel bit(1) DEFAULT 1 NOT NULL COMMENT '是否是并行执行'; +-- 任务记录批量执行的环境等信息 +ALTER TABLE exec_task ADD environment_id varchar(50) NULL COMMENT '用例批量执行环境ID'; +ALTER TABLE exec_task ADD env_grouped bit(1) DEFAULT 0 NULL COMMENT '是否是环境组'; +ALTER TABLE exec_task ADD pool_id varchar(50) NULL COMMENT '资源池ID'; + -- set innodb lock wait timeout to default SET SESSION innodb_lock_wait_timeout = DEFAULT; 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 a2686e955c..c27d606631 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 @@ -1,6 +1,9 @@ package io.metersphere.api.service; +import io.metersphere.api.domain.ApiReportRelateTask; +import io.metersphere.api.domain.ApiReportRelateTaskExample; import io.metersphere.api.domain.ApiScenarioReport; +import io.metersphere.api.mapper.ApiReportRelateTaskMapper; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.sdk.constants.ApiBatchRunMode; import io.metersphere.sdk.constants.CommonConstants; @@ -18,7 +21,9 @@ import io.metersphere.system.domain.ExecTask; import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.mapper.ExecTaskMapper; import io.metersphere.system.mapper.ExtExecTaskItemMapper; +import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -40,6 +45,8 @@ public class ApiBatchRunBaseService { private ApiExecuteService apiExecuteService; @Resource private ExecTaskMapper execTaskMapper; + @Resource + private ApiReportRelateTaskMapper apiReportRelateTaskMapper; public static final int BATCH_TASK_ITEM_SIZE = 500; @@ -283,4 +290,15 @@ public class ApiBatchRunBaseService { SubListUtils.dealForSubList(execTaskItems, ApiBatchRunBaseService.BATCH_TASK_ITEM_SIZE, subExecTaskItems -> initExecutionQueueDetails(queue.getQueueId(), subExecTaskItems)); } + + public String getIntegratedReportId(ExecTask execTask) { + ApiReportRelateTaskExample example = new ApiReportRelateTaskExample(); + example.createCriteria().andTaskResourceIdEqualTo(execTask.getId()); + List apiReportRelateTasks = apiReportRelateTaskMapper.selectByExample(example); + String reportId = IDGenerator.nextStr(); + if (CollectionUtils.isNotEmpty(apiReportRelateTasks)) { + reportId = apiReportRelateTasks.getFirst().getReportId(); + } + return reportId; + } } 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 108f779895..dabb58642a 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 @@ -18,9 +18,10 @@ import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.util.*; import io.metersphere.system.domain.ExecTask; import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.mapper.ExtExecTaskItemMapper; import io.metersphere.system.service.BaseTaskHubService; -import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -66,6 +67,8 @@ public class ApiTestCaseBatchRunService { private ProjectMapper projectMapper; @Resource private BaseTaskHubService baseTaskHubService; + @Resource + private ExtExecTaskItemMapper extExecTaskItemMapper; public static final int TASK_BATCH_SIZE = 600; @@ -159,6 +162,10 @@ public class ApiTestCaseBatchRunService { execTask.setTriggerMode(TaskTriggerMode.BATCH.name()); execTask.setTaskType(ExecTaskType.API_CASE_BATCH.name()); execTask.setIntegrated(runModeConfig.getIntegratedReport()); + execTask.setPoolId(runModeConfig.getPoolId()); + execTask.setParallel(StringUtils.equals(runModeConfig.getRunMode(), ApiBatchRunMode.PARALLEL.name())); + execTask.setEnvGrouped(runModeConfig.getGrouped()); + execTask.setEnvironmentId(runModeConfig.getEnvironmentId()); baseTaskHubService.insertExecTask(execTask); return execTask; } @@ -267,6 +274,21 @@ public class ApiTestCaseBatchRunService { return runModeConfig; } + public ApiRunModeConfigDTO getRunModeConfig(ExecTask execTask) { + ApiRunModeConfigDTO runModeConfig = BeanUtils.copyBean(new ApiRunModeConfigDTO(), execTask); + runModeConfig.setRunMode(BooleanUtils.isTrue(execTask.getParallel()) ? ApiBatchRunMode.PARALLEL.name() : ApiBatchRunMode.SERIAL.name()); + runModeConfig.setPoolId(execTask.getPoolId()); + runModeConfig.setEnvironmentId(execTask.getEnvironmentId()); + runModeConfig.setGrouped(execTask.getEnvGrouped()); + runModeConfig.setIntegratedReport(execTask.getIntegrated()); + if (BooleanUtils.isTrue(execTask.getIntegrated())) { + runModeConfig.setCollectionReport(new CollectionReportDTO()); + runModeConfig.getCollectionReport().setReportId(apiBatchRunBaseService.getIntegratedReportId(execTask)); + runModeConfig.getCollectionReport().setReportName(execTask.getTaskName()); + } + return runModeConfig; + } + /** * 预生成用例的执行报告 * @@ -356,7 +378,7 @@ public class ApiTestCaseBatchRunService { String integratedReportId = null; if (runModeConfig.isIntegratedReport()) { - integratedReportId = runModeConfig.getCollectionReport().getReportId() + IDGenerator.nextStr(); + integratedReportId = runModeConfig.getCollectionReport().getReportId(); } if (apiTestCase == null) { @@ -368,6 +390,7 @@ public class ApiTestCaseBatchRunService { taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); + taskRequest.getTaskInfo().setRerun(queue.getRerun()); taskRequest.getTaskItem().setRequestCount(1L); taskRequest.getTaskItem().setId(taskItemId); @@ -451,4 +474,55 @@ public class ApiTestCaseBatchRunService { apiReportMapper.updateByPrimaryKeySelective(report); } } + + public void rerun(ExecTask execTask, String userId) { + if (BooleanUtils.isTrue(execTask.getParallel())) { + parallelRerunExecute(execTask, userId); + } else { + serialRerunExecute(execTask, userId); + } + } + + private void serialRerunExecute(ExecTask execTask, String userId) { + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(execTask); + + List execTaskItems = extExecTaskItemMapper.selectIdAndResourceIdByTaskId(execTask.getId()); + + // 初始化执行队列 + ExecutionQueue queue = apiBatchRunBaseService.getExecutionQueue(runModeConfig, ApiExecuteResourceType.API_CASE.name(), execTask.getId(), userId); + queue.setQueueId(execTask.getId()); + queue.setRerun(true); + apiExecutionQueueService.insertQueue(queue); + + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + + // 执行第一个任务 + ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + executeNextTask(queue, nextDetail); + } + + /** + * 并行重跑 + * + */ + public void parallelRerunExecute(ExecTask execTask, String userId) { + String projectId = execTask.getProjectId(); + List execTaskItems = extExecTaskItemMapper.selectIdAndResourceIdByTaskId(execTask.getId()); + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(execTask); + + // 记录用例和任务的映射 + Map resourceExecTaskItemMap = new TreeMap<>(); + execTaskItems.forEach(item -> resourceExecTaskItemMap.put(item.getResourceId(), item.getId())); + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(projectId, runModeConfig); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); + taskRequest.getTaskInfo().setSetId(execTask.getId()); + taskRequest.getTaskInfo().setUserId(userId); + taskRequest.getTaskInfo().setRerun(true); + + // 记录任务项,用于统计整体执行情况 + apiExecutionSetService.initSet(execTask.getId(), new ArrayList<>(resourceExecTaskItemMap.values())); + apiBatchRunBaseService.parallelBatchExecute(taskRequest, runModeConfig, resourceExecTaskItemMap); + } } 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 e0d954a633..ea54fa8d63 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 @@ -6,10 +6,12 @@ import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest; import io.metersphere.api.dto.scenario.ApiScenarioDetail; import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.mapper.ApiScenarioReportMapper; +import io.metersphere.api.mapper.ApiScenarioReportStepMapper; 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.definition.ApiTestCaseBatchRunService; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionSetService; import io.metersphere.project.domain.Project; @@ -21,6 +23,7 @@ import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.util.*; import io.metersphere.system.domain.ExecTask; import io.metersphere.system.domain.ExecTaskItem; +import io.metersphere.system.mapper.ExtExecTaskItemMapper; import io.metersphere.system.service.BaseTaskHubService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; @@ -67,6 +70,12 @@ public class ApiScenarioBatchRunService { private ApiCommonService apiCommonService; @Resource private BaseTaskHubService baseTaskHubService; + @Resource + private ApiTestCaseBatchRunService apiTestCaseBatchRunService; + @Resource + private ExtExecTaskItemMapper extExecTaskItemMapper; + @Resource + private ApiScenarioReportStepMapper apiScenarioReportStepMapper; public static final int TASK_BATCH_SIZE = 600; @@ -225,6 +234,10 @@ public class ApiScenarioBatchRunService { } else { execTask.setTaskName(Translator.get("api_scenario_batch_task_name")); } + execTask.setPoolId(runModeConfig.getPoolId()); + execTask.setParallel(StringUtils.equals(runModeConfig.getRunMode(), ApiBatchRunMode.PARALLEL.name())); + execTask.setEnvGrouped(runModeConfig.getGrouped()); + execTask.setEnvironmentId(runModeConfig.getEnvironmentId()); execTask.setOrganizationId(project.getOrganizationId()); execTask.setTriggerMode(TaskTriggerMode.BATCH.name()); execTask.setTaskType(ExecTaskType.API_SCENARIO_BATCH.name()); @@ -336,6 +349,7 @@ public class ApiScenarioBatchRunService { taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); taskRequest.getTaskInfo().setUserId(queue.getUserId()); taskRequest.getTaskInfo().setTaskId(queue.getTaskId()); + taskRequest.getTaskInfo().setRerun(queue.getRerun()); apiExecuteService.execute(taskRequest); } @@ -432,4 +446,74 @@ public class ApiScenarioBatchRunService { LogUtils.error("失败停止,补充报告步骤失败:", e); } } + + public void rerun(ExecTask execTask, String userId) { + if (BooleanUtils.isTrue(execTask.getParallel())) { + parallelRerunExecute(execTask, userId); + } else { + serialRerunExecute(execTask, userId); + } + } + + private void serialRerunExecute(ExecTask execTask, String userId) { + ApiRunModeConfigDTO runModeConfig = apiTestCaseBatchRunService.getRunModeConfig(execTask); + + List execTaskItems = extExecTaskItemMapper.selectIdAndResourceIdByTaskId(execTask.getId()); + + // 删除重跑的步骤 + deleteRerunIntegratedStepResult(execTask, execTaskItems, runModeConfig); + + // 初始化执行队列 + ExecutionQueue queue = apiBatchRunBaseService.getExecutionQueue(runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), execTask.getId(), userId); + queue.setQueueId(execTask.getId()); + queue.setRerun(true); + apiExecutionQueueService.insertQueue(queue); + + // 初始化队列项 + apiBatchRunBaseService.initExecutionQueueDetails(queue.getQueueId(), execTaskItems); + + // 执行第一个任务 + ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + executeNextTask(queue, nextDetail); + } + + /** + * 并行重跑 + * + */ + public void parallelRerunExecute(ExecTask execTask, String userId) { + String projectId = execTask.getProjectId(); + List execTaskItems = extExecTaskItemMapper.selectIdAndResourceIdByTaskId(execTask.getId()); + ApiRunModeConfigDTO runModeConfig = apiTestCaseBatchRunService.getRunModeConfig(execTask); + + // 删除重跑的步骤 + deleteRerunIntegratedStepResult(execTask, execTaskItems, runModeConfig); + + // 记录用例和任务的映射 + Map resourceExecTaskItemMap = new TreeMap<>(); + execTaskItems.forEach(item -> resourceExecTaskItemMap.put(item.getResourceId(), item.getId())); + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(projectId, runModeConfig); + taskRequest.getTaskInfo().setTaskId(execTask.getId()); + taskRequest.getTaskInfo().setSetId(execTask.getId()); + taskRequest.getTaskInfo().setUserId(userId); + taskRequest.getTaskInfo().setRerun(true); + + // 记录任务项,用于统计整体执行情况 + apiExecutionSetService.initSet(execTask.getId(), new ArrayList<>(resourceExecTaskItemMap.values())); + apiBatchRunBaseService.parallelBatchExecute(taskRequest, runModeConfig, resourceExecTaskItemMap); + } + + private void deleteRerunIntegratedStepResult(ExecTask execTask, List execTaskItems, ApiRunModeConfigDTO runModeConfig) { + if (BooleanUtils.isTrue(execTask.getIntegrated())) { + SubListUtils.dealForSubList(execTaskItems, TASK_BATCH_SIZE, subItems -> { + // 删除子步骤,重新执行 + ApiScenarioReportStepExample stepExample = new ApiScenarioReportStepExample(); + stepExample.createCriteria() + .andReportIdEqualTo(runModeConfig.getCollectionReport().getReportId()) + .andParentIdIn(subItems.stream().map(ExecTaskItem::getResourceId).toList()); + apiScenarioReportStepMapper.deleteByExample(stepExample); + }); + } + } } 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 2a86feaac0..be40fdf9e4 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,5 +1,6 @@ package io.metersphere.system.mapper; +import io.metersphere.system.domain.ExecTask; import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.dto.table.TableBatchProcessDTO; import io.metersphere.system.dto.BatchExecTaskReportDTO; @@ -70,4 +71,6 @@ public interface ExtExecTaskItemMapper { void deleteRerunTaskItemReportRelation(@Param("taskId") String taskId); Set selectRerunCollectionIds(@Param("taskId") String taskId); + + List selectIdAndResourceIdByTaskId(@Param("taskId") String taskId); } 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 05d8d2ee59..87d94fe025 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 @@ -327,6 +327,11 @@ from exec_task_item where task_id = #{taskId} and rerun = true and deleted = false; + UPDATE exec_task_item diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiCaseBatchRerunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiCaseBatchRerunService.java new file mode 100644 index 0000000000..3a6ebb95e1 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiCaseBatchRerunService.java @@ -0,0 +1,31 @@ +package io.metersphere.plan.service.rerun; + +import io.metersphere.api.service.definition.ApiTestCaseBatchRunService; +import io.metersphere.sdk.constants.ExecTaskType; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.invoker.TaskRerunServiceInvoker; +import io.metersphere.system.service.TaskRerunService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @Author: jianxing + * @CreateTime: 2024-02-06 20:47 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiCaseBatchRerunService implements TaskRerunService { + @Resource + private ApiTestCaseBatchRunService apiTestCaseBatchRunService; + + + public ApiCaseBatchRerunService() { + TaskRerunServiceInvoker.register(ExecTaskType.API_CASE_BATCH, this); + } + + @Override + public void rerun(ExecTask execTask, String userId) { + apiTestCaseBatchRunService.rerun(execTask, userId); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiScenarioBatchRerunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiScenarioBatchRerunService.java new file mode 100644 index 0000000000..387901dcb6 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/rerun/ApiScenarioBatchRerunService.java @@ -0,0 +1,31 @@ +package io.metersphere.plan.service.rerun; + +import io.metersphere.api.service.scenario.ApiScenarioBatchRunService; +import io.metersphere.sdk.constants.ExecTaskType; +import io.metersphere.system.domain.ExecTask; +import io.metersphere.system.invoker.TaskRerunServiceInvoker; +import io.metersphere.system.service.TaskRerunService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * @Author: jianxing + * @CreateTime: 2024-02-06 20:47 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiScenarioBatchRerunService implements TaskRerunService { + @Resource + private ApiScenarioBatchRunService apiScenarioBatchRunService; + + + public ApiScenarioBatchRerunService() { + TaskRerunServiceInvoker.register(ExecTaskType.API_SCENARIO_BATCH, this); + } + + @Override + public void rerun(ExecTask execTask, String userId) { + apiScenarioBatchRunService.rerun(execTask, userId); + } +}