From 4e4266972a82c5229f4cdba1fac4b6e0397ea060 Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Wed, 5 Jun 2024 19:18:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=B3=BB=E7=BB=9F=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=B8=AD=E5=BF=83=E5=A2=9E=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E8=AE=A1=E5=88=92=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/listener/MessageListener.java | 3 +- .../api/mapper/ExtApiReportMapper.xml | 1 - .../api/mapper/ExtApiScenarioReportMapper.xml | 1 - .../TestPlanTaskCenterController.java | 55 +++ .../plan/mapper/ExtTestPlanReportMapper.java | 12 +- .../plan/mapper/ExtTestPlanReportMapper.xml | 53 ++- .../service/TestPlanTaskCenterService.java | 190 ++++++++ .../TestPlanTaskCenterControllerTests.java | 135 ++++++ .../src/test/resources/dml/init_task_plan.sql | 12 + .../modules/project-management/taskCenter.ts | 16 + .../requrls/project-management/taskCenter.ts | 4 + frontend/src/enums/tableEnum.ts | 3 + .../taskCenter/component/taskCom.vue | 32 +- .../taskCenter/component/testPlan.vue | 419 ++++++++++++++++++ .../taskCenter/component/utils.ts | 5 +- .../test-plan/report/component/reportList.vue | 4 +- .../report/component/reportStatus.vue | 2 +- 17 files changed, 928 insertions(+), 19 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanTaskCenterController.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanTaskCenterService.java create mode 100644 backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTaskCenterControllerTests.java create mode 100644 backend/services/test-plan/src/test/resources/dml/init_task_plan.sql create mode 100644 frontend/src/views/project-management/taskCenter/component/testPlan.vue 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 b28861d72e..618e295c21 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 @@ -45,10 +45,9 @@ public class MessageListener { @KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID) public void messageConsume(ConsumerRecord record) { try { - LogUtils.info("接收到发送通知信息:{}", record.key()); if (ObjectUtils.isNotEmpty(record.value())) { ApiNoticeDTO dto = JSON.parseObject(record.value(), ApiNoticeDTO.class); - + LogUtils.info("接收到发送通知信息:{}", dto.getReportId()); // 集合报告不发送通知 if (!BooleanUtils.isTrue(dto.getIntegratedReport())) { apiReportSendNoticeService.sendNotice(dto); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml index 16aa9b18bf..5881701020 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiReportMapper.xml @@ -217,7 +217,6 @@ left join project on ar.project_id = project.id where ar.deleted = false - and ar.test_plan_id = 'NONE' and ar.start_time BETWEEN #{startTime} AND #{endTime} and ar.exec_status in ('PENDING', 'RUNNING', 'RERUNNING') diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioReportMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioReportMapper.xml index aca12b5ff4..76737f6834 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioReportMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioReportMapper.xml @@ -111,7 +111,6 @@ left join project on asr.project_id = project.id where asr.deleted = false - and asr.test_plan_id = 'NONE' and asr.start_time BETWEEN #{startTime} AND #{endTime} and asr.exec_status in ('PENDING', 'RUNNING', 'RERUNNING') diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanTaskCenterController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanTaskCenterController.java new file mode 100644 index 0000000000..d22adf4d98 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanTaskCenterController.java @@ -0,0 +1,55 @@ +package io.metersphere.plan.controller; + +import io.metersphere.plan.service.TestPlanTaskCenterService; +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.system.dto.taskcenter.TaskCenterDTO; +import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest; +import io.metersphere.system.utils.Pager; +import io.metersphere.system.utils.SessionUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping(value = "/task/center/plan") +@Tag(name = "任务中心-实时任务-测试计划") +public class TestPlanTaskCenterController { + + @Resource + private TestPlanTaskCenterService testPlanTaskCenterService; + + private static final String PROJECT = "project"; + private static final String ORG = "org"; + private static final String SYSTEM = "system"; + + + @PostMapping("/project/real-time/page") + @Operation(summary = "项目-任务中心-测试计划-实时任务列表") + public Pager> projectList(@Validated @RequestBody TaskCenterPageRequest request) { + return testPlanTaskCenterService.getProjectPage(request, SessionUtils.getCurrentProjectId()); + } + + @PostMapping("/org/real-time/page") + @Operation(summary = "组织-任务中心-测试计划-实时任务列表") + @RequiresPermissions(PermissionConstants.ORGANIZATION_TASK_CENTER_READ) + public Pager> orgList(@Validated @RequestBody TaskCenterPageRequest request) { + return testPlanTaskCenterService.getOrganizationPage(request, SessionUtils.getCurrentOrganizationId()); + } + + @PostMapping("/system/real-time/page") + @Operation(summary = "系统-任务中心-测试计划-实时任务列表") + @RequiresPermissions(PermissionConstants.SYSTEM_TASK_CENTER_READ) + public Pager> systemList(@Validated @RequestBody TaskCenterPageRequest request) { + return testPlanTaskCenterService.getSystemPage(request); + } + + +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.java index c9c7f07802..408a88dcfb 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.java @@ -5,6 +5,8 @@ import io.metersphere.plan.dto.request.TestPlanReportBatchRequest; import io.metersphere.plan.dto.request.TestPlanReportPageRequest; import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.system.dto.sdk.ApiReportMessageDTO; +import io.metersphere.system.dto.taskcenter.TaskCenterDTO; +import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -31,9 +33,13 @@ public interface ExtTestPlanReportMapper { List getNoticeList(@Param("ids") List subList); - long countReportByTime(@Param("time") long timeMills, @Param("projectId") String projectId); + long countReportByTime(@Param("time") long timeMills, @Param("projectId") String projectId); - List selectReportIdByProjectIdAndTime(@Param("time") long timeMills, @Param("projectId") String projectId); + List selectReportIdByProjectIdAndTime(@Param("time") long timeMills, @Param("projectId") String projectId); + + List selectReportIdTestPlanIds(@Param("testPlanIds") List testPlanIds); + + List taskCenterlist(@Param("request") TaskCenterPageRequest request, @Param("projectIds") List projectIds, + @Param("startTime") long startTime, @Param("endTime") long endTime); - List selectReportIdTestPlanIds(@Param("testPlanIds") List testPlanIds); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.xml index e7a4a1b4cb..cd95a6c854 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanReportMapper.xml @@ -59,6 +59,43 @@ #{testPlanId} + @@ -104,10 +141,24 @@ - + and tpr.result_status in + + + and tpr.project_id in + + #{value} + + + + + and project.organization_id in + + #{value} + + diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanTaskCenterService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanTaskCenterService.java new file mode 100644 index 0000000000..87ad4ce1a2 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanTaskCenterService.java @@ -0,0 +1,190 @@ +package io.metersphere.plan.service; + +import com.github.pagehelper.Page; +import com.github.pagehelper.page.PageMethod; +import io.metersphere.api.dto.definition.ExecuteReportDTO; +import io.metersphere.api.mapper.ExtApiScenarioReportMapper; +import io.metersphere.plan.mapper.ExtTestPlanReportMapper; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.DateUtils; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.domain.Organization; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.dto.taskcenter.TaskCenterDTO; +import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest; +import io.metersphere.system.log.service.OperationLogService; +import io.metersphere.system.mapper.BaseProjectMapper; +import io.metersphere.system.mapper.ExtOrganizationMapper; +import io.metersphere.system.mapper.OrganizationMapper; +import io.metersphere.system.service.TestResourcePoolService; +import io.metersphere.system.service.UserLoginService; +import io.metersphere.system.utils.PageUtils; +import io.metersphere.system.utils.Pager; +import jakarta.annotation.Resource; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanTaskCenterService { + + @Resource + ExtTestPlanReportMapper extTestPlanReportMapper; + + @Resource + ExtOrganizationMapper extOrganizationMapper; + + @Resource + BaseProjectMapper baseProjectMapper; + + @Resource + UserLoginService userLoginService; + + @Resource + ProjectMapper projectMapper; + + @Resource + OrganizationMapper organizationMapper; + + @Resource + ExtApiScenarioReportMapper extApiScenarioReportMapper; + + @Resource + TestResourcePoolService testResourcePoolService; + @Resource + OperationLogService operationLogService; + @Resource + private KafkaTemplate kafkaTemplate; + private static final String DEFAULT_SORT = "start_time desc"; + private final static String PROJECT_STOP = "/task/center/api/project/stop"; + private final static String ORG_STOP = "/task/center/api/org/stop"; + private final static String SYSTEM_STOP = "/task/center/api/system/stop"; + + /** + * 任务中心实时任务列表-项目级 + * + * @param request 请求参数 + * @return 任务中心实时任务列表 + */ + public Pager> getProjectPage(TaskCenterPageRequest request, String projectId) { + checkProjectExist(projectId); + List projectList = getProjectOption(projectId); + return createTaskCenterPager(request, projectList, false); + } + + /** + * 任务中心实时任务列表-组织级 + * + * @param request 请求参数 + * @return 任务中心实时任务列表 + */ + public Pager> getOrganizationPage(TaskCenterPageRequest request, String organizationId) { + checkOrganizationExist(organizationId); + List projectList = getOrgProjectList(organizationId); + return createTaskCenterPager(request, projectList, false); + } + + /** + * 任务中心实时任务列表-系统级 + * + * @param request 请求参数 + * @return 任务中心实时任务列表 + */ + public Pager> getSystemPage(TaskCenterPageRequest request) { + List projectList = getSystemProjectList(); + return createTaskCenterPager(request, projectList, true); + } + + private Pager> createTaskCenterPager(TaskCenterPageRequest request, List projectList, boolean isSystem) { + Page page = PageMethod.startPage(request.getCurrent(), request.getPageSize(), + StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : DEFAULT_SORT); + return PageUtils.setPageInfo(page, getPage(request, projectList, isSystem)); + } + + public List getPage(TaskCenterPageRequest request, List projectList, boolean isSystem) { + List list = new ArrayList<>(); + List projectIds = projectList.stream().map(OptionDTO::getId).toList(); + Map historyDeletedMap = new HashMap<>(); + list = extTestPlanReportMapper.taskCenterlist(request, isSystem ? new ArrayList<>() : projectIds, DateUtils.getDailyStartTime(), DateUtils.getDailyEndTime()); + //执行历史列表 + /*List reportIds = list.stream().map(TaskCenterDTO::getId).toList(); + if (CollectionUtils.isNotEmpty(reportIds)) { + List historyDeletedList = extTestPlanReportMapper.getHistoryDeleted(reportIds); + historyDeletedMap = historyDeletedList.stream().collect(Collectors.toMap(ExecuteReportDTO::getId, Function.identity())); + }*/ + processTaskCenter(list, projectList, projectIds, historyDeletedMap); + return list; + } + + private void processTaskCenter(List list, List projectList, List projectIds, Map historyDeletedMap) { + if (!list.isEmpty()) { + // 取所有的userid + Set userSet = list.stream() + .flatMap(item -> Stream.of(item.getOperationName())) + .collect(Collectors.toSet()); + Map userMap = userLoginService.getUserNameMap(new ArrayList<>(userSet)); + // 项目 + Map projectMap = projectList.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); + // 组织 + List orgListByProjectList = getOrgListByProjectIds(projectIds); + Map orgMap = orgListByProjectList.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); + + list.forEach(item -> { + item.setOperationName(userMap.getOrDefault(item.getOperationName(), StringUtils.EMPTY)); + item.setProjectName(projectMap.getOrDefault(item.getProjectId(), StringUtils.EMPTY)); + item.setOrganizationName(orgMap.getOrDefault(item.getProjectId(), StringUtils.EMPTY)); + item.setHistoryDeleted(MapUtils.isNotEmpty(historyDeletedMap) && !historyDeletedMap.containsKey(item.getId())); + }); + } + } + + private List getProjectOption(String id) { + return baseProjectMapper.getProjectOptionsById(id); + } + + private List getOrgProjectList(String orgId) { + return baseProjectMapper.getProjectOptionsByOrgId(orgId); + } + + private List getSystemProjectList() { + return baseProjectMapper.getProjectOptions(); + } + + private List getOrgListByProjectIds(List projectIds) { + return extOrganizationMapper.getOrgListByProjectIds(projectIds); + } + + /** + * 查看项目是否存在 + * + * @param projectId 项目ID + */ + private void checkProjectExist(String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + if (project == null) { + throw new MSException(Translator.get("project_not_exist")); + } + } + + /** + * 查看组织是否存在 + * + * @param orgId 组织ID + */ + private void checkOrganizationExist(String orgId) { + Organization organization = organizationMapper.selectByPrimaryKey(orgId); + if (organization == null) { + throw new MSException(Translator.get("organization_not_exist")); + } + } + +} diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTaskCenterControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTaskCenterControllerTests.java new file mode 100644 index 0000000000..c46419802e --- /dev/null +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTaskCenterControllerTests.java @@ -0,0 +1,135 @@ +package io.metersphere.plan.controller; + +import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.constants.TaskCenterResourceType; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.base.BaseTest; +import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.dto.taskcenter.request.TaskCenterPageRequest; +import io.metersphere.system.utils.Pager; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class TestPlanTaskCenterControllerTests extends BaseTest { + + private static final String BASE_PATH = "/task/center/plan/"; + private final static String REAL_TIME_PROJECT_PAGE = BASE_PATH + "project/real-time/page"; + private final static String REAL_TIME_ORG_PAGE = BASE_PATH + "org/real-time/page"; + private final static String REAL_TIME_SYSTEM_PAGE = BASE_PATH + "system/real-time/page"; + + private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError(); + + + @Test + @Order(9) + @Sql(scripts = {"/dml/init_task_plan.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void getPage() throws Exception { + + doTaskCenterPage("KEYWORD", REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPage("FILTER", REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPage("KEYWORD", REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPage("FILTER", REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPage("KEYWORD", REAL_TIME_SYSTEM_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPage("FILTER", REAL_TIME_SYSTEM_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + } + + private void doTaskCenterPage(String search, String url, String moduleType) throws Exception { + TaskCenterPageRequest request = new TaskCenterPageRequest(); + request.setModuleType(moduleType); + request.setCurrent(1); + request.setPageSize(10); + request.setSort(Map.of("startTime", "asc")); + // "KEYWORD", "FILTER" + switch (search) { + case "KEYWORD" -> configureKeywordSearch(request); + case "FILTER" -> configureFilterSearch(request); + default -> { + } + } + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(url) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.CURRENT_PROJECT, DEFAULT_PROJECT_ID) + .header(SessionConstants.CURRENT_ORGANIZATION, DEFAULT_ORGANIZATION_ID) + .content(JSON.toJSONString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + + // 获取返回值 + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + LogUtils.info(resultHolder); + // 返回请求正常 + Assertions.assertNotNull(resultHolder); + Pager pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class); + // 返回值不为空 + Assertions.assertNotNull(pageData); + // 返回值的页码和当前页码相同 + Assertions.assertEquals(pageData.getCurrent(), request.getCurrent()); + // 返回的数据量不超过规定要返回的数据量相同 + Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize()); + + } + + private void doTaskCenterPageError(String url, String moduleType) throws Exception { + TaskCenterPageRequest request = new TaskCenterPageRequest(); + request.setModuleType(moduleType); + request.setCurrent(1); + request.setPageSize(10); + request.setSort(Map.of("startTime", "asc")); + configureKeywordSearch(request); + + mockMvc.perform(MockMvcRequestBuilders.post(url) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.CURRENT_PROJECT, "DEFAULT_PROJECT_ID") + .header(SessionConstants.CURRENT_ORGANIZATION, "DEFAULT_ORGANIZATION_ID") + .content(JSON.toJSONString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(ERROR_REQUEST_MATCHER); + + } + + private void configureKeywordSearch(TaskCenterPageRequest request) { + request.setKeyword("18"); + request.setSort(Map.of("triggerMode", "asc")); + } + + private void configureFilterSearch(TaskCenterPageRequest request) { + Map> filters = new HashMap<>(); + request.setSort(Map.of()); + filters.put("triggerMode", List.of("MANUAL")); + request.setFilter(filters); + } + + @Test + @Order(10) + public void getPageError() throws Exception { + doTaskCenterPageError(REAL_TIME_PROJECT_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + doTaskCenterPageError(REAL_TIME_ORG_PAGE, TaskCenterResourceType.TEST_PLAN.toString()); + + } + + +} diff --git a/backend/services/test-plan/src/test/resources/dml/init_task_plan.sql b/backend/services/test-plan/src/test/resources/dml/init_task_plan.sql new file mode 100644 index 0000000000..c1dae4734c --- /dev/null +++ b/backend/services/test-plan/src/test/resources/dml/init_task_plan.sql @@ -0,0 +1,12 @@ +replace INTO `test_plan`(`id`, `num`, `project_id`, `group_id`, `module_id`, `name`, `status`, `type`, `tags`, `create_time`, `create_user`, `update_time`, `update_user`, `planned_start_time`, `planned_end_time`, `actual_start_time`, `actual_end_time`, `description`) +VALUES ('test_plan_id_1', 5000, '100001100001', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'); + +replace INTO `test_plan_report`(`id`, `test_plan_id`, `name`, `create_user`, `create_time`, `start_time`, `end_time`, `trigger_mode`, `exec_status`, `result_status`, `pass_threshold`, `pass_rate`, `project_id`, `integrated`, `deleted`) +VALUES +('test-plan-report-id-1', 'test_plan_id_1', '测试一下计划报告1', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', 'SUCCESS', '99.99', 100.00, '100001100001', 0, 0), +('test-plan-report-id-2', 'test_plan_id_1', '测试一下计划报告1', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001', 0, 0), +('test-plan-report-id-3', 'test_plan_id_1', '测试一下计划报告3', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001',1, 0), +('test-plan-report-id-4', 'test_plan_id_1', '测试一下计划报告4', 'admin', UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, UNIX_TIMESTAMP()*1000, 'MANUAL', 'PENDING', '-', '99.99', 100.00, '100001100001', 1, 0); + + diff --git a/frontend/src/api/modules/project-management/taskCenter.ts b/frontend/src/api/modules/project-management/taskCenter.ts index e2241348a2..2d3f318920 100644 --- a/frontend/src/api/modules/project-management/taskCenter.ts +++ b/frontend/src/api/modules/project-management/taskCenter.ts @@ -28,8 +28,11 @@ import { stopRealSysApiUrl, systemRealTotal, systemScheduleTotal, + taskOrgPlanRealCenterListUrl, taskOrgRealCenterListUrl, + taskProPlanRealCenterListUrl, taskProRealCenterListUrl, + taskSysPlanRealCenterListUrl, taskSysRealCenterListUrl, updateScheduleOrgTaskUrl, updateScheduleProTaskUrl, @@ -193,4 +196,17 @@ export function getProjectRealTotal() { return MSR.get({ url: `${projectRealTotal}` }); } +// 实时任务 测试计划 +export function getRealSysPlanList(data: TableQueryParams) { + return MSR.post>({ url: taskSysPlanRealCenterListUrl, data }); +} + +export function getRealOrgPlanList(data: TableQueryParams) { + return MSR.post>({ url: taskOrgPlanRealCenterListUrl, data }); +} + +export function getRealProPlanList(data: TableQueryParams) { + return MSR.post>({ url: taskProPlanRealCenterListUrl, data }); +} + export default {}; diff --git a/frontend/src/api/requrls/project-management/taskCenter.ts b/frontend/src/api/requrls/project-management/taskCenter.ts index 9035be6f9f..889fcfc870 100644 --- a/frontend/src/api/requrls/project-management/taskCenter.ts +++ b/frontend/src/api/requrls/project-management/taskCenter.ts @@ -76,3 +76,7 @@ export const projectScheduleTotal = '/task/center/project/schedule/total'; export const systemRealTotal = '/task/center/system/real/total'; export const orgRealTotal = '/task/center/org/real/total'; export const projectRealTotal = '/task/center/project/real/total'; + +export const taskSysPlanRealCenterListUrl = '/task/center/plan/system/real-time/page'; +export const taskOrgPlanRealCenterListUrl = '/task/center/plan/org/real-time/page'; +export const taskProPlanRealCenterListUrl = '/task/center/plan/project/real-time/page'; diff --git a/frontend/src/enums/tableEnum.ts b/frontend/src/enums/tableEnum.ts index 4f911c18bd..3f56ed1938 100644 --- a/frontend/src/enums/tableEnum.ts +++ b/frontend/src/enums/tableEnum.ts @@ -86,6 +86,9 @@ export enum TableKeyEnum { TASK_SCHEDULE_TASK_TEST_PLAN_SYSTEM = 'taskCenterScheduleTestPlanSystem', TASK_SCHEDULE_TASK_TEST_PLAN_ORGANIZATION = 'taskCenterScheduleTestPlanOrganization', TASK_SCHEDULE_TASK_TEST_PLAN_PROJECT = 'taskCenterScheduleTestPlanProject', + TASK_PLAN_SYSTEM = 'taskCenterPlanSystem', + TASK_PLAN_ORGANIZATION = 'taskCenterPlanOrganization', + TASK_PLAN_PROJECT = 'taskCenterPlanProject', } // 具有特殊功能的列 diff --git a/frontend/src/views/project-management/taskCenter/component/taskCom.vue b/frontend/src/views/project-management/taskCenter/component/taskCom.vue index 7d29f287b7..26cf63243e 100644 --- a/frontend/src/views/project-management/taskCenter/component/taskCom.vue +++ b/frontend/src/views/project-management/taskCenter/component/taskCom.vue @@ -14,7 +14,20 @@ - + + + @@ -25,10 +38,10 @@ import ApiCase from './apiCase.vue'; import ScheduledTask from './scheduledTask.vue'; + import TestPlan from './testPlan.vue'; import { useI18n } from '@/hooks/useI18n'; - import type { ResourceTypeMapKey } from '@/enums/taskCenter'; import { TaskCenterEnum } from '@/enums/taskCenter'; import type { ExtractedKeys } from './utils'; @@ -51,6 +64,10 @@ value: TaskCenterEnum.API_SCENARIO, label: t('project.taskCenter.apiScenario'), }, + { + value: TaskCenterEnum.TEST_PLAN, + label: t('project.taskCenter.testPlan'), + }, // TODO 第一个版本目前不上以下几类 // { // value: TaskCenterEnum.UI_TEST, @@ -60,10 +77,6 @@ // value: TaskCenterEnum.LOAD_TEST, // label: t('project.taskCenter.performanceTest'), // }, - // { - // value: TaskCenterEnum.TEST_PLAN, - // label: t('project.taskCenter.testPlan'), - // }, ]); const timingTabList = ref([ @@ -78,7 +91,7 @@ { value: TaskCenterEnum.TEST_PLAN, label: t('project.taskCenter.testPlan'), - } + }, ]); const activeTask = ref(route.query.tab || 'real'); @@ -110,10 +123,12 @@ .box { display: flex; height: 100%; + .left { width: 252px; height: 100%; border-right: 1px solid var(--color-text-n8); + .item { padding: 0 20px; height: 38px; @@ -121,18 +136,21 @@ line-height: 38px; border-radius: 4px; cursor: pointer; + &.active { color: rgb(var(--primary-5)); background: rgb(var(--primary-1)); } } } + .right { width: calc(100% - 300px); flex-grow: 1; /* 自适应 */ height: 100%; } } + .no-content { :deep(.arco-tabs-content) { padding-top: 0; diff --git a/frontend/src/views/project-management/taskCenter/component/testPlan.vue b/frontend/src/views/project-management/taskCenter/component/testPlan.vue new file mode 100644 index 0000000000..cc99907a35 --- /dev/null +++ b/frontend/src/views/project-management/taskCenter/component/testPlan.vue @@ -0,0 +1,419 @@ + + + + + diff --git a/frontend/src/views/project-management/taskCenter/component/utils.ts b/frontend/src/views/project-management/taskCenter/component/utils.ts index 85af900725..4a2efec5b8 100644 --- a/frontend/src/views/project-management/taskCenter/component/utils.ts +++ b/frontend/src/views/project-management/taskCenter/component/utils.ts @@ -121,7 +121,10 @@ export const TaskStatus: Record; +export type ExtractedKeys = Extract< + ResourceTypeMapKey, + TaskCenterEnum.API_CASE | TaskCenterEnum.API_SCENARIO | TaskCenterEnum.TEST_PLAN +>; export const resourceTypeMap: Record> = { [TaskCenterEnum.API_CASE]: { diff --git a/frontend/src/views/test-plan/report/component/reportList.vue b/frontend/src/views/test-plan/report/component/reportList.vue index f7391310cc..e46c72f318 100644 --- a/frontend/src/views/test-plan/report/component/reportList.vue +++ b/frontend/src/views/test-plan/report/component/reportList.vue @@ -61,7 +61,7 @@