From 205dfc087e70d8a22b976d993574403b26d0aca5 Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Tue, 2 Jan 2024 11:40:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=BB=93=E6=9D=9F=E5=8F=91=E9=80=81=E9=80=9A?= =?UTF-8?q?=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/dto/api/notice/ApiNoticeDTO.java | 20 +++ .../api/listener/MessageListener.java | 37 ++++++ .../service/ApiReportSendNoticeService.java | 118 ++++++++++++++++++ .../api/listener/MessageListenerTest.java | 73 +++++++++++ 4 files changed, 248 insertions(+) create mode 100644 backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/notice/ApiNoticeDTO.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportSendNoticeService.java create mode 100644 backend/services/api-test/src/test/java/io/metersphere/api/listener/MessageListenerTest.java diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/notice/ApiNoticeDTO.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/notice/ApiNoticeDTO.java new file mode 100644 index 0000000000..4cbfb41543 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/notice/ApiNoticeDTO.java @@ -0,0 +1,20 @@ +package io.metersphere.sdk.dto.api.notice; + +import io.metersphere.sdk.constants.ApiExecuteResourceType; +import lombok.Data; + +import java.io.Serial; + +@Data +public class ApiNoticeDTO implements java.io.Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private ApiExecuteResourceType resourceType; + private String resourceId; + private String reportStatus; + private String userId; + private String projectId; + private String environmentId; + private String reportId; +} 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 new file mode 100644 index 0000000000..15fdf02e1b --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/listener/MessageListener.java @@ -0,0 +1,37 @@ +package io.metersphere.api.listener; + +import io.metersphere.api.service.ApiReportSendNoticeService; +import io.metersphere.sdk.constants.KafkaTopicConstants; +import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO; +import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.LogUtils; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.KafkaListener; + +@Configuration +public class MessageListener { + public static final String MESSAGE_CONSUME_ID = "MS-API-MESSAGE-CONSUME"; + @Resource + private ApiReportSendNoticeService apiReportSendNoticeService; + + @KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID) + public void messageConsume(ConsumerRecord record) { + try { + if (apiReportSendNoticeService == null) { + apiReportSendNoticeService = CommonBeanFactory.getBean(ApiReportSendNoticeService.class); + } + + LogUtils.info("接收到发送通知信息:", record.key()); + if (ObjectUtils.isNotEmpty(record.value())) { + ApiNoticeDTO dto = JSON.parseObject(record.value(), ApiNoticeDTO.class); + apiReportSendNoticeService.sendNotice(dto); + } + } catch (Exception e) { + LogUtils.error("接收到发送通知信息:", e); + } + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportSendNoticeService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportSendNoticeService.java new file mode 100644 index 0000000000..8bbd2b1a55 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiReportSendNoticeService.java @@ -0,0 +1,118 @@ +package io.metersphere.api.service; + +import io.metersphere.api.domain.ApiDefinition; +import io.metersphere.api.domain.ApiScenario; +import io.metersphere.api.domain.ApiTestCase; +import io.metersphere.api.mapper.ApiDefinitionMapper; +import io.metersphere.api.mapper.ApiScenarioMapper; +import io.metersphere.api.mapper.ApiTestCaseMapper; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.constants.ApiReportStatus; +import io.metersphere.sdk.domain.Environment; +import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO; +import io.metersphere.sdk.mapper.EnvironmentMapper; +import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.system.domain.User; +import io.metersphere.system.dto.sdk.BaseSystemConfigDTO; +import io.metersphere.system.mapper.UserMapper; +import io.metersphere.system.notice.NoticeModel; +import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.service.NoticeSendService; +import io.metersphere.system.service.SystemParameterService; +import jakarta.annotation.Resource; +import org.apache.commons.beanutils.BeanMap; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class ApiReportSendNoticeService { + + @Resource + private ApiScenarioMapper apiScenarioMapper; + @Resource + private NoticeSendService noticeSendService; + @Resource + private UserMapper userMapper; + @Resource + private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private ApiDefinitionMapper apiDefinitionMapper; + @Resource + private EnvironmentMapper environmentMapper; + @Resource + private ProjectMapper projectMapper; + + public void sendNotice(ApiNoticeDTO noticeDTO) { + String noticeType; + String reportUrl; + SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class); + assert systemParameterService != null; + BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo(); + BeanMap beanMap; + switch (noticeDTO.getResourceType()) { + case ApiExecuteResourceType.API_SCENARIO: + ApiScenario scenario = apiScenarioMapper.selectByPrimaryKey(noticeDTO.getResourceId()); + beanMap = new BeanMap(scenario); + noticeType = NoticeConstants.TaskType.API_SCENARIO_TASK; + reportUrl = baseSystemConfigDTO.getUrl() + "/#/api/automation/report/view/" + noticeDTO.getReportId(); + break; + case ApiExecuteResourceType.API: + ApiDefinition definition = apiDefinitionMapper.selectByPrimaryKey(noticeDTO.getResourceId()); + beanMap = new BeanMap(definition); + noticeType = NoticeConstants.TaskType.API_DEFINITION_TASK; + + // TODO: 缺少生成分享链接 + reportUrl = baseSystemConfigDTO.getUrl() + "/#/api/automation/report/view/" + noticeDTO.getReportId(); + break; + default: + ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(noticeDTO.getResourceId()); + beanMap = new BeanMap(testCase); + + // TODO 是否需要区分场景和用例 + noticeType = NoticeConstants.TaskType.API_DEFINITION_TASK; + reportUrl = baseSystemConfigDTO.getUrl() + "/#/api/automation/report/view/" + noticeDTO.getReportId(); + break; + } + + String event; + String status; + if (StringUtils.endsWithIgnoreCase(noticeDTO.getReportStatus(), ApiReportStatus.SUCCESS.name())) { + event = NoticeConstants.Event.EXECUTE_SUCCESSFUL; + status = "成功"; + } else { + event = NoticeConstants.Event.EXECUTE_FAILED; + status = "失败"; + } + String userId = noticeDTO.getUserId(); + User user = userMapper.selectByPrimaryKey(userId); + + Map paramMap = new HashMap(beanMap); + paramMap.put("operator", user != null ? user.getName() : ""); + paramMap.put("status", noticeDTO.getReportStatus()); + Environment environment = environmentMapper.selectByPrimaryKey(noticeDTO.getEnvironmentId()); + if (environment != null) { + paramMap.put("environment", environment.getName()); + } else { + paramMap.put("environment", "未配置"); + } + paramMap.put("reportUrl", reportUrl); + + + // TODO: 缺少生成分享链接 + String shareUrl = null; + paramMap.put("scenarioShareUrl", baseSystemConfigDTO.getUrl() + "/api/share-api-report" + shareUrl); + String context = "${operator}执行接口测试" + status + ": ${name}"; + NoticeModel noticeModel = NoticeModel.builder().operator(userId) + .context(context).subject("执行通知").paramMap(paramMap).event(event).build(); + + Project project = projectMapper.selectByPrimaryKey(noticeDTO.getProjectId()); + + noticeSendService.send(project, noticeType, noticeModel); + } + +} diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/listener/MessageListenerTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/listener/MessageListenerTest.java new file mode 100644 index 0000000000..08d46736cb --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/listener/MessageListenerTest.java @@ -0,0 +1,73 @@ +package io.metersphere.api.listener; + +import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.constants.KafkaTopicConstants; +import io.metersphere.sdk.dto.api.notice.ApiNoticeDTO; +import io.metersphere.sdk.util.JSON; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.mockito.InjectMocks; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class MessageListenerTest { + + @InjectMocks + private MessageListener messageListener; + + @Test + void testDebugConsume() { + // 模拟参数 + ApiNoticeDTO api = new ApiNoticeDTO(); + + // Set values for the fields + api.setResourceType(ApiExecuteResourceType.API); + api.setResourceId("exampleResourceId"); + api.setReportStatus("exampleReportStatus"); + api.setUserId("exampleUserId"); + api.setProjectId("exampleProjectId"); + api.setEnvironmentId("exampleEnvironmentId"); + api.setReportId("exampleReportId"); + + ConsumerRecord record = new ConsumerRecord<>(KafkaTopicConstants.API_REPORT_TASK_TOPIC, 0, 0, "123", JSON.toJSONString(api)); + // 调用被测试方法 + messageListener.messageConsume(record); + + // 模拟参数 + ApiNoticeDTO scenario = new ApiNoticeDTO(); + + // Set values for the fields + scenario.setResourceType(ApiExecuteResourceType.API); + scenario.setResourceId("exampleResourceId"); + scenario.setReportStatus("exampleReportStatus"); + scenario.setUserId("exampleUserId"); + scenario.setProjectId("exampleProjectId"); + scenario.setEnvironmentId("exampleEnvironmentId"); + scenario.setReportId("exampleReportId"); + + ConsumerRecord scenarioRecord = new ConsumerRecord<>(KafkaTopicConstants.API_REPORT_TASK_TOPIC, 0, 0, "123", JSON.toJSONString(scenario)); + // 调用被测试方法 + messageListener.messageConsume(scenarioRecord); + + // 模拟参数 + ApiNoticeDTO testCase = new ApiNoticeDTO(); + + // Set values for the fields + testCase.setResourceType(ApiExecuteResourceType.API); + testCase.setResourceId("exampleResourceId"); + testCase.setReportStatus("exampleReportStatus"); + testCase.setUserId("exampleUserId"); + testCase.setProjectId("exampleProjectId"); + testCase.setEnvironmentId("exampleEnvironmentId"); + testCase.setReportId("exampleReportId"); + + ConsumerRecord testCaseRecord = new ConsumerRecord<>(KafkaTopicConstants.API_REPORT_TASK_TOPIC, 0, 0, "123", JSON.toJSONString(testCase)); + // 调用被测试方法 + messageListener.messageConsume(testCaseRecord); + } +}