diff --git a/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java b/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java index 7eef92ea16..97f09dc255 100644 --- a/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java +++ b/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java @@ -1,7 +1,6 @@ package io.metersphere.base.mapper.ext; import io.metersphere.api.dto.QueryAPIReportRequest; -import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult; import io.metersphere.base.domain.ApiDefinitionExecResult; import io.metersphere.base.domain.ApiDefinitionExecResultExpand; import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; @@ -26,8 +25,6 @@ public interface ExtApiDefinitionExecResultMapper { long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("version") String version, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp); - List findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("selectFunctionCase") boolean selectFunctionCase, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber); - String selectExecResult(String resourceId); ApiDefinitionExecResultWithBLOBs selectPlanApiMaxResultByTestIdAndType(String resourceId, String type); diff --git a/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml b/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml index 0b85369823..2098f279d3 100644 --- a/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml +++ b/api-test/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml @@ -39,105 +39,6 @@ WHERE integrated_report_id = #{0} - + + diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java index 057db0223a..33e78aa21e 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java @@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext; import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.dto.ExecutedCaseInfoResult; import io.metersphere.plan.dto.CaseExecResult; import io.metersphere.plan.dto.TestPlanApiScenarioInfoDTO; import org.apache.ibatis.annotations.Param; @@ -20,4 +21,6 @@ public interface ExtTestPlanScenarioCaseMapper { TestPlanApiScenario selectBaseInfoById(String testId); List selectReportStatusByReportIds(@Param("ids") List scenarioReportIdList); + + List findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber); } diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml index f96cca20f8..2c1cfb5d17 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml @@ -46,4 +46,32 @@ #{value} + + diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.java b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.java index 216e2a489e..868b0de84a 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.java +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.java @@ -7,6 +7,7 @@ import io.metersphere.plan.request.function.QueryTestPlanCaseRequest; import io.metersphere.plan.request.function.TestPlanFuncCaseConditions; import io.metersphere.request.BaseQueryRequest; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; @@ -76,4 +77,9 @@ public interface ExtTestPlanTestCaseMapper { void updateExecResultByTestCaseIdAndTestPlanId(@Param("testCaseId") String testCaseId, @Param("testPlanId") String testPlanId, @Param("execResult") String execResult); List selectByAutomationCaseIdAndTestPlanId(@Param("automationCaseId") String automationCaseId, @Param("test_plan_id") String testPlanId); + + List findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber); + + @Select("SELECT id FROM test_plan_test_case WHERE plan_id = #{planId} AND case_id = #{caseId}") + List selectIdByTestCaseIdAndTestPlanId(@Param("caseId") String caseId, @Param("planId") String planId); } diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml index 2e6d35741c..a69d913c40 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml @@ -666,5 +666,36 @@ UPDATE test_plan_test_case SET status = #{execResult} WHERE case_id = #{testCaseId} AND plan_id = #{testPlanId} - + diff --git a/test-track/backend/src/main/java/io/metersphere/config/KafkaConfig.java b/test-track/backend/src/main/java/io/metersphere/config/KafkaConfig.java new file mode 100644 index 0000000000..1493d03e05 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/config/KafkaConfig.java @@ -0,0 +1,61 @@ +package io.metersphere.config; + +import jakarta.annotation.Resource; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; +import org.springframework.kafka.listener.ContainerProperties; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConfig { + @Resource + private KafkaProperties kafkaProperties; + + @Bean + public KafkaListenerContainerFactory> batchFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfigs())); + //并发数量 + factory.setConcurrency(1); + //开启批量监听 + factory.setBatchListener(true); + + factory.getContainerProperties().setPollTimeout(5000L); + + //设置提交偏移量的方式, + factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE); + + return factory; + } + + public Map consumerConfigs() { + Map producerProps = new HashMap<>(); + producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers()); + producerProps.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, kafkaProperties.getMaxRequestSize()); + // 批量一次最大拉取数据量 + producerProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 100); + + // 消费者每次去kafka拉取数据最大间隔,服务端会认为消费者已离线。触发reBalance + producerProps.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 900000); + // 心跳检查 + producerProps.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 5000); + // 手动提交 配置 false + producerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); + + producerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + producerProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 5000); + + producerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + producerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + return producerProps; + } +} diff --git a/test-track/backend/src/main/java/io/metersphere/controller/TrackController.java b/test-track/backend/src/main/java/io/metersphere/controller/TrackController.java index 52c829dbe9..ff476d1175 100644 --- a/test-track/backend/src/main/java/io/metersphere/controller/TrackController.java +++ b/test-track/backend/src/main/java/io/metersphere/controller/TrackController.java @@ -1,23 +1,27 @@ package io.metersphere.controller; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; import io.metersphere.base.domain.TestCase; import io.metersphere.commons.constants.MicroServiceName; -import io.metersphere.dto.BugStatistics; -import io.metersphere.dto.TrackCountResult; -import io.metersphere.dto.TrackStatisticsDTO; +import io.metersphere.commons.utils.PageUtils; +import io.metersphere.commons.utils.Pager; +import io.metersphere.dto.*; import io.metersphere.i18n.Translator; import io.metersphere.plan.dto.ChartsData; import io.metersphere.service.TestCaseService; import io.metersphere.service.TrackService; import io.metersphere.utils.DiscoveryUtil; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.List; @RestController @@ -58,6 +62,32 @@ public class TrackController { return statistics; } + @GetMapping("/failure/case/about/plan/{projectId}/{versionId}/{limitNumber}/{goPage}/{pageSize}") + public Pager> failureCaseAboutTestPlan(@PathVariable String projectId, @PathVariable String versionId, + @PathVariable int limitNumber, @PathVariable int goPage, @PathVariable int pageSize) { + if (StringUtils.equalsIgnoreCase(versionId, "default")) { + versionId = null; + } + Page page = PageHelper.startPage(goPage, pageSize, true); + List selectDataList = trackService.findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(projectId, versionId, limitNumber); + List returnList = new ArrayList<>(selectDataList.size()); + for (int dataIndex = 0; dataIndex < selectDataList.size(); dataIndex++) { + ExecutedCaseInfoDTO dataDTO = new ExecutedCaseInfoDTO(); + dataDTO.setSortIndex(dataIndex + 1); + ExecutedCaseInfoResult selectData = selectDataList.get(dataIndex); + dataDTO.setCaseID(selectData.getTestCaseID()); + dataDTO.setCaseName(selectData.getCaseName()); + dataDTO.setTestPlan(selectData.getTestPlan()); + dataDTO.setFailureTimes(selectData.getFailureTimes()); + dataDTO.setTestPlanId(selectData.getTestPlanId()); + dataDTO.setCaseType(selectData.getCaseType()); + dataDTO.setId(selectData.getId()); + dataDTO.setTestPlanDTOList(selectData.getTestPlanDTOList()); + returnList.add(dataDTO); + } + return PageUtils.setPageInfo(page, returnList); + } + @GetMapping("/relevance/count/{projectId}") public TrackStatisticsDTO getRelevanceCount(@PathVariable String projectId) { TrackStatisticsDTO statistics = new TrackStatisticsDTO(); diff --git a/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoDTO.java b/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoDTO.java new file mode 100644 index 0000000000..03326fe572 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoDTO.java @@ -0,0 +1,31 @@ +package io.metersphere.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 已执行的案例 + */ +@Getter +@Setter +public class ExecutedCaseInfoDTO { + //排名 + private int sortIndex; + //案例名称 + private String caseName; + //所属测试计划 + private String testPlan; + private String testPlanId; + //失败次数 + private Long failureTimes; + //案例类型 + private String caseType; + //案例ID -- 目前被用为案例-测试计划 关联表ID + private String caseID; + //ID + private String id; + //测试计划集合 + private List testPlanDTOList; +} diff --git a/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoResult.java b/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoResult.java new file mode 100644 index 0000000000..ad7ee3be5b --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/dto/ExecutedCaseInfoResult.java @@ -0,0 +1,26 @@ +package io.metersphere.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 已执行的案例 + */ +@Getter +@Setter +public class ExecutedCaseInfoResult { + private String testCaseID; + private String id; + //案例名称 + private String caseName; + //所属测试计划 + private String testPlan; + private String testPlanId; + //失败次数 + private Long failureTimes; + //案例类型 + private String caseType; + private List testPlanDTOList; +} diff --git a/test-track/backend/src/main/java/io/metersphere/dto/TestPlanDTO.java b/test-track/backend/src/main/java/io/metersphere/dto/TestPlanDTO.java new file mode 100644 index 0000000000..6da9b0487a --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/dto/TestPlanDTO.java @@ -0,0 +1,38 @@ +package io.metersphere.dto; + +import io.metersphere.base.domain.TestPlanWithBLOBs; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TestPlanDTO extends TestPlanWithBLOBs { + private String projectName; + private String userName; + private List projectIds; + private List principals; + private List follows; + /** + * 定时任务ID + */ + private String scheduleId; + /** + * 定时任务是否开启 + */ + private boolean scheduleOpen; + /** + * 定时任务状态 + */ + private String scheduleStatus; + /** + * 定时任务规则 + */ + private String scheduleCorn; + /** + * 定时任务下一次执行时间 + */ + private Long scheduleExecuteTime; + private String workspaceName; +} diff --git a/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListener.java b/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListener.java index 20ff3c02d6..8514f1f370 100644 --- a/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListener.java +++ b/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListener.java @@ -1,24 +1,22 @@ package io.metersphere.listener; -import io.metersphere.base.domain.ApiExecutionQueue; -import io.metersphere.base.domain.ApiExecutionQueueDetailExample; -import io.metersphere.base.domain.ApiExecutionQueueExample; import io.metersphere.base.mapper.ApiExecutionQueueDetailMapper; import io.metersphere.base.mapper.ApiExecutionQueueMapper; import io.metersphere.commons.constants.KafkaTopicConstants; -import io.metersphere.commons.constants.TestPlanReportStatus; import io.metersphere.plan.service.AutomationCaseExecOverService; import io.metersphere.plan.service.TestPlanReportService; import io.metersphere.utils.LoggerUtil; +import io.metersphere.utils.NamedThreadFactory; import jakarta.annotation.Resource; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.Acknowledgment; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; import java.util.List; -import java.util.stream.Collectors; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; @Component public class ExecReportListener { @@ -32,45 +30,40 @@ public class ExecReportListener { @Resource private AutomationCaseExecOverService automationCaseExecOverService; - @KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.TEST_PLAN_REPORT_TOPIC, groupId = "${spring.application.name}") - public void consume(ConsumerRecord record) { - Object testIdObj = record.key(); - if (ObjectUtils.isEmpty(testIdObj)) { - LoggerUtil.info("Execute message. received:", record.value()); - this.testPlanReportTestEnded(record.value()); - } else { - LoggerUtil.info("Execute message. key:[" + testIdObj.toString() + "], received:", record.value()); - this.automationCaseTestEnd(testIdObj.toString()); - } + // 线程池维护线程的最少数量 + private final static int CORE_POOL_SIZE = 5; + // 线程池维护线程的最大数量 + private final static int MAX_POOL_SIZE = 5; + // 线程池维护线程所允许的空闲时间 + private final static int KEEP_ALIVE_TIME = 1; + // 线程池所使用的缓冲队列大小 + private final static int WORK_QUEUE_SIZE = 10000; + private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( + CORE_POOL_SIZE, + MAX_POOL_SIZE, + KEEP_ALIVE_TIME, + TimeUnit.SECONDS, + new ArrayBlockingQueue(WORK_QUEUE_SIZE), + new NamedThreadFactory("MS-TEST-PLAN-CASE-EXEC-LISTENER-TASK")); - } + @KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.TEST_PLAN_REPORT_TOPIC, groupId = "${spring.application.name}", containerFactory = "batchFactory") + public void consume(List> records, Acknowledgment ack) { - public void automationCaseTestEnd(String testId) { - //自动化用例执行完成之后的后续操作 - automationCaseExecOverService.automationCaseExecOver(testId); - } - - public void testPlanReportTestEnded(String testPlanReportId) { - // 检查测试计划中其他队列是否结束 - ApiExecutionQueueExample executionQueueExample = new ApiExecutionQueueExample(); - executionQueueExample.createCriteria().andReportIdEqualTo(testPlanReportId); - List queues = queueMapper.selectByExample(executionQueueExample); - if (CollectionUtils.isEmpty(queues)) { - LoggerUtil.info("Normal execution completes, update test plan report status:" + testPlanReportId); - testPlanReportService.testPlanExecuteOver(testPlanReportId, TestPlanReportStatus.COMPLETED.name()); - } else { - List ids = queues.stream().map(ApiExecutionQueue::getId).collect(Collectors.toList()); - ApiExecutionQueueDetailExample detailExample = new ApiExecutionQueueDetailExample(); - detailExample.createCriteria().andQueueIdIn(ids); - long count = executionQueueDetailMapper.countByExample(detailExample); - if (count == 0) { - LoggerUtil.info("Normal execution completes, update test plan report status:" + testPlanReportId); - testPlanReportService.testPlanExecuteOver(testPlanReportId, TestPlanReportStatus.COMPLETED.name()); - LoggerUtil.info("Clear Queue:" + ids); - ApiExecutionQueueExample queueExample = new ApiExecutionQueueExample(); - queueExample.createCriteria().andIdIn(ids); - queueMapper.deleteByExample(queueExample); - } + try { + records.forEach(item -> { + LoggerUtil.info("接收到报告【key:" + item.key() + ",value:" + item.value() + "】,加入到结果处理队列"); + ExecReportListenerTask task = new ExecReportListenerTask(); + task.setApiExecutionQueueMapper(queueMapper); + task.setApiExecutionQueueDetailMapper(executionQueueDetailMapper); + task.setAutomationCaseExecOverService(automationCaseExecOverService); + task.setTestPlanReportService(testPlanReportService); + task.setRecord(item); + threadPool.execute(task); + }); + } catch (Exception e) { + LoggerUtil.error("测试计划自动化用例结束后-KAFKA消费失败:", e); + } finally { + ack.acknowledge(); } } } diff --git a/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListenerTask.java b/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListenerTask.java new file mode 100644 index 0000000000..9d03811af6 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/listener/ExecReportListenerTask.java @@ -0,0 +1,71 @@ +package io.metersphere.listener; + +import io.metersphere.base.domain.ApiExecutionQueue; +import io.metersphere.base.domain.ApiExecutionQueueDetailExample; +import io.metersphere.base.domain.ApiExecutionQueueExample; +import io.metersphere.base.mapper.ApiExecutionQueueDetailMapper; +import io.metersphere.base.mapper.ApiExecutionQueueMapper; +import io.metersphere.commons.constants.TestPlanReportStatus; +import io.metersphere.plan.service.AutomationCaseExecOverService; +import io.metersphere.plan.service.TestPlanReportService; +import io.metersphere.utils.LoggerUtil; +import lombok.Data; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class ExecReportListenerTask implements Runnable { + private ConsumerRecord record; + + protected ApiExecutionQueueMapper apiExecutionQueueMapper; + private ApiExecutionQueueDetailMapper apiExecutionQueueDetailMapper; + private TestPlanReportService testPlanReportService; + private AutomationCaseExecOverService automationCaseExecOverService; + + + @Override + public void run() { + Object testIdObj = record.key(); + if (ObjectUtils.isEmpty(testIdObj)) { + LoggerUtil.info("Execute message. received:", record.value()); + this.testPlanReportTestEnded(record.value()); + } else { + LoggerUtil.info("Execute message. key:[" + testIdObj.toString() + "], received:", record.value()); + this.automationCaseTestEnd(testIdObj.toString()); + } + } + + + public void automationCaseTestEnd(String testId) { + //自动化用例执行完成之后的后续操作 + automationCaseExecOverService.automationCaseExecOver(testId); + } + + public void testPlanReportTestEnded(String testPlanReportId) { + // 检查测试计划中其他队列是否结束 + ApiExecutionQueueExample executionQueueExample = new ApiExecutionQueueExample(); + executionQueueExample.createCriteria().andReportIdEqualTo(testPlanReportId); + List queues = apiExecutionQueueMapper.selectByExample(executionQueueExample); + if (CollectionUtils.isEmpty(queues)) { + LoggerUtil.info("Normal execution completes, update test plan report status:" + testPlanReportId); + testPlanReportService.testPlanExecuteOver(testPlanReportId, TestPlanReportStatus.COMPLETED.name()); + } else { + List ids = queues.stream().map(ApiExecutionQueue::getId).collect(Collectors.toList()); + ApiExecutionQueueDetailExample detailExample = new ApiExecutionQueueDetailExample(); + detailExample.createCriteria().andQueueIdIn(ids); + long count = apiExecutionQueueDetailMapper.countByExample(detailExample); + if (count == 0) { + LoggerUtil.info("Normal execution completes, update test plan report status:" + testPlanReportId); + testPlanReportService.testPlanExecuteOver(testPlanReportId, TestPlanReportStatus.COMPLETED.name()); + LoggerUtil.info("Clear Queue:" + ids); + ApiExecutionQueueExample queueExample = new ApiExecutionQueueExample(); + queueExample.createCriteria().andIdIn(ids); + apiExecutionQueueMapper.deleteByExample(queueExample); + } + } + } +} diff --git a/test-track/backend/src/main/java/io/metersphere/plan/service/AutomationCaseExecOverService.java b/test-track/backend/src/main/java/io/metersphere/plan/service/AutomationCaseExecOverService.java index 4f8b3a7666..1ca3fde58f 100644 --- a/test-track/backend/src/main/java/io/metersphere/plan/service/AutomationCaseExecOverService.java +++ b/test-track/backend/src/main/java/io/metersphere/plan/service/AutomationCaseExecOverService.java @@ -4,9 +4,13 @@ import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.base.domain.TestPlanApiScenario; import io.metersphere.base.domain.TestPlanLoadCase; import io.metersphere.base.domain.TestPlanUiScenario; -import io.metersphere.base.mapper.ext.*; +import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanUiCaseMapper; import io.metersphere.dto.TestPlanCaseStatusDTO; import io.metersphere.utils.JsonUtils; +import io.metersphere.utils.LoggerUtil; import io.metersphere.websocket.UICaseStatusHandleSocket; import jakarta.annotation.Resource; import org.apache.commons.lang3.ObjectUtils; @@ -71,8 +75,13 @@ public class AutomationCaseExecOverService { testCaseSyncStatusService.updateFunctionCaseStatusByAutomationCaseId(automationCaseId, planId, triggerCaseExecResult); } if (testPlanUiScenario != null) { - //UI执行完成发送Socket - UICaseStatusHandleSocket.sendMessageSingle(planId, JsonUtils.toJSONString(TestPlanCaseStatusDTO.builder().planCaseId(testPlanUiScenario.getId()).planCaseStatus(triggerCaseExecResult))); + try { + //UI执行完成发送Socket + UICaseStatusHandleSocket.sendMessageSingle(planId, JsonUtils.toJSONString(TestPlanCaseStatusDTO.builder().planCaseId(testPlanUiScenario.getId()).planCaseStatus(triggerCaseExecResult))); + } catch (Exception e) { + LoggerUtil.error("ui执行完成发送socket失败!", e); + } + } } } diff --git a/test-track/backend/src/main/java/io/metersphere/plan/service/TestCaseSyncStatusService.java b/test-track/backend/src/main/java/io/metersphere/plan/service/TestCaseSyncStatusService.java index 7bfc628b60..36c0db75e0 100644 --- a/test-track/backend/src/main/java/io/metersphere/plan/service/TestCaseSyncStatusService.java +++ b/test-track/backend/src/main/java/io/metersphere/plan/service/TestCaseSyncStatusService.java @@ -16,11 +16,14 @@ import io.metersphere.plan.enums.FunctionCaseExecResult; import io.metersphere.plan.enums.TestCaseReleevanceType; import io.metersphere.plan.utils.TestCaseSyncStatusUtil; import io.metersphere.service.BaseUserService; +import io.metersphere.service.FunctionCaseExecutionInfoService; import io.metersphere.utils.BatchProcessingUtil; +import io.metersphere.utils.LoggerUtil; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -49,6 +52,8 @@ public class TestCaseSyncStatusService { private BaseUserService baseUserService; @Resource private TestCaseCommentMapper testCaseCommentMapper; + @Resource + private FunctionCaseExecutionInfoService functionCaseExecutionInfoService; //通过自动化用例的状态,获取最新的功能用例状态。 public void getTestCaseStatusByTestPlanExecuteOver(List testPlanCaseList, List apiAllCases, List scenarioAllCases, List loadAllCases, List uiAllCases) { @@ -100,64 +105,72 @@ public class TestCaseSyncStatusService { } if (MapUtils.isNotEmpty(successCaseMap)) { extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(successCaseMap.keySet()), FunctionCaseExecResult.SUCCESS.toString()); + functionCaseExecutionInfoService.insertExecutionInfoByIdList(new ArrayList<>(successCaseMap.keySet()), FunctionCaseExecResult.SUCCESS.toString()); } if (MapUtils.isNotEmpty(errorCaseMap)) { extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(errorCaseMap.keySet()), FunctionCaseExecResult.ERROR.toString()); + functionCaseExecutionInfoService.insertExecutionInfoByIdList(new ArrayList<>(errorCaseMap.keySet()), FunctionCaseExecResult.ERROR.toString()); } if (MapUtils.isNotEmpty(blockingCaseMap)) { extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(blockingCaseMap.keySet()), FunctionCaseExecResult.BLOCKING.toString()); + functionCaseExecutionInfoService.insertExecutionInfoByIdList(new ArrayList<>(blockingCaseMap.keySet()), FunctionCaseExecResult.BLOCKING.toString()); this.addTestCaseComment(operator, testPlanName, blockingCaseMap, FunctionCaseExecResult.BLOCKING.toString()); } } } - + @Async public void updateFunctionCaseStatusByAutomationCaseId(String automationCaseId, String testPlanId, String triggerCaseRunResult) { - TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); - if (testPlan != null && testPlan.getAutomaticStatusUpdate()) { - HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(testPlan.getCreator())); + try { + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); + if (testPlan != null && testPlan.getAutomaticStatusUpdate()) { + HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(testPlan.getCreator())); - Set testCaseIdSet = new HashSet<>(); - List testPlanTestCaseList = extTestPlanTestCaseMapper.selectByAutomationCaseIdAndTestPlanId(automationCaseId, testPlanId); - testPlanTestCaseList.forEach(item -> testCaseIdSet.add(item.getCaseId())); + Set testCaseIdSet = new HashSet<>(); + List testPlanTestCaseList = extTestPlanTestCaseMapper.selectByAutomationCaseIdAndTestPlanId(automationCaseId, testPlanId); + testPlanTestCaseList.forEach(item -> testCaseIdSet.add(item.getCaseId())); - if (CollectionUtils.isNotEmpty(testCaseIdSet)) { - TestCaseTestExample testCaseTestExample = new TestCaseTestExample(); - testCaseTestExample.createCriteria().andTestCaseIdIn(new ArrayList<>(testCaseIdSet)); - List testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample); - Map> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId)); + if (CollectionUtils.isNotEmpty(testCaseIdSet)) { + TestCaseTestExample testCaseTestExample = new TestCaseTestExample(); + testCaseTestExample.createCriteria().andTestCaseIdIn(new ArrayList<>(testCaseIdSet)); + List testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample); + Map> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId)); - for (Map.Entry> entry : testCaseTestMap.entrySet()) { - TestCaseRelevanceCasesRequest request = this.generateRelevanceCasesRequest(testPlanId, entry.getKey(), entry.getValue()); - CaseExecResult priorityResult = TestCaseSyncStatusUtil.getTestCaseExecResultByRelevance(request.getTestCaseTestList(), - request.getApiAllCaseMap(), request.getScenarioAllCaseMap(), request.getLoadAllCaseMap(), request.getUiAllCaseMap()); - if (priorityResult == null) { - continue; - } + for (Map.Entry> entry : testCaseTestMap.entrySet()) { + TestCaseRelevanceCasesRequest request = this.generateRelevanceCasesRequest(testPlanId, entry.getKey(), entry.getValue()); + CaseExecResult priorityResult = TestCaseSyncStatusUtil.getTestCaseExecResultByRelevance(request.getTestCaseTestList(), + request.getApiAllCaseMap(), request.getScenarioAllCaseMap(), request.getLoadAllCaseMap(), request.getUiAllCaseMap()); + if (priorityResult == null) { + continue; + } - String automationCaseResult = null; - if (priorityResult != null && StringUtils.isNotEmpty(priorityResult.getExecResult())) { - if (StringUtils.equalsIgnoreCase(ApiReportStatus.ERROR.name(), priorityResult.getExecResult())) { - automationCaseResult = ApiReportStatus.ERROR.name(); - priorityResult.setExecResult(FunctionCaseExecResult.ERROR.toString()); - } else if (StringUtils.equalsIgnoreCase(ApiReportStatus.FAKE_ERROR.name(), priorityResult.getExecResult())) { - automationCaseResult = ApiReportStatus.FAKE_ERROR.name(); - priorityResult.setExecResult(FunctionCaseExecResult.BLOCKING.toString()); - } else if (StringUtils.equalsIgnoreCase(ApiReportStatus.SUCCESS.name(), priorityResult.getExecResult())) { - priorityResult.setExecResult(FunctionCaseExecResult.SUCCESS.toString()); + String automationCaseResult = null; + if (priorityResult != null && StringUtils.isNotEmpty(priorityResult.getExecResult())) { + if (StringUtils.equalsIgnoreCase(ApiReportStatus.ERROR.name(), priorityResult.getExecResult())) { + automationCaseResult = ApiReportStatus.ERROR.name(); + priorityResult.setExecResult(FunctionCaseExecResult.ERROR.toString()); + } else if (StringUtils.equalsIgnoreCase(ApiReportStatus.FAKE_ERROR.name(), priorityResult.getExecResult())) { + automationCaseResult = ApiReportStatus.FAKE_ERROR.name(); + priorityResult.setExecResult(FunctionCaseExecResult.BLOCKING.toString()); + } else if (StringUtils.equalsIgnoreCase(ApiReportStatus.SUCCESS.name(), priorityResult.getExecResult())) { + priorityResult.setExecResult(FunctionCaseExecResult.SUCCESS.toString()); + } + } + + //通过 triggerCaseRunResult(触发操作的用例的执行结果) 进行判断,会不会直接影响最终结果。如果是,在改变功能用例状态时也要增加一条评论。 + extTestPlanTestCaseMapper.updateExecResultByTestCaseIdAndTestPlanId(entry.getKey(), testPlanId, priorityResult.getExecResult()); + //记录功能用例执行信息 + functionCaseExecutionInfoService.insertExecutionInfoByCaseIdAndPlanId(entry.getKey(), testPlanId, priorityResult.getExecResult()); + if (StringUtils.equalsIgnoreCase(triggerCaseRunResult, automationCaseResult) && !StringUtils.equalsIgnoreCase(triggerCaseRunResult, ApiReportStatus.SUCCESS.name())) { + this.addTestCaseComment(testPlan.getCreator(), testPlan.getName(), entry.getKey(), priorityResult.getCaseName(), FunctionCaseExecResult.BLOCKING.toString()); } } - - //通过 triggerCaseRunResult(触发操作的用例的执行结果) 进行判断,会不会直接影响最终结果。如果是,在改变功能用例状态时也要增加一条评论。 - extTestPlanTestCaseMapper.updateExecResultByTestCaseIdAndTestPlanId(entry.getKey(), testPlanId, priorityResult.getExecResult()); - if (StringUtils.equalsIgnoreCase(triggerCaseRunResult, automationCaseResult) && !StringUtils.equalsIgnoreCase(triggerCaseRunResult, ApiReportStatus.SUCCESS.name())) { - this.addTestCaseComment(testPlan.getCreator(), testPlan.getName(), entry.getKey(), priorityResult.getCaseName(), FunctionCaseExecResult.BLOCKING.toString()); - } } + HttpHeaderUtils.clearUser(); } - - HttpHeaderUtils.clearUser(); + } catch (Exception e) { + LoggerUtil.error("更新功能用例状态出错!", e); } } diff --git a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java index 349928e8af..6087dd2538 100644 --- a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java +++ b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java @@ -14,6 +14,7 @@ import io.metersphere.excel.constants.TestPlanTestCaseStatus; import io.metersphere.i18n.Translator; import io.metersphere.log.vo.OperatingLogDetails; import io.metersphere.plan.constant.ApiReportStatus; +import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.dto.*; import io.metersphere.plan.request.QueryTestPlanRequest; import io.metersphere.plan.request.TestPlanReportSaveRequest; diff --git a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanService.java b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanService.java index 507c1adfcd..076bb5cd2a 100644 --- a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanService.java +++ b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanService.java @@ -22,6 +22,7 @@ import io.metersphere.log.utils.ReflexObjectUtil; import io.metersphere.log.vo.DetailColumn; import io.metersphere.log.vo.OperatingLogDetails; import io.metersphere.log.vo.track.TestPlanReference; +import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.dto.*; import io.metersphere.plan.job.TestPlanTestJob; import io.metersphere.plan.request.AddTestPlanRequest; diff --git a/test-track/backend/src/main/java/io/metersphere/service/FunctionCaseExecutionInfoService.java b/test-track/backend/src/main/java/io/metersphere/service/FunctionCaseExecutionInfoService.java index dec91b2fe2..46f2d612b1 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/FunctionCaseExecutionInfoService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/FunctionCaseExecutionInfoService.java @@ -3,12 +3,13 @@ package io.metersphere.service; import io.metersphere.base.domain.FunctionCaseExecutionInfo; import io.metersphere.base.domain.FunctionCaseExecutionInfoExample; import io.metersphere.base.mapper.FunctionCaseExecutionInfoMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper; +import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import jakarta.annotation.Resource; import java.util.List; import java.util.UUID; @@ -17,6 +18,8 @@ import java.util.UUID; public class FunctionCaseExecutionInfoService { @Resource private FunctionCaseExecutionInfoMapper functionCaseExecutionInfoMapper; + @Resource + private ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper; public void insertExecutionInfo(String caseId, String result) { if (!StringUtils.isAnyEmpty(caseId, result)) { @@ -29,6 +32,23 @@ public class FunctionCaseExecutionInfoService { } } + public void insertExecutionInfoByIdList(List caseIdList, String result) { + if (CollectionUtils.isNotEmpty(caseIdList)) { + caseIdList.forEach(item -> { + this.insertExecutionInfo(item, result); + }); + } + } + + public void insertExecutionInfoByCaseIdAndPlanId(String caseId, String planId, String result) { + if (!StringUtils.isAnyEmpty(caseId, planId, result)) { + List testPlanTestCaseIdList = extTestPlanTestCaseMapper.selectIdByTestCaseIdAndTestPlanId(caseId, planId); + testPlanTestCaseIdList.forEach(item -> { + this.insertExecutionInfo(item, result); + }); + } + } + public void deleteBySourceIdList(List ids) { if (CollectionUtils.isNotEmpty(ids)) { FunctionCaseExecutionInfoExample example = new FunctionCaseExecutionInfoExample(); diff --git a/test-track/backend/src/main/java/io/metersphere/service/TrackService.java b/test-track/backend/src/main/java/io/metersphere/service/TrackService.java index 3c2585cb94..ad1f746c1f 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/TrackService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/TrackService.java @@ -3,18 +3,16 @@ package io.metersphere.service; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.metersphere.base.domain.CustomField; -import io.metersphere.base.domain.TestPlan; -import io.metersphere.base.domain.TestPlanExample; import io.metersphere.base.mapper.TestPlanMapper; -import io.metersphere.base.mapper.ext.ExtIssuesMapper; -import io.metersphere.base.mapper.ext.ExtTestCaseMapper; +import io.metersphere.base.mapper.ext.*; import io.metersphere.commons.constants.CustomFieldScene; import io.metersphere.commons.utils.DateUtils; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.constants.IssueStatus; import io.metersphere.constants.SystemCustomField; import io.metersphere.dto.BugStatistics; -import io.metersphere.dto.TestPlanBugCount; +import io.metersphere.dto.ExecutedCaseInfoResult; import io.metersphere.dto.TestPlanDTOWithMetric; import io.metersphere.dto.TrackCountResult; import io.metersphere.i18n.Translator; @@ -23,13 +21,13 @@ import io.metersphere.plan.service.TestPlanService; import io.metersphere.request.testcase.TrackCount; import io.metersphere.xpack.track.dto.IssuesDao; import io.metersphere.xpack.track.dto.request.IssuesRequest; +import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import jakarta.annotation.Resource; import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.*; @@ -49,7 +47,12 @@ public class TrackService { private BaseCustomFieldService baseCustomFieldService; @Resource private TestPlanService testPlanService; - + @Resource + private ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper; + @Resource + private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; + @Resource + private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper; @Resource private ExtIssuesMapper extIssuesMapper; @@ -304,4 +307,40 @@ public class TrackService { testPlanService.calcTestPlanRate(testPlan); return testPlan.getPassRate(); } + + public List findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(String projectId, String versionId, int limitNumber) { + + //获取7天之前的日期 + Date startDay = DateUtils.dateSum(new Date(), -6); + //将日期转化为 00:00:00 的时间戳 + Date startTime = null; + try { + startTime = DateUtils.getDayStartTime(startDay); + } catch (Exception e) { + LogUtil.error("解析日期出错!", e); + } + + if (startTime == null) { + return new ArrayList<>(0); + } else { + List returnList = new ArrayList<>(limitNumber); + ArrayList allCaseExecList = new ArrayList<>(); + allCaseExecList.addAll(extTestPlanTestCaseMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, versionId, startTime.getTime(), limitNumber)); + allCaseExecList.addAll(extTestPlanApiCaseMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, versionId, startTime.getTime(), limitNumber)); + allCaseExecList.addAll(extTestPlanScenarioCaseMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, versionId, startTime.getTime(), limitNumber)); + + if (CollectionUtils.isNotEmpty(allCaseExecList)) { + allCaseExecList.sort(Comparator.comparing(ExecutedCaseInfoResult::getFailureTimes).reversed()); + for (int i = 0; i < allCaseExecList.size(); i++) { + if (i < limitNumber) { + ExecutedCaseInfoResult item = allCaseExecList.get(i); + returnList.add(item); + } else { + break; + } + } + } + return returnList; + } + } } diff --git a/test-track/backend/src/main/java/io/metersphere/utils/NamedThreadFactory.java b/test-track/backend/src/main/java/io/metersphere/utils/NamedThreadFactory.java new file mode 100644 index 0000000000..71f6476721 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/utils/NamedThreadFactory.java @@ -0,0 +1,20 @@ +package io.metersphere.utils; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class NamedThreadFactory implements ThreadFactory { + private static AtomicInteger tag = new AtomicInteger(0); + private String name; + + public NamedThreadFactory(String name) { + this.name = name; + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setName(this.name + ":" + tag.getAndIncrement()); + return thread; + } +} diff --git a/test-track/frontend/src/api/remote/api/api-home.js b/test-track/frontend/src/api/remote/api/api-home.js deleted file mode 100644 index 6925515ac1..0000000000 --- a/test-track/frontend/src/api/remote/api/api-home.js +++ /dev/null @@ -1,16 +0,0 @@ -import { get } from "@/business/utils/sdk-utils"; - -const BASE_URL = "/home/"; - -export function homeTestPlanFailureCaseGet( - projectId, - selectFunctionCase, - limitNumber, - currentPage, - pageSize -) { - return get( - BASE_URL + - `failure/case/about/plan/${projectId}/default/${selectFunctionCase}/${limitNumber}/${currentPage}/${pageSize}` - ); -} diff --git a/test-track/frontend/src/api/track.js b/test-track/frontend/src/api/track.js index bea09e17d0..6e67c1b389 100644 --- a/test-track/frontend/src/api/track.js +++ b/test-track/frontend/src/api/track.js @@ -1,9 +1,20 @@ -import {post, get} from "metersphere-frontend/src/plugins/request"; +import { get, post } from "metersphere-frontend/src/plugins/request"; export function getTrackCount(selectProjectId) { return get("/track/count/" + selectProjectId); } +export function homeTestPlanFailureCaseGet( + projectId, + limitNumber, + currentPage, + pageSize +) { + return get( + `/track/failure/case/about/plan/${projectId}/default/${limitNumber}/${currentPage}/${pageSize}` + ); +} + export function getTrackRelevanceCount(selectProjectId) { return get("/track/relevance/count/" + selectProjectId); } @@ -12,8 +23,21 @@ export function getTrackCaseBar(selectProjectId) { return get("/track/case/bar/" + selectProjectId); } -export function getTrackRunningTask(selectProjectId, currentPage, pageSize, param) { - return post("/task/center/runningTask/" + selectProjectId + "/" + currentPage + "/" + pageSize, param); +export function getTrackRunningTask( + selectProjectId, + currentPage, + pageSize, + param +) { + return post( + "/task/center/runningTask/" + + selectProjectId + + "/" + + currentPage + + "/" + + pageSize, + param + ); } export function getTrackBugCount(selectProjectId) { @@ -21,9 +45,10 @@ export function getTrackBugCount(selectProjectId) { } export function formatNumber(param) { - let num = (param || 0).toString(), result = ''; + let num = (param || 0).toString(), + result = ""; while (num.length > 3) { - result = ',' + num.slice(-3) + result; + result = "," + num.slice(-3) + result; num = num.slice(0, num.length - 3); } if (num) { @@ -31,4 +56,3 @@ export function formatNumber(param) { } return result; } - diff --git a/test-track/frontend/src/business/home/TrackHome.vue b/test-track/frontend/src/business/home/TrackHome.vue index ea3ff80d90..1fad667930 100644 --- a/test-track/frontend/src/business/home/TrackHome.vue +++ b/test-track/frontend/src/business/home/TrackHome.vue @@ -23,10 +23,7 @@ - + diff --git a/test-track/frontend/src/business/home/components/FailureTestCaseList.vue b/test-track/frontend/src/business/home/components/FailureTestCaseList.vue index 306463ded6..31b4bf8768 100644 --- a/test-track/frontend/src/business/home/components/FailureTestCaseList.vue +++ b/test-track/frontend/src/business/home/components/FailureTestCaseList.vue @@ -1,32 +1,64 @@