fix(测试跟踪): 优化测试跟踪首页失败用例top10的查询方式和逻辑
--bug=1020830 --user=宋天阳 [测试跟踪]github#205762.4.1msctl uninstall+msctl reload服务全部healthy登录测试跟踪首页报错 https://www.tapd.cn/55049933/s/1348104
This commit is contained in:
parent
ee0a5f1a49
commit
fba936030c
|
@ -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<ExecutedCaseInfoResult> 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);
|
||||
|
|
|
@ -39,105 +39,6 @@
|
|||
WHERE integrated_report_id = #{0}
|
||||
</select>
|
||||
|
||||
<select id="findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber"
|
||||
resultType="io.metersphere.api.dto.datacount.ExecutedCaseInfoResult">
|
||||
SELECT * FROM (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT testCase.testPlanCaseID AS testPlanCaseID,
|
||||
testCase.id AS id,
|
||||
testCase.testCaseName AS caseName,
|
||||
testCase.testPlanName AS testPlan,
|
||||
testCase.testPlanId AS testPlanId,
|
||||
caseErrorCountData.dataCountNumber AS failureTimes,
|
||||
'apiCase' AS caseType
|
||||
FROM (SELECT testPlanCase.id AS testPlanCaseID,
|
||||
apiCase.id AS id,
|
||||
apiCase.`name` AS testCaseName,
|
||||
testPlan.id AS testPlanId,
|
||||
testPlan.`name` AS testPlanName
|
||||
FROM api_test_case apiCase
|
||||
INNER JOIN test_plan_api_case testPlanCase ON testPlanCase.api_case_id = apiCase.id
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanCase.test_plan_id
|
||||
WHERE (
|
||||
(apiCase.`status` IS NULL OR apiCase.`status` != 'Trash')
|
||||
AND apiCase.project_id = #{projectId}
|
||||
)) testCase
|
||||
INNER JOIN (SELECT executionInfo.source_id AS sourceId,
|
||||
COUNT(executionInfo.id) AS dataCountNumber
|
||||
FROM api_case_execution_info executionInfo
|
||||
INNER JOIN test_plan_api_case testPlanCase
|
||||
ON executionInfo.source_id = testPlanCase.id
|
||||
WHERE executionInfo.`result` = 'ERROR'
|
||||
AND executionInfo.create_time > #{startTimestamp}
|
||||
<if test="versionId != null">
|
||||
AND executionInfo.version = #{versionId}
|
||||
</if>
|
||||
GROUP BY source_id) caseErrorCountData
|
||||
ON caseErrorCountData.sourceId = testCase.testPlanCaseID
|
||||
UNION
|
||||
|
||||
SELECT scene.id AS testCaseID,
|
||||
scene.id AS id,
|
||||
scene.`name` AS caseName,
|
||||
apiScene.testPlanName AS testPlan,
|
||||
apiScene.testPlanId AS testPlanId,
|
||||
count(executionInfo.id) AS failureTimes,
|
||||
'scenario' AS caseType
|
||||
FROM scenario_execution_info executionInfo
|
||||
INNER JOIN (SELECT testPlanScenario.id,
|
||||
testPlanScenario.api_scenario_id,
|
||||
testPlan.id AS testPlanId,
|
||||
testPlan.`name` AS testPlanName
|
||||
FROM test_plan_api_scenario testPlanScenario
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanScenario.test_plan_id) apiScene
|
||||
ON apiScene.id = executionInfo.source_id
|
||||
INNER JOIN api_scenario scene ON scene.id = apiScene.api_scenario_id
|
||||
|
||||
WHERE scene.project_id = #{projectId}
|
||||
AND scene.`status` != 'Trash'
|
||||
AND ( executionInfo.result = 'ERROR' )
|
||||
AND executionInfo.create_time >= #{startTimestamp}
|
||||
<if test="versionId != null">
|
||||
AND executionInfo.version = #{versionId}
|
||||
</if>
|
||||
GROUP BY
|
||||
scene.id,apiScene.testPlanId
|
||||
<if test="selectFunctionCase == true">
|
||||
UNION
|
||||
SELECT
|
||||
testCase.id AS testCaseID,
|
||||
testCase.id AS id,
|
||||
testCase.`name` AS caseName,
|
||||
testCasePlan.testPlanName AS testPlan,
|
||||
testCasePlan.testPlanId AS testPlanId,
|
||||
count( executionInfo.id ) AS failureTimes,
|
||||
'testCase' AS caseType
|
||||
FROM
|
||||
function_case_execution_info executionInfo
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
testPlanTestCase.id,
|
||||
testPlanTestCase.case_id,
|
||||
testPlan.id AS testPlanId,
|
||||
testPlan.`name` AS testPlanName
|
||||
FROM
|
||||
test_plan_test_case testPlanTestCase
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanTestCase.plan_id
|
||||
) testCasePlan ON testCasePlan.id = executionInfo.source_id
|
||||
INNER JOIN test_case testCase ON testCase.id = testCasePlan.case_id
|
||||
WHERE
|
||||
testCase.project_id = #{projectId}
|
||||
AND testCase.`status` != 'Trash'
|
||||
AND ( executionInfo.result = 'Failure' )
|
||||
AND executionInfo.create_time >= #{startTimestamp}
|
||||
GROUP BY
|
||||
testCase.id
|
||||
</if>
|
||||
) showTable
|
||||
ORDER BY showTable.failureTimes DESC LIMIT ${limitNumber}
|
||||
) pageTable
|
||||
</select>
|
||||
<select id="selectExecResult" resultType="java.lang.String">
|
||||
select ader.status
|
||||
from api_definition_exec_result ader
|
||||
|
|
|
@ -6,11 +6,9 @@ import io.metersphere.api.dto.DubboProvider;
|
|||
import io.metersphere.api.dto.JmxInfoDTO;
|
||||
import io.metersphere.api.dto.RegistryCenter;
|
||||
import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
||||
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
|
||||
import io.metersphere.api.dto.datacount.response.ApiDataCountDTO;
|
||||
import io.metersphere.api.dto.datacount.response.CoveredDTO;
|
||||
import io.metersphere.api.dto.datacount.response.ExecuteResultCountDTO;
|
||||
import io.metersphere.api.dto.datacount.response.ExecutedCaseInfoDTO;
|
||||
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||
import io.metersphere.api.dto.export.ScenarioToPerformanceInfoDTO;
|
||||
import io.metersphere.base.domain.ApiDefinition;
|
||||
|
@ -32,7 +30,6 @@ import org.springframework.web.bind.annotation.*;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -251,31 +248,6 @@ public class ApiHomeController {
|
|||
return PageUtils.setPageInfo(page, resultList);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/failure/case/about/plan/{projectId}/{versionId}/{selectFunctionCase}/{limitNumber}/{goPage}/{pageSize}")
|
||||
public Pager<List<ExecutedCaseInfoDTO>> failureCaseAboutTestPlan(@PathVariable String projectId, @PathVariable String versionId, @PathVariable boolean selectFunctionCase,
|
||||
@PathVariable int limitNumber, @PathVariable int goPage, @PathVariable int pageSize) {
|
||||
versionId = this.initializationVersionId(versionId);
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
List<ExecutedCaseInfoResult> selectDataList = apiDefinitionExecResultService.findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(projectId, versionId, selectFunctionCase, limitNumber);
|
||||
List<ExecutedCaseInfoDTO> 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);
|
||||
}
|
||||
|
||||
//初始化版本ID
|
||||
private String initializationVersionId(String versionId) {
|
||||
if (StringUtils.equalsIgnoreCase(versionId, "default")) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.metersphere.service.definition;
|
|||
|
||||
import io.metersphere.api.dto.QueryAPIReportRequest;
|
||||
import io.metersphere.api.dto.RequestResultExpandDTO;
|
||||
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
||||
|
@ -355,39 +354,6 @@ public class ApiDefinitionExecResultService {
|
|||
}
|
||||
}
|
||||
|
||||
public List<ExecutedCaseInfoResult> findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(String projectId, String versionId, boolean selectFunctionCase, 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<ExecutedCaseInfoResult> list = extApiDefinitionExecResultMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, versionId, selectFunctionCase, startTime.getTime(), limitNumber);
|
||||
|
||||
List<ExecutedCaseInfoResult> returnList = new ArrayList<>(limitNumber);
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i < limitNumber) {
|
||||
//开始遍历查询TestPlan信息 --> 提供前台做超链接
|
||||
ExecutedCaseInfoResult item = list.get(i);
|
||||
returnList.add(item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
|
||||
private ApiDefinitionExecResult editResult(RequestResult item, String reportId, String console, String type, String testId, ApiDefinitionExecResultMapper batchMapper) {
|
||||
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
|
||||
ApiDefinitionExecResultWithBLOBs saveResult = new ApiDefinitionExecResultWithBLOBs();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.commons.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;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext;
|
|||
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||
import io.metersphere.base.domain.TestPlanApiCase;
|
||||
import io.metersphere.dto.ExecutedCaseInfoResult;
|
||||
import io.metersphere.plan.dto.CaseExecResult;
|
||||
import io.metersphere.plan.dto.TestPlanApiCaseInfoDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
@ -17,6 +18,8 @@ public interface ExtTestPlanApiCaseMapper {
|
|||
@Select("SELECT id,test_plan_id,api_case_id,status FROM test_plan_api_case WHERE id = #{0} ")
|
||||
TestPlanApiCase selectBaseInfoById(String testId);
|
||||
|
||||
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
|
||||
|
||||
List<ApiDefinitionExecResult> selectReportStatusByReportIds(@Param("ids") List<String> apiReportIdList);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,4 +28,42 @@
|
|||
</foreach>
|
||||
</select>
|
||||
|
||||
|
||||
<select id="findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber"
|
||||
resultType="io.metersphere.dto.ExecutedCaseInfoResult">
|
||||
SELECT * FROM (
|
||||
SELECT testCase.testPlanCaseID AS testPlanCaseID,
|
||||
testCase.id AS id,
|
||||
testCase.testCaseName AS caseName,
|
||||
testCase.testPlanName AS testPlan,
|
||||
testCase.testPlanId AS testPlanId,
|
||||
caseErrorCountData.dataCountNumber AS failureTimes,
|
||||
'apiCase' AS caseType
|
||||
FROM (
|
||||
SELECT testPlanCase.id AS testPlanCaseID,
|
||||
apiCase.id AS id,
|
||||
apiCase.`name` AS testCaseName,
|
||||
testPlan.id AS testPlanId,
|
||||
testPlan.`name` AS testPlanName
|
||||
FROM api_test_case apiCase
|
||||
INNER JOIN test_plan_api_case testPlanCase ON testPlanCase.api_case_id = apiCase.id
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanCase.test_plan_id
|
||||
WHERE
|
||||
(apiCase.`status` IS NULL OR apiCase.`status` != 'Trash')
|
||||
AND apiCase.project_id = #{projectId}
|
||||
) testCase
|
||||
INNER JOIN (SELECT executionInfo.source_id AS sourceId,
|
||||
COUNT(executionInfo.id) AS dataCountNumber
|
||||
FROM api_case_execution_info executionInfo
|
||||
INNER JOIN test_plan_api_case testPlanCase ON executionInfo.source_id = testPlanCase.id
|
||||
WHERE executionInfo.`result` = 'ERROR'
|
||||
AND executionInfo.create_time > #{startTimestamp}
|
||||
<if test="versionId != null">
|
||||
AND executionInfo.version = #{versionId}
|
||||
</if>
|
||||
GROUP BY source_id
|
||||
) caseErrorCountData
|
||||
ON caseErrorCountData.sourceId = testCase.testPlanCaseID
|
||||
) showTable ORDER BY showTable.failureTimes DESC LIMIT ${limitNumber}
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
@ -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<ApiScenarioReport> selectReportStatusByReportIds(@Param("ids") List<String> scenarioReportIdList);
|
||||
|
||||
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
|
||||
}
|
||||
|
|
|
@ -46,4 +46,32 @@
|
|||
#{value}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber"
|
||||
resultType="io.metersphere.dto.ExecutedCaseInfoResult">
|
||||
SELECT * FROM (
|
||||
SELECT scene.id AS testCaseID,
|
||||
scene.id AS id,
|
||||
scene.`name` AS caseName,
|
||||
apiScene.testPlanName AS testPlan,
|
||||
apiScene.testPlanId AS testPlanId,
|
||||
count(executionInfo.id) AS failureTimes,
|
||||
'scenario' AS caseType
|
||||
FROM scenario_execution_info executionInfo
|
||||
INNER JOIN (
|
||||
SELECT testPlanScenario.id, testPlanScenario.api_scenario_id, testPlan.id AS testPlanId, testPlan.`name` AS testPlanName
|
||||
FROM test_plan_api_scenario testPlanScenario
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanScenario.test_plan_id
|
||||
) apiScene ON apiScene.id = executionInfo.source_id
|
||||
INNER JOIN api_scenario scene ON scene.id = apiScene.api_scenario_id
|
||||
WHERE scene.project_id = #{projectId}
|
||||
AND scene.`status` != 'Trash'
|
||||
AND ( executionInfo.result = 'ERROR' )
|
||||
AND executionInfo.create_time >= #{startTimestamp}
|
||||
<if test="versionId != null">
|
||||
AND executionInfo.version = #{versionId}
|
||||
</if>
|
||||
GROUP BY scene.id,apiScene.testPlanId
|
||||
) showTable ORDER BY showTable.failureTimes DESC LIMIT ${limitNumber}
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
@ -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<TestPlanTestCase> selectByAutomationCaseIdAndTestPlanId(@Param("automationCaseId") String automationCaseId, @Param("test_plan_id") String testPlanId);
|
||||
|
||||
List<ExecutedCaseInfoResult> 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<String> selectIdByTestCaseIdAndTestPlanId(@Param("caseId") String caseId, @Param("planId") String planId);
|
||||
}
|
||||
|
|
|
@ -666,5 +666,36 @@
|
|||
UPDATE test_plan_test_case SET status = #{execResult}
|
||||
WHERE case_id = #{testCaseId} AND plan_id = #{testPlanId}
|
||||
</update>
|
||||
|
||||
<select id="findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber"
|
||||
resultType="io.metersphere.dto.ExecutedCaseInfoResult">
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
testCase.id AS testCaseID,
|
||||
testCase.id AS id,
|
||||
testCase.`name` AS caseName,
|
||||
testCasePlan.testPlanName AS testPlan,
|
||||
testCasePlan.testPlanId AS testPlanId,
|
||||
count( executionInfo.id ) AS failureTimes,
|
||||
'testCase' AS caseType
|
||||
FROM
|
||||
function_case_execution_info executionInfo
|
||||
INNER JOIN (
|
||||
SELECT
|
||||
testPlanTestCase.id,
|
||||
testPlanTestCase.case_id,
|
||||
testPlan.id AS testPlanId,
|
||||
testPlan.`name` AS testPlanName
|
||||
FROM
|
||||
test_plan_test_case testPlanTestCase
|
||||
INNER JOIN test_plan testPlan ON testPlan.id = testPlanTestCase.plan_id
|
||||
) testCasePlan ON testCasePlan.id = executionInfo.source_id
|
||||
INNER JOIN test_case testCase ON testCase.id = testCasePlan.case_id
|
||||
WHERE
|
||||
testCase.project_id = #{projectId}
|
||||
AND testCase.`status` != 'Trash'
|
||||
AND ( executionInfo.result = 'Failure' )
|
||||
AND executionInfo.create_time >= #{startTimestamp}
|
||||
GROUP BY testCase.id
|
||||
) showTable ORDER BY showTable.failureTimes DESC LIMIT ${limitNumber}
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
@ -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<ConcurrentMessageListenerContainer<String, Object>> batchFactory() {
|
||||
ConcurrentKafkaListenerContainerFactory<String, Object> 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<String, Object> consumerConfigs() {
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
}
|
|
@ -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<List<ExecutedCaseInfoDTO>> failureCaseAboutTestPlan(@PathVariable String projectId, @PathVariable String versionId,
|
||||
@PathVariable int limitNumber, @PathVariable int goPage, @PathVariable int pageSize) {
|
||||
if (StringUtils.equalsIgnoreCase(versionId, "default")) {
|
||||
versionId = null;
|
||||
}
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
List<ExecutedCaseInfoResult> selectDataList = trackService.findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(projectId, versionId, limitNumber);
|
||||
List<ExecutedCaseInfoDTO> 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();
|
||||
|
|
|
@ -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<TestPlanDTO> testPlanDTOList;
|
||||
}
|
|
@ -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<TestPlanDTO> testPlanDTOList;
|
||||
}
|
|
@ -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<String> projectIds;
|
||||
private List<String> principals;
|
||||
private List<String> follows;
|
||||
/**
|
||||
* 定时任务ID
|
||||
*/
|
||||
private String scheduleId;
|
||||
/**
|
||||
* 定时任务是否开启
|
||||
*/
|
||||
private boolean scheduleOpen;
|
||||
/**
|
||||
* 定时任务状态
|
||||
*/
|
||||
private String scheduleStatus;
|
||||
/**
|
||||
* 定时任务规则
|
||||
*/
|
||||
private String scheduleCorn;
|
||||
/**
|
||||
* 定时任务下一次执行时间
|
||||
*/
|
||||
private Long scheduleExecuteTime;
|
||||
private String workspaceName;
|
||||
}
|
|
@ -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<?, String> 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<ConsumerRecord<?, String>> 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<ApiExecutionQueue> 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<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<?, String> 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<ApiExecutionQueue> 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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PlanReportCaseDTO> testPlanCaseList, List<TestPlanApiDTO> apiAllCases, List<TestPlanScenarioDTO> scenarioAllCases, List<TestPlanLoadCaseDTO> loadAllCases, List<TestPlanUiScenarioDTO> 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<String> testCaseIdSet = new HashSet<>();
|
||||
List<TestPlanTestCase> testPlanTestCaseList = extTestPlanTestCaseMapper.selectByAutomationCaseIdAndTestPlanId(automationCaseId, testPlanId);
|
||||
testPlanTestCaseList.forEach(item -> testCaseIdSet.add(item.getCaseId()));
|
||||
Set<String> testCaseIdSet = new HashSet<>();
|
||||
List<TestPlanTestCase> 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<TestCaseTest> testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample);
|
||||
Map<String, List<TestCaseTest>> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId));
|
||||
if (CollectionUtils.isNotEmpty(testCaseIdSet)) {
|
||||
TestCaseTestExample testCaseTestExample = new TestCaseTestExample();
|
||||
testCaseTestExample.createCriteria().andTestCaseIdIn(new ArrayList<>(testCaseIdSet));
|
||||
List<TestCaseTest> testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample);
|
||||
Map<String, List<TestCaseTest>> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId));
|
||||
|
||||
for (Map.Entry<String, List<TestCaseTest>> 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<String, List<TestCaseTest>> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String> 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<String> testPlanTestCaseIdList = extTestPlanTestCaseMapper.selectIdByTestCaseIdAndTestPlanId(caseId, planId);
|
||||
testPlanTestCaseIdList.forEach(item -> {
|
||||
this.insertExecutionInfo(item, result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteBySourceIdList(List<String> ids) {
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
FunctionCaseExecutionInfoExample example = new FunctionCaseExecutionInfoExample();
|
||||
|
|
|
@ -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<ExecutedCaseInfoResult> 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<ExecutedCaseInfoResult> returnList = new ArrayList<>(limitNumber);
|
||||
ArrayList<ExecutedCaseInfoResult> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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}`
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,7 @@
|
|||
|
||||
<el-row style="margin-top: 16px">
|
||||
<el-col style="background-color: #ffffff">
|
||||
<ms-failure-test-case-list
|
||||
:select-function-case="true"
|
||||
@redirectPage="redirectPage"
|
||||
/>
|
||||
<ms-failure-test-case-list @redirectPage="redirectPage" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
|
|
@ -1,32 +1,64 @@
|
|||
<template>
|
||||
<div style="margin: 24px" class="failure-case-table">
|
||||
<span class="table-title">
|
||||
{{ $t('api_test.home_page.failed_case_list.title') }}
|
||||
{{ $t("api_test.home_page.failed_case_list.title") }}
|
||||
</span>
|
||||
<div style="margin-top: 16px" v-loading="loading" element-loading-background="#FFFFFF">
|
||||
<div v-show="loadError"
|
||||
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
|
||||
<img style="height: 100px;width: 100px;"
|
||||
src="/assets/module/figma/icon_load_error.svg"/>
|
||||
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
|
||||
<div
|
||||
style="margin-top: 16px"
|
||||
v-loading="loading"
|
||||
element-loading-background="#FFFFFF"
|
||||
>
|
||||
<div
|
||||
v-show="loadError"
|
||||
style="
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<img
|
||||
style="height: 100px; width: 100px"
|
||||
src="/assets/module/figma/icon_load_error.svg"
|
||||
/>
|
||||
<span class="addition-info-title" style="color: #646a73">{{
|
||||
$t("home.dashboard.public.load_error")
|
||||
}}</span>
|
||||
</div>
|
||||
<div v-show="!loadError">
|
||||
<el-table :data="tableData" class="adjust-table table-content"
|
||||
header-cell-class-name="home-table-cell" style="min-height: 228px">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
class="adjust-table table-content"
|
||||
header-cell-class-name="home-table-cell"
|
||||
style="min-height: 228px"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
:label="$t('home.case.index')"
|
||||
show-overflow-tooltip
|
||||
width="100px"/>
|
||||
width="100px"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="caseName"
|
||||
:label="$t('home.case.case_name')"
|
||||
min-width="200px">
|
||||
<template v-slot:default="{row}">
|
||||
<el-link style="color: #783887; width: 100%;" :underline="false" type="info" @click="redirect(row.caseType,row.id)"
|
||||
:disabled="(row.caseType === 'apiCase' && apiCaseReadOnly) || (row.caseType === 'scenario' && apiScenarioReadOnly) ||
|
||||
(row.caseType === 'load' && loadCaseReadOnly) || (row.caseType === 'testCase' && testCaseReadOnly)">
|
||||
min-width="200px"
|
||||
>
|
||||
<template v-slot:default="{ row }">
|
||||
<el-link
|
||||
style="color: #783887; width: 100%"
|
||||
:underline="false"
|
||||
type="info"
|
||||
@click="redirect(row.caseType, row.id)"
|
||||
:disabled="
|
||||
(row.caseType === 'apiCase' && apiCaseReadOnly) ||
|
||||
(row.caseType === 'scenario' && apiScenarioReadOnly) ||
|
||||
(row.caseType === 'load' && loadCaseReadOnly) ||
|
||||
(row.caseType === 'testCase' && testCaseReadOnly)
|
||||
"
|
||||
>
|
||||
{{ row.caseName }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
@ -37,18 +69,28 @@
|
|||
:label="$t('home.case.case_type')"
|
||||
show-overflow-tooltip
|
||||
column-key="caseType"
|
||||
width="150px">
|
||||
width="150px"
|
||||
>
|
||||
<template v-slot:default="scope">
|
||||
<basic-case-type-label :value="scope.row.caseType"></basic-case-type-label>
|
||||
<basic-case-type-label
|
||||
:value="scope.row.caseType"
|
||||
></basic-case-type-label>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="testPlan"
|
||||
:label="$t('home.case.test_plan')"
|
||||
width="300px">
|
||||
<template v-slot:default="{row}">
|
||||
<el-link style="color: #783887; width: 100%;" :underline="false" type="info" @click="redirect('testPlanEdit',row.testPlanId)" v-permission-disable="['PROJECT_TRACK_PLAN:READ']">
|
||||
width="300px"
|
||||
>
|
||||
<template v-slot:default="{ row }">
|
||||
<el-link
|
||||
style="color: #783887; width: 100%"
|
||||
:underline="false"
|
||||
type="info"
|
||||
@click="redirect('testPlanEdit', row.testPlanId)"
|
||||
v-permission-disable="['PROJECT_TRACK_PLAN:READ']"
|
||||
>
|
||||
{{ row.testPlan }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
@ -58,19 +100,38 @@
|
|||
prop="failureTimes"
|
||||
:label="$t('home.case.failure_times')"
|
||||
show-overflow-tooltip
|
||||
width="350px"/>
|
||||
width="350px"
|
||||
/>
|
||||
|
||||
<template #empty>
|
||||
<div
|
||||
style="width: 100%;height: 238px;display: flex;flex-direction: column;justify-content: center;align-items: center">
|
||||
<img style="height: 100px;width: 100px;margin-bottom: 8px"
|
||||
src="/assets/module/figma/icon_none.svg"/>
|
||||
<span class="addition-info-title">{{ $t("home.dashboard.public.no_data") }}</span>
|
||||
style="
|
||||
width: 100%;
|
||||
height: 238px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<img
|
||||
style="height: 100px; width: 100px; margin-bottom: 8px"
|
||||
src="/assets/module/figma/icon_none.svg"
|
||||
/>
|
||||
<span class="addition-info-title">{{
|
||||
$t("home.dashboard.public.no_data")
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table>
|
||||
<home-pagination v-if="tableData.length > 0" :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize" layout="prev, pager, next, sizes"
|
||||
:total="total"/>
|
||||
<home-pagination
|
||||
v-if="tableData.length > 0"
|
||||
:change="search"
|
||||
:current-page.sync="currentPage"
|
||||
:page-size.sync="pageSize"
|
||||
layout="prev, pager, next, sizes"
|
||||
:total="total"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,11 +139,11 @@
|
|||
|
||||
<script>
|
||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
||||
import {homeTestPlanFailureCaseGet} from "@/api/remote/api/api-home";
|
||||
import {hasPermission} from "@/business/utils/sdk-utils";
|
||||
import { getCurrentProjectID } from "metersphere-frontend/src/utils/token";
|
||||
import { hasPermission } from "@/business/utils/sdk-utils";
|
||||
import HomePagination from "@/business/home/components/pagination/HomePagination";
|
||||
import BasicCaseTypeLabel from "metersphere-frontend/src/components/BasicCaseTypeLabel";
|
||||
import { homeTestPlanFailureCaseGet } from "@/api/track";
|
||||
|
||||
export default {
|
||||
name: "MsFailureTestCaseList",
|
||||
|
@ -101,10 +162,7 @@ export default {
|
|||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
selectFunctionCase: Boolean,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
projectId() {
|
||||
|
@ -116,13 +174,19 @@ export default {
|
|||
if (this.projectId) {
|
||||
this.loading = true;
|
||||
this.loadError = false;
|
||||
homeTestPlanFailureCaseGet(this.projectId, this.selectFunctionCase, 10, this.currentPage, this.pageSize)
|
||||
homeTestPlanFailureCaseGet(
|
||||
this.projectId,
|
||||
10,
|
||||
this.currentPage,
|
||||
this.pageSize
|
||||
)
|
||||
.then((r) => {
|
||||
this.loading = false;
|
||||
this.loadError = false;
|
||||
this.total = r.data.itemCount;
|
||||
this.tableData = r.data.listObject;
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
this.loadError = true;
|
||||
});
|
||||
|
@ -131,28 +195,33 @@ export default {
|
|||
redirect(pageType, param) {
|
||||
switch (pageType) {
|
||||
case "testPlanEdit":
|
||||
this.$emit('redirectPage', 'testPlanEdit', null, param);
|
||||
this.$emit("redirectPage", "testPlanEdit", null, param);
|
||||
break;
|
||||
case "apiCase":
|
||||
this.$emit('redirectPage', 'api', 'apiTestCase', 'single:' + param);
|
||||
this.$emit("redirectPage", "api", "apiTestCase", "single:" + param);
|
||||
break;
|
||||
case "scenario":
|
||||
this.$emit('redirectPage', 'scenarioWithQuery', 'scenario', 'edit:' + param);
|
||||
this.$emit(
|
||||
"redirectPage",
|
||||
"scenarioWithQuery",
|
||||
"scenario",
|
||||
"edit:" + param
|
||||
);
|
||||
break;
|
||||
case "testCase":
|
||||
this.$emit('redirectPage', 'testCase', 'case', 'single:' + param);
|
||||
this.$emit("redirectPage", "testCase", "case", "single:" + param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
this.search();
|
||||
this.testCaseReadOnly = !hasPermission('PROJECT_TRACK_CASE:READ');
|
||||
this.apiCaseReadOnly = !hasPermission('PROJECT_API_DEFINITION:READ');
|
||||
this.apiScenarioReadOnly = !hasPermission('PROJECT_API_SCENARIO:READ');
|
||||
this.loadCaseReadOnly = !hasPermission('PROJECT_PERFORMANCE_TEST:READ');
|
||||
}
|
||||
}
|
||||
this.testCaseReadOnly = !hasPermission("PROJECT_TRACK_CASE:READ");
|
||||
this.apiCaseReadOnly = !hasPermission("PROJECT_API_DEFINITION:READ");
|
||||
this.apiScenarioReadOnly = !hasPermission("PROJECT_API_SCENARIO:READ");
|
||||
this.loadCaseReadOnly = !hasPermission("PROJECT_PERFORMANCE_TEST:READ");
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in New Issue