From 03737a5cc68149e985edcf5a5870ff23dba5ad1e Mon Sep 17 00:00:00 2001 From: song-cc-rock Date: Fri, 14 Jun 2024 18:15:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92):=20?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E8=AE=A1=E5=88=92=E6=89=A7=E8=A1=8C=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=8A=A5=E5=91=8A=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan/domain/TestPlanReport.java | 4 - .../plan/domain/TestPlanReportExample.java | 60 ------- .../plan/mapper/TestPlanReportMapper.xml | 63 +++----- .../migration/3.0.1/ddl/V3.0.1_2__ga_ddl.sql | 1 + .../plan/dto/TestPlanReportPostParam.java | 6 - .../TestPlanReportDetailResponse.java | 6 +- .../plan/mapper/ExtTestPlanReportMapper.java | 2 + .../plan/mapper/ExtTestPlanReportMapper.xml | 9 ++ .../plan/service/TestPlanExecuteService.java | 89 ++++++++--- .../plan/service/TestPlanReportService.java | 151 ++++++++++++------ .../io/metersphere/plan/utils/CountUtils.java | 46 ++++++ .../plan/controller/TestPlanExecuteTests.java | 1 - 12 files changed, 253 insertions(+), 185 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/utils/CountUtils.java diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java index 14d46775ca..d9ceb27794 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java @@ -35,9 +35,6 @@ public class TestPlanReport implements Serializable { @Schema(description = "创建时间") private Long createTime; - @Schema(description = "执行时间;计划真正执行的时间") - private Long executeTime; - @Schema(description = "开始时间;计划开始执行的时间") private Long startTime; @@ -93,7 +90,6 @@ public class TestPlanReport implements Serializable { name("name", "name", "VARCHAR", true), createUser("create_user", "createUser", "VARCHAR", false), createTime("create_time", "createTime", "BIGINT", false), - executeTime("execute_time", "executeTime", "BIGINT", false), startTime("start_time", "startTime", "BIGINT", false), endTime("end_time", "endTime", "BIGINT", false), execStatus("exec_status", "execStatus", "VARCHAR", false), diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportExample.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportExample.java index 5962241dc2..4c403aaa9f 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportExample.java @@ -445,66 +445,6 @@ public class TestPlanReportExample { return (Criteria) this; } - public Criteria andExecuteTimeIsNull() { - addCriterion("execute_time is null"); - return (Criteria) this; - } - - public Criteria andExecuteTimeIsNotNull() { - addCriterion("execute_time is not null"); - return (Criteria) this; - } - - public Criteria andExecuteTimeEqualTo(Long value) { - addCriterion("execute_time =", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeNotEqualTo(Long value) { - addCriterion("execute_time <>", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeGreaterThan(Long value) { - addCriterion("execute_time >", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeGreaterThanOrEqualTo(Long value) { - addCriterion("execute_time >=", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeLessThan(Long value) { - addCriterion("execute_time <", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeLessThanOrEqualTo(Long value) { - addCriterion("execute_time <=", value, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeIn(List values) { - addCriterion("execute_time in", values, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeNotIn(List values) { - addCriterion("execute_time not in", values, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeBetween(Long value1, Long value2) { - addCriterion("execute_time between", value1, value2, "executeTime"); - return (Criteria) this; - } - - public Criteria andExecuteTimeNotBetween(Long value1, Long value2) { - addCriterion("execute_time not between", value1, value2, "executeTime"); - return (Criteria) this; - } - public Criteria andStartTimeIsNull() { addCriterion("start_time is null"); return (Criteria) this; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/mapper/TestPlanReportMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/plan/mapper/TestPlanReportMapper.xml index 64be013e96..18efbde3cd 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/mapper/TestPlanReportMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/mapper/TestPlanReportMapper.xml @@ -7,7 +7,6 @@ - @@ -80,9 +79,9 @@ - id, test_plan_id, `name`, create_user, create_time, execute_time, start_time, end_time, - exec_status, result_status, pass_rate, trigger_mode, pass_threshold, project_id, - integrated, deleted, execute_rate, parent_id + id, test_plan_id, `name`, create_user, create_time, start_time, end_time, exec_status, + result_status, pass_rate, trigger_mode, pass_threshold, project_id, integrated, deleted, + execute_rate, parent_id + + update test_plan_report + set start_time = #{startTime} + where id in + + #{id} + + + 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 5d16347170..311f51e8c2 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,18 +1,23 @@ package io.metersphere.plan.service; import com.esotericsoftware.minlog.Log; +import io.metersphere.plan.dto.TestPlanReportPostParam; 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.ExtTestPlanReportMapper; import io.metersphere.plan.mapper.TestPlanCollectionMapper; import io.metersphere.plan.mapper.TestPlanConfigMapper; import io.metersphere.plan.mapper.TestPlanMapper; import io.metersphere.sdk.constants.ApiBatchRunMode; import io.metersphere.sdk.constants.CaseType; +import io.metersphere.sdk.constants.ExecStatus; import io.metersphere.sdk.constants.TestPlanConstants; 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.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections4.CollectionUtils; @@ -24,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; @Service @@ -33,10 +39,14 @@ public class TestPlanExecuteService { @Resource private TestPlanMapper testPlanMapper; @Resource + private ExtTestPlanReportMapper extTestPlanReportMapper; + @Resource private TestPlanConfigMapper testPlanConfigMapper; @Resource private TestPlanService testPlanService; @Resource + private TestPlanReportService testPlanReportService; + @Resource private TestPlanCollectionMapper testPlanCollectionMapper; @Resource private TestPlanApiCasePlanRunService testPlanApiCasePlanRunService; @@ -121,12 +131,20 @@ public class TestPlanExecuteService { if (testPlan == null || StringUtils.equalsIgnoreCase(testPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) { throw new MSException("test_plan.error"); } + + TestPlanReportGenRequest genReportRequest = new TestPlanReportGenRequest(); + genReportRequest.setTriggerMode(executionQueue.getExecutionSource()); + genReportRequest.setTestPlanId(executionQueue.getSourceID()); + genReportRequest.setProjectId(testPlan.getProjectId()); if (StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { List children = testPlanService.selectNotArchivedChildren(testPlan.getId()); + // 预生成计划组报告 + Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(),genReportRequest, executionQueue.getCreateUser()); + long pos = 0; List childrenQueue = new ArrayList<>(); - String queueId = IDGenerator.nextStr(); String queueType = QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE; + String queueId = executionQueue.getPrepareReportId(); for (TestPlan child : children) { childrenQueue.add( new TestPlanExecutionQueue( @@ -140,7 +158,7 @@ public class TestPlanExecuteService { child.getId(), executionQueue.getRunMode(), executionQueue.getExecutionSource(), - IDGenerator.nextStr() + reportMap.get(child.getId()) ) ); } @@ -150,22 +168,26 @@ public class TestPlanExecuteService { } else { this.setRedisForList(genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList()); - // todo Song-cc 这里是否要生成测试计划组的集合报告,并且记录测试计划里用例的执行信息? + // 更新报告的执行时间 + extTestPlanReportMapper.batchUpdateExecuteTime(System.currentTimeMillis(),reportMap.values().stream().toList()); if (StringUtils.equalsIgnoreCase(executionQueue.getRunMode(), ApiBatchRunMode.SERIAL.name())) { //串行 TestPlanExecutionQueue nextQueue = this.getNextQueue(queueId, queueType); - executeTestPlanOrGroup(nextQueue); + executeTestPlan(nextQueue); } else { //并行 childrenQueue.forEach(childQueue -> { - executeTestPlanOrGroup(childQueue); + executeTestPlan(childQueue); }); } } return executionQueue.getPrepareReportId(); } else { + Map reportMap = testPlanReportService.genReportByExecution(executionQueue.getPrepareReportId(),genReportRequest, executionQueue.getCreateUser()); + executionQueue.setPrepareReportId(reportMap.get(executionQueue.getSourceID())); + extTestPlanReportMapper.batchUpdateExecuteTime(System.currentTimeMillis(),reportMap.values().stream().toList()); return this.executeTestPlan(executionQueue); } } @@ -185,7 +207,7 @@ public class TestPlanExecuteService { TestPlanConfig testPlanConfig = testPlanConfigMapper.selectByPrimaryKey(testPlan.getId()); String runMode = StringUtils.isBlank(testPlanConfig.getCaseRunMode()) ? ApiBatchRunMode.SERIAL.name() : testPlanConfig.getCaseRunMode(); - String queueId = IDGenerator.nextStr(); + String queueId = executionQueue.getPrepareReportId(); String queueType = QUEUE_PREFIX_TEST_PLAN_CASE_TYPE; List childrenQueue = new ArrayList<>(); for (TestPlanCollection collection : testPlanCollectionList) { @@ -201,7 +223,7 @@ public class TestPlanExecuteService { collection.getId(), runMode, executionQueue.getExecutionSource(), - IDGenerator.nextStr()) + executionQueue.getPrepareReportId()) ); } @@ -210,7 +232,6 @@ public class TestPlanExecuteService { this.testPlanExecuteQueueFinish(executionQueue.getQueueId(), executionQueue.getQueueType()); } else { this.setRedisForList(genQueueKey(queueId, queueType), childrenQueue.stream().map(JSON::toJSONString).toList()); - // todo Song-cc 这里是否要生成测试计划报告,并且记录测试计划里用例的执行信息? //开始根据测试计划集合执行测试用例 if (StringUtils.equalsIgnoreCase(runMode, ApiBatchRunMode.SERIAL.name())) { @@ -253,7 +274,7 @@ public class TestPlanExecuteService { collection.getId(), collection.getExecuteMethod(), executionQueue.getExecutionSource(), - IDGenerator.nextStr()) {{ + executionQueue.getPrepareReportId()) {{ this.setTestPlanCollectionJson(JSON.toJSONString(collection)); }} ); @@ -404,12 +425,43 @@ public class TestPlanExecuteService { } } + private void summaryTestPlanReport(String reportId,boolean isGroupReport){ + try { + if(isGroupReport){ + testPlanReportService.summaryGroupReport(reportId); + }else { + testPlanReportService.summaryPlanReport(reportId); + } + + TestPlanReportPostParam postParam = new TestPlanReportPostParam(); + postParam.setReportId(reportId); + // 执行生成报告, 执行状态为已完成, 执行及结束时间为当前时间 + postParam.setEndTime(System.currentTimeMillis()); + postParam.setExecStatus(ExecStatus.COMPLETED.name()); + testPlanReportService.postHandleReport(postParam); + }catch (Exception e){ + LogUtils.error("Cannot find test plan report for " + reportId, e); + } + } + + private void queueExecuteFinish(TestPlanExecutionQueue queue) { if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE)) { - // todo Song-cc 测试计划组集合报告生成 + if(StringUtils.equalsIgnoreCase(queue.getQueueType(),QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE)){ + // 计划组报告汇总并统计 + this.summaryTestPlanReport(queue.getQueueId(),true); + }else if(StringUtils.equalsIgnoreCase(queue.getQueueType(),QUEUE_PREFIX_TEST_PLAN_CASE_TYPE)){ + /* + 此时处于批量勾选执行中的游离态测试计划执行。所以队列顺序为:QUEUE_PREFIX_TEST_PLAN_BATCH_EXECUTE -> QUEUE_PREFIX_TEST_PLAN_CASE_TYPE。 + 此时queue节点为testPlanCollection的节点。 而测试计划节点(串行状态下)在执行之前就被弹出了。 + 所以获取报告ID的方式为读取queueId (caseType队列和collection队列的queueId都是报告ID) + */ + this.summaryTestPlanReport(queue.getQueueId(),false); + } this.testPlanGroupQueueFinish(queue.getParentQueueId(), queue.getParentQueueType()); } else if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_GROUP_EXECUTE)) { - // todo Song-cc 测试计划报告计算 + // 计划报告汇总并统计 + this.summaryTestPlanReport(queue.getQueueId(),false); this.testPlanExecuteQueueFinish(queue.getParentQueueId(), queue.getParentQueueType()); } else if (StringUtils.equalsIgnoreCase(queue.getParentQueueType(), QUEUE_PREFIX_TEST_PLAN_CASE_TYPE)) { this.caseTypeExecuteQueueFinish(queue.getParentQueueId(), queue.getParentQueueType()); @@ -428,17 +480,12 @@ public class TestPlanExecuteService { ListOperations listOps = redisTemplate.opsForList(); String queueDetail = listOps.leftPop(queueKey); if (StringUtils.isBlank(queueDetail)) { - // 重试2次获取 - for (int i = 0; i < 3; i++) { - queueDetail = redisTemplate.opsForList().leftPop(queueKey); - if (StringUtils.isNotBlank(queueDetail)) { - break; - } - try { - Thread.sleep(1000); - } catch (Exception ignore) { - } + // 重试1次获取 + try { + Thread.sleep(1000); + } catch (Exception ignore) { } + queueDetail = redisTemplate.opsForList().leftPop(queueKey); } if (StringUtils.isNotBlank(queueDetail)) { 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 db5e5e8814..070423aac6 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 @@ -10,6 +10,7 @@ import io.metersphere.plan.dto.response.TestPlanReportDetailResponse; import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType; import io.metersphere.plan.mapper.*; +import io.metersphere.plan.utils.CountUtils; import io.metersphere.plan.utils.ModuleTreeUtils; import io.metersphere.plan.utils.RateCalculateUtils; import io.metersphere.plugin.platform.dto.SelectOption; @@ -43,7 +44,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @Service @@ -215,8 +215,8 @@ public class TestPlanReportService { * @param request 请求参数 * @param currentUser 当前用户 */ - public void genReportByManual(TestPlanReportGenRequest request, String currentUser) { - genReport(request, true, currentUser, "/test-plan/report/gen"); + public Map genReportByManual(TestPlanReportGenRequest request, String currentUser) { + return genReport(IDGenerator.nextStr(),request, true, currentUser, "/test-plan/report/gen"); } /** @@ -225,55 +225,63 @@ public class TestPlanReportService { * @param request 请求参数 * @param currentUser 当前用户 */ - public void genReportByExecution(TestPlanReportGenRequest request, String currentUser) { - genReport(request, false, currentUser, "/test-plan/report/gen"); + public Map genReportByExecution(String prepareReportId,TestPlanReportGenRequest request, String currentUser) { + return genReport(prepareReportId,request, false, currentUser, "/test-plan/report/gen"); } - public void genReport(TestPlanReportGenRequest request, boolean manual, String currentUser, String logPath) { - // 所有计划 - List plans = getPlans(request.getTestPlanId()); - // 模块参数 - TestPlanReportModuleParam moduleParam = getModuleParam(request.getProjectId()); + public Map genReport(String prepareReportId,TestPlanReportGenRequest request, boolean manual, String currentUser, String logPath) { + Map preReportMap = new HashMap<>(); + try { + // 所有计划 + List plans = getPlans(request.getTestPlanId()); + // 模块参数 + TestPlanReportModuleParam moduleParam = getModuleParam(request.getProjectId()); - /* - * 1. 准备报告生成参数 - * 2. 预生成报告 - * 3. 汇总报告数据 {执行时跳过} - * 3. 报告后置处理 (计算通过率, 执行率, 执行状态...) {执行时跳过} - */ - List childPlanIds = plans.stream().filter(plan -> StringUtils.equals(plan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN)).map(TestPlan::getId).toList(); - AtomicReference groupReportId = new AtomicReference<>(); - plans.forEach(plan -> { - request.setTestPlanId(plan.getId()); - TestPlanReportGenPreParam genPreParam = buildReportGenParam(request, plan, groupReportId.get()); - genPreParam.setUseManual(manual); - TestPlanReport preReport = preGenReport(genPreParam, currentUser, logPath, moduleParam, childPlanIds); - if (genPreParam.getIntegrated()) { - // 如果是计划组的报告, 初始化组的报告ID - groupReportId.set(preReport.getId()); - } + /* + * 1. 准备报告生成参数 + * 2. 预生成报告 + * 3. 汇总报告数据 {执行时跳过} + * 3. 报告后置处理 (计算通过率, 执行率, 执行状态...) {执行时跳过} + */ + List childPlanIds = plans.stream().filter(plan -> StringUtils.equals(plan.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN)).map(TestPlan::getId).toList(); - if (manual) { - // 汇总 - summaryReport(preReport.getId()); - // 手动生成的报告, 汇总结束后直接进行后置处理 - TestPlanReportPostParam postParam = new TestPlanReportPostParam(); - BeanUtils.copyBean(postParam, request); - postParam.setReportId(preReport.getId()); - // 手动生成报告, 执行状态为已完成, 执行及结束时间为当前时间 - postParam.setExecuteTime(System.currentTimeMillis()); - postParam.setEndTime(System.currentTimeMillis()); - postParam.setExecStatus(ExecStatus.COMPLETED.name()); - postHandleReport(postParam); - } - }); + boolean isGroupReports = plans.size() > 1; + plans.forEach(plan -> { + request.setTestPlanId(plan.getId()); + TestPlanReportGenPreParam genPreParam = buildReportGenParam(request, plan, prepareReportId); + genPreParam.setUseManual(manual); + //如果是测试计划的独立报告,使用参数中的预生成的报告id。否则只有测试计划组报告使用该id + String prepareItemReportId = isGroupReports?IDGenerator.nextStr() : prepareReportId; + TestPlanReport preReport = preGenReport(prepareItemReportId,genPreParam, currentUser, logPath, moduleParam, childPlanIds); + if (manual) { + // 汇总 + if (genPreParam.getIntegrated()) { + summaryGroupReport(preReport.getId()); + } else { + summaryPlanReport(preReport.getId()); + } + // 手动生成的报告, 汇总结束后直接进行后置处理 + TestPlanReportPostParam postParam = new TestPlanReportPostParam(); + postParam.setReportId(preReport.getId()); + // 手动生成报告, 执行状态为已完成, 执行及结束时间为当前时间 + postParam.setExecuteTime(System.currentTimeMillis()); + postParam.setEndTime(System.currentTimeMillis()); + postParam.setExecStatus(ExecStatus.COMPLETED.name()); + postHandleReport(postParam); + } + preReportMap.put(plan.getId(), preReport.getId()); + }); + } catch (Exception e) { + LogUtils.error("生成报告异常: " + e.getMessage()); + } + return preReportMap; } /** * 预生成报告内容(汇总前调用) * @return 报告 */ - public TestPlanReport preGenReport(TestPlanReportGenPreParam genParam, String currentUser, String logPath, TestPlanReportModuleParam moduleParam, List childPlanIds) { + public TestPlanReport preGenReport(String prepareId,TestPlanReportGenPreParam genParam, String currentUser, String logPath, TestPlanReportModuleParam moduleParam, List childPlanIds) { // 计划配置 TestPlanConfig config = testPlanConfigMapper.selectByPrimaryKey(genParam.getTestPlanId()); @@ -284,7 +292,7 @@ public class TestPlanReportService { */ TestPlanReport report = new TestPlanReport(); BeanUtils.copyBean(report, genParam); - report.setId(IDGenerator.nextStr()); + report.setId(genParam.getIntegrated() ? genParam.getGroupReportId() : prepareId); report.setName(genParam.getTestPlanName() + "-" + DateUtils.getTimeStr(System.currentTimeMillis())); report.setCreateUser(currentUser); report.setCreateTime(System.currentTimeMillis()); @@ -398,7 +406,7 @@ public class TestPlanReportService { if (genParam.getIntegrated()) { reportBugs = CollectionUtils.isEmpty(childPlanIds) ? new ArrayList<>() : extTestPlanReportBugMapper.getGroupBugs(childPlanIds); } else { - reportBugs = extTestPlanReportBugMapper.getPlanBugs(genParam.getTestPlanId());; + reportBugs = extTestPlanReportBugMapper.getPlanBugs(genParam.getTestPlanId()); } if (CollectionUtils.isNotEmpty(reportBugs)) { // MS处理人会与第三方的值冲突, 分开查询 @@ -531,11 +539,11 @@ public class TestPlanReportService { } /** - * 汇总规划生成的报告 + * 汇总生成的计划报告 * * @param reportId 报告ID */ - public void summaryReport(String reportId) { + public void summaryPlanReport(String reportId) { TestPlanReportSummaryExample example = new TestPlanReportSummaryExample(); example.createCriteria().andTestPlanReportIdEqualTo(reportId); List testPlanReportSummaries = testPlanReportSummaryMapper.selectByExample(example); @@ -569,6 +577,53 @@ public class TestPlanReportService { testPlanReportSummaryMapper.updateByPrimaryKeySelective(reportSummary); } + /** + * 汇总生成的计划组报告 + * @param reportId + */ + public void summaryGroupReport(String reportId) { + TestPlanReportSummaryExample summaryExample = new TestPlanReportSummaryExample(); + summaryExample.createCriteria().andTestPlanReportIdEqualTo(reportId); + List testPlanReportSummaries = testPlanReportSummaryMapper.selectByExample(summaryExample); + if (CollectionUtils.isEmpty(testPlanReportSummaries)) { + // 报告详情不存在 + return; + } + TestPlanReportSummary groupSummary = testPlanReportSummaries.get(0); + + TestPlanReportExample example = new TestPlanReportExample(); + example.createCriteria().andParentIdEqualTo(reportId); + List testPlanReports = testPlanReportMapper.selectByExample(example); + if(CollectionUtils.isEmpty(testPlanReports)){ + return; + } + List ids = testPlanReports.stream().map(TestPlanReport::getId).toList(); + summaryExample.clear(); + summaryExample.createCriteria().andIdIn(ids); + List summaryList = testPlanReportSummaryMapper.selectByExampleWithBLOBs(summaryExample); + List functionalCaseCountList = new ArrayList<>(); + List apiCaseCountList = new ArrayList<>(); + List scenarioCountList = new ArrayList<>(); + List executeCountList = new ArrayList<>(); + summaryList.forEach(summary -> { + CaseCount functionalCaseCount = summary.getFunctionalExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getFunctionalExecuteResult()), CaseCount.class); + CaseCount apiCaseCount = summary.getApiExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getApiExecuteResult()), CaseCount.class); + CaseCount scenarioCount = summary.getScenarioExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getScenarioExecuteResult()), CaseCount.class); + CaseCount executeCount = summary.getExecuteResult() == null ? CaseCount.builder().build() : JSON.parseObject(new String(summary.getExecuteResult()), CaseCount.class); + functionalCaseCountList.add(functionalCaseCount); + apiCaseCountList.add(apiCaseCount); + scenarioCountList.add(scenarioCount); + executeCountList.add(executeCount); + }); + + // 入库组汇总数据 => 报告详情表 + groupSummary.setFunctionalExecuteResult(JSON.toJSONBytes(CountUtils.summarizeProperties(functionalCaseCountList))); + groupSummary.setApiExecuteResult(JSON.toJSONBytes(CountUtils.summarizeProperties(apiCaseCountList))); + groupSummary.setScenarioExecuteResult(JSON.toJSONBytes(CountUtils.summarizeProperties(scenarioCountList))); + groupSummary.setExecuteResult(JSON.toJSONBytes(CountUtils.summarizeProperties(executeCountList))); + testPlanReportSummaryMapper.updateByPrimaryKeySelective(groupSummary); + } + /** * 通过请求参数获取批量操作的ID集合 * @@ -778,8 +833,8 @@ public class TestPlanReportService { List testPlans = testPlanMapper.selectByExample(example); plans.addAll(testPlans); } - // 保证第一条为计划组 - plans.addFirst(testPlan); + // 保证最后一条为计划组 + plans.addLast(testPlan); return plans; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/utils/CountUtils.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/utils/CountUtils.java new file mode 100644 index 0000000000..27c15d022b --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/utils/CountUtils.java @@ -0,0 +1,46 @@ +package io.metersphere.plan.utils; + +import io.metersphere.plan.dto.CaseCount; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.LogUtils; +import lombok.experimental.UtilityClass; + +import java.lang.reflect.Field; +import java.util.List; + +/** + * 汇总工具类 + */ +@UtilityClass +public class CountUtils { + + /** + * 集合属性汇总 + * @param list 集合 + * @return 汇总实体 + */ + public static CaseCount summarizeProperties(List list) { + if (list.isEmpty()) { + return new CaseCount(); + } + + Class clazz = (Class) list.get(0).getClass(); + CaseCount summary; + try { + summary = clazz.newInstance(); + Field[] fields = clazz.getDeclaredFields(); + for (CaseCount caseCount : list) { + for (Field field : fields) { + field.setAccessible(true); + Integer value = (Integer) field.get(summary) + (Integer) field.get(caseCount); + field.set(summary, value); + } + } + } catch (Exception e) { + LogUtils.error("汇总数据异常: " + e.getMessage()); + throw new MSException(e); + } + + return summary; + } +} diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanExecuteTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanExecuteTests.java index e6c8ee994c..3b9a8e3b39 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanExecuteTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanExecuteTests.java @@ -267,7 +267,6 @@ public class TestPlanExecuteTests extends BaseTest { //本条测试用例中,最多传入的是5个批量执行。(4个测试计划组和1个测试计划, 有一个测试计划组下面没有测试计划), // 串行的话模拟其中8个测试集的回调。 既while中最多循环8次进行回调,每次回调只传入1个测试集,防止并行/串行的干扰 int foreachIndex = 8; - long timeStem = System.currentTimeMillis(); while (foreachIndex > 0 && !allQueueIds.isEmpty()) { String collectionFinishQueueIds = collectionQueueIdList.getFirst();