From 00ee3a53a32c062321d9e879991bc9c3af581619 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Wed, 3 Apr 2024 15:52:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=89=B9=E9=87=8F=E6=89=A7=E8=A1=8C=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=81=9C=E6=AD=A2=EF=BC=8C=E6=8A=A5=E5=91=8A=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/listener/MessageListener.java | 37 ++++- .../parser/jmeter/MsScenarioConverter.java | 6 +- .../scenario/ApiScenarioBatchRunService.java | 155 +++++++++++++----- .../scenario/ApiScenarioReportService.java | 11 +- .../service/scenario/ApiScenarioService.java | 27 +-- 5 files changed, 175 insertions(+), 61 deletions(-) diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java b/backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java index 0a1c1db3b8..3ca388efae 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java @@ -2,9 +2,11 @@ package io.metersphere.api.listener; import io.metersphere.api.event.ApiEventSource; import io.metersphere.api.service.ApiReportSendNoticeService; +import io.metersphere.api.service.definition.ApiReportService; import io.metersphere.api.service.definition.ApiTestCaseBatchRunService; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.scenario.ApiScenarioBatchRunService; +import io.metersphere.api.service.scenario.ApiScenarioReportService; import io.metersphere.sdk.constants.ApiExecuteResourceType; import io.metersphere.sdk.constants.ApiReportStatus; import io.metersphere.sdk.constants.ApplicationScope; @@ -38,6 +40,10 @@ public class MessageListener { private ApiTestCaseBatchRunService apiTestCaseBatchRunService; @Resource private ApiScenarioBatchRunService apiScenarioBatchRunService; + @Resource + private ApiReportService apiReportService; + @Resource + private ApiScenarioReportService apiScenarioReportService; @KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID) public void messageConsume(ConsumerRecord record) { @@ -78,9 +84,8 @@ public class MessageListener { } ApiExecuteResourceType resourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, queue.getResourceType()); - if (BooleanUtils.isTrue(queue.getRunModeConfig().getStopOnFailure()) && StringUtils.equals(dto.getReportStatus(), ApiReportStatus.ERROR.name())) { - // 如果是失败停止,清空队列,不继续执行 - apiExecutionQueueService.deleteQueue(queue.getQueueId()); + if (isStopOnFailure(dto, queue, resourceType)) { + // 失败停止,不执行后续任务 return; } @@ -95,4 +100,30 @@ public class MessageListener { LogUtils.error("执行任务失败:", e); } } + + /** + * 处理失败停止后的报告处理 + * @param dto + * @param queue + * @param resourceType + * @return + */ + private boolean isStopOnFailure(ApiNoticeDTO dto, ExecutionQueue queue, ApiExecuteResourceType resourceType) { + if (BooleanUtils.isTrue(queue.getRunModeConfig().getStopOnFailure()) && StringUtils.equals(dto.getReportStatus(), ApiReportStatus.ERROR.name())) { + String reportId = queue.getRunModeConfig().isIntegratedReport() ? queue.getRunModeConfig().getCollectionReport().getReportId() : dto.getReportId(); + if (resourceType.equals(ApiExecuteResourceType.API_SCENARIO)) { + apiScenarioBatchRunService.UpdateStopOnFailureReport(queue); + } + switch (resourceType) { + case API_CASE -> apiReportService.updateReportStatus(reportId, ApiReportStatus.ERROR.name()); + case API_SCENARIO -> apiScenarioReportService.updateReportStatus(reportId, ApiReportStatus.ERROR.name()); + default -> { + } + } + // 如果是失败停止,清空队列,不继续执行 + apiExecutionQueueService.deleteQueue(queue.getQueueId()); + return true; + } + return false; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java index 3e1a3dc250..d2a0915c26 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java @@ -263,12 +263,12 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter 10) { - LogUtils.error("批量执行用例失败,错误次数超过10次,停止执行"); - return; - } + if (runModeConfig.isIntegratedReport()) { + // 集成报告生成虚拟的报告ID + reportId = IDGenerator.nextStr(); + } else { + reportId = scenarioReportMap.get(id); + } + + TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiScenarioDetail, runModeConfig); + execute(taskRequest, apiScenarioDetail); + } catch (Exception e) { + LogUtils.error("执行用例失败 {}-{}", reportId, id); + LogUtils.error(e); + if (errorCount.getAndIncrement() > 10) { + LogUtils.error("批量执行用例失败,错误次数超过10次,停止执行"); + return; } } + } } private Map initReport(List ids, ApiRunModeConfigDTO runModeConfig, String userId) { @@ -223,7 +220,8 @@ public class ApiScenarioBatchRunService { } /** - * 集成报告,执行前先设置成 RUNNING + * 集成报告,执行前先设置成 RUNNING + * * @param runModeConfig */ private void setRunningIntegrateReport(ApiRunModeConfigDTO runModeConfig) { @@ -366,7 +364,7 @@ public class ApiScenarioBatchRunService { private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario, String userId) { ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, userId); apiScenarioReport.setEnvironmentId(getEnvId(runModeConfig, apiScenario)); - apiScenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiScenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); apiScenarioReport.setProjectId(apiScenario.getProjectId()); apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name()); return apiScenarioReport; @@ -398,4 +396,85 @@ public class ApiScenarioBatchRunService { return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getGrouped() : runModeConfig.getGrouped(); } + public void UpdateStopOnFailureReport(ExecutionQueue queue) { + ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); + try { + ExecutionQueueDetail queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + if (queueDetail == null) { + return; + } + Long requestCount = 0L; + while (queueDetail != null) { + ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(queueDetail.getResourceId()); + if (apiScenarioDetail == null) { + LogUtils.info("当前场景已删除 {}", queueDetail.getResourceId()); + continue; + } + + Long requestCountItem = getRequestCount(apiScenarioDetail.getSteps()); + requestCount += requestCountItem; + + // 初始化报告步骤 + if (runModeConfig.isIntegratedReport()) { + apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId()); + } else { + apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), queueDetail.getReportId()); + } + queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + } + if (runModeConfig.isIntegratedReport()) { + // 获取未执行的请求数,更新统计指标 + String reportId = runModeConfig.getCollectionReport().getReportId(); + ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId); + Long pendingCount = requestCount + report.getPendingCount(); + report.setPendingCount(pendingCount); + // 计算各种通过率 + report = computeRequestRate(report); + apiScenarioReportMapper.updateByPrimaryKeySelective(report); + } + } catch (Exception e) { + LogUtils.error("失败停止,补充报告步骤失败:", e); + } + } + + public ApiScenarioReport computeRequestRate(ApiScenarioReport report) { + long total = apiScenarioReportService.getRequestTotal(report); + // 计算各个概率 + double successRate = calculateRate(report.getSuccessCount(), total); + double errorRate = calculateRate(report.getErrorCount(), total); + double pendingRate = calculateRate(report.getPendingCount(), total); + double fakeErrorRate = calculateRate(report.getFakeErrorCount(), total); + + // 计算总和 + double sum = successRate + errorRate + pendingRate + fakeErrorRate; + + LogUtils.info("偏移总量重新计算", sum); + + // 避免分母为零 + double adjustment = sum > 0 ? 1.0 / sum : 0.0; + + // 调整概率,使总和精确为100% + successRate *= adjustment; + errorRate *= adjustment; + pendingRate *= adjustment; + fakeErrorRate *= adjustment; + + report.setRequestPassRate(formatRate(successRate)); + report.setRequestErrorRate(formatRate(errorRate)); + report.setRequestPendingRate(formatRate(pendingRate)); + report.setRequestFakeErrorRate(formatRate(fakeErrorRate)); + + return report; + } + + // 计算概率 + private static double calculateRate(long count, double total) { + return total > 0 ? count / total : 0.0; + } + + // 格式化概率,保留两位小数 + private static String formatRate(double rate) { + return String.format("%.2f", rate * 100); + } + } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java index cfe76f3f71..74b0b7bcea 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioReportService.java @@ -184,9 +184,6 @@ public class ApiScenarioReportService { BeanUtils.copyBean(scenarioReportDTO, scenarioReport); //需要查询出所有的步骤 List scenarioReportSteps = extApiScenarioReportMapper.selectStepByReportId(id); - if (CollectionUtils.isEmpty(scenarioReportSteps)) { - throw new MSException(Translator.get("api_scenario_report_not_exist")); - } if (BooleanUtils.isFalse(scenarioReport.getIntegrated())) { ApiScenarioBlob apiScenarioBlob = extApiScenarioReportMapper.getScenarioBlob(id); if (apiScenarioBlob != null) { @@ -236,12 +233,12 @@ public class ApiScenarioReportService { //将scenarioReportSteps按照parentId进行分组 值为list 然后根据sort进行排序 Map> scenarioReportStepMap = scenarioReportSteps.stream().collect(Collectors.groupingBy(ApiScenarioReportStepDTO::getParentId)); // TODO 查询修改 - List steps = scenarioReportStepMap.get("NONE"); + List steps = Optional.ofNullable(scenarioReportStepMap.get("NONE")).orElse(new ArrayList<>(0)); steps.sort(Comparator.comparingLong(ApiScenarioReportStepDTO::getSort)); getStepTree(steps, scenarioReportStepMap); scenarioReportDTO.setStepTotal(steps.size()); - scenarioReportDTO.setRequestTotal(scenarioReportDTO.getErrorCount() + scenarioReportDTO.getPendingCount() + scenarioReportDTO.getSuccessCount() + scenarioReportDTO.getFakeErrorCount()); + scenarioReportDTO.setRequestTotal(getRequestTotal(scenarioReportDTO)); scenarioReportDTO.setChildren(steps); scenarioReportDTO.setStepErrorCount(steps.stream().filter(step -> StringUtils.equals(ApiReportStatus.ERROR.name(), step.getStatus())).count()); @@ -274,6 +271,10 @@ public class ApiScenarioReportService { return scenarioReportDTO; } + public long getRequestTotal(ApiScenarioReport report) { + return report.getErrorCount() + report.getPendingCount() + report.getSuccessCount() + report.getFakeErrorCount(); + } + private static void getStepTree(List steps, Map> scenarioReportStepMap) { if (CollectionUtils.isNotEmpty(steps)) { List stepTypes = Arrays.asList(ApiScenarioStepType.IF_CONTROLLER.name(), 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 3bf9859256..3971ddc1c3 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 @@ -1710,19 +1710,22 @@ public class ApiScenarioService extends MoveNodeService { // 获取当前场景配置的环境信息 EnvironmentModeDTO environmentModeDTO = scenarioParseEnvInfo.getRefScenarioEnvMap().get(step.getResourceId()); - String environmentId = environmentModeDTO.getEnvironmentId(); - // 设置是否是环境组 - Boolean isGrouped = environmentModeDTO.getGrouped(); - msScenario.setGrouped(isGrouped); - Map envMap = scenarioParseEnvInfo.getEnvMap(); + if (environmentModeDTO != null) { + String environmentId = environmentModeDTO.getEnvironmentId(); - if (BooleanUtils.isTrue(isGrouped)) { - // 设置环境组 map - msScenario.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, environmentId)); - } else { - // 设置环境 - msScenario.setEnvironmentInfo(envMap.get(environmentId)); + // 设置是否是环境组 + Boolean isGrouped = environmentModeDTO.getGrouped(); + msScenario.setGrouped(isGrouped); + Map envMap = scenarioParseEnvInfo.getEnvMap(); + + if (BooleanUtils.isTrue(isGrouped)) { + // 设置环境组 map + msScenario.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, environmentId)); + } else { + // 设置环境 + msScenario.setEnvironmentInfo(envMap.get(environmentId)); + } } } @@ -2151,7 +2154,7 @@ public class ApiScenarioService extends MoveNodeService { } apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement); } - return stepDetail; + return JSON.parseObject(JSON.toJSONString(stepDetail)); } private void checkTargetModule(String targetModuleId, String projectId) {