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;
|
package io.metersphere.base.mapper.ext;
|
||||||
|
|
||||||
import io.metersphere.api.dto.QueryAPIReportRequest;
|
import io.metersphere.api.dto.QueryAPIReportRequest;
|
||||||
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
|
|
||||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||||
import io.metersphere.base.domain.ApiDefinitionExecResultExpand;
|
import io.metersphere.base.domain.ApiDefinitionExecResultExpand;
|
||||||
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
|
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);
|
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);
|
String selectExecResult(String resourceId);
|
||||||
|
|
||||||
ApiDefinitionExecResultWithBLOBs selectPlanApiMaxResultByTestIdAndType(String resourceId, String type);
|
ApiDefinitionExecResultWithBLOBs selectPlanApiMaxResultByTestIdAndType(String resourceId, String type);
|
||||||
|
|
|
@ -39,105 +39,6 @@
|
||||||
WHERE integrated_report_id = #{0}
|
WHERE integrated_report_id = #{0}
|
||||||
</select>
|
</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 id="selectExecResult" resultType="java.lang.String">
|
||||||
select ader.status
|
select ader.status
|
||||||
from api_definition_exec_result ader
|
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.JmxInfoDTO;
|
||||||
import io.metersphere.api.dto.RegistryCenter;
|
import io.metersphere.api.dto.RegistryCenter;
|
||||||
import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
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.ApiDataCountDTO;
|
||||||
import io.metersphere.api.dto.datacount.response.CoveredDTO;
|
import io.metersphere.api.dto.datacount.response.CoveredDTO;
|
||||||
import io.metersphere.api.dto.datacount.response.ExecuteResultCountDTO;
|
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.definition.RunDefinitionRequest;
|
||||||
import io.metersphere.api.dto.export.ScenarioToPerformanceInfoDTO;
|
import io.metersphere.api.dto.export.ScenarioToPerformanceInfoDTO;
|
||||||
import io.metersphere.base.domain.ApiDefinition;
|
import io.metersphere.base.domain.ApiDefinition;
|
||||||
|
@ -32,7 +30,6 @@ import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -251,31 +248,6 @@ public class ApiHomeController {
|
||||||
return PageUtils.setPageInfo(page, resultList);
|
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
|
//初始化版本ID
|
||||||
private String initializationVersionId(String versionId) {
|
private String initializationVersionId(String versionId) {
|
||||||
if (StringUtils.equalsIgnoreCase(versionId, "default")) {
|
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.QueryAPIReportRequest;
|
||||||
import io.metersphere.api.dto.RequestResultExpandDTO;
|
import io.metersphere.api.dto.RequestResultExpandDTO;
|
||||||
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
|
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.mapper.*;
|
import io.metersphere.base.mapper.*;
|
||||||
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
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) {
|
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_")) {
|
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
|
||||||
ApiDefinitionExecResultWithBLOBs saveResult = new ApiDefinitionExecResultWithBLOBs();
|
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.ApiDefinitionExecResult;
|
||||||
import io.metersphere.base.domain.TestPlanApiCase;
|
import io.metersphere.base.domain.TestPlanApiCase;
|
||||||
|
import io.metersphere.dto.ExecutedCaseInfoResult;
|
||||||
import io.metersphere.plan.dto.CaseExecResult;
|
import io.metersphere.plan.dto.CaseExecResult;
|
||||||
import io.metersphere.plan.dto.TestPlanApiCaseInfoDTO;
|
import io.metersphere.plan.dto.TestPlanApiCaseInfoDTO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
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} ")
|
@Select("SELECT id,test_plan_id,api_case_id,status FROM test_plan_api_case WHERE id = #{0} ")
|
||||||
TestPlanApiCase selectBaseInfoById(String testId);
|
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);
|
List<ApiDefinitionExecResult> selectReportStatusByReportIds(@Param("ids") List<String> apiReportIdList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,42 @@
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</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>
|
</mapper>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext;
|
||||||
|
|
||||||
import io.metersphere.base.domain.ApiScenarioReport;
|
import io.metersphere.base.domain.ApiScenarioReport;
|
||||||
import io.metersphere.base.domain.TestPlanApiScenario;
|
import io.metersphere.base.domain.TestPlanApiScenario;
|
||||||
|
import io.metersphere.dto.ExecutedCaseInfoResult;
|
||||||
import io.metersphere.plan.dto.CaseExecResult;
|
import io.metersphere.plan.dto.CaseExecResult;
|
||||||
import io.metersphere.plan.dto.TestPlanApiScenarioInfoDTO;
|
import io.metersphere.plan.dto.TestPlanApiScenarioInfoDTO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
@ -20,4 +21,6 @@ public interface ExtTestPlanScenarioCaseMapper {
|
||||||
TestPlanApiScenario selectBaseInfoById(String testId);
|
TestPlanApiScenario selectBaseInfoById(String testId);
|
||||||
|
|
||||||
List<ApiScenarioReport> selectReportStatusByReportIds(@Param("ids") List<String> scenarioReportIdList);
|
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}
|
#{value}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</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>
|
</mapper>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.metersphere.plan.request.function.QueryTestPlanCaseRequest;
|
||||||
import io.metersphere.plan.request.function.TestPlanFuncCaseConditions;
|
import io.metersphere.plan.request.function.TestPlanFuncCaseConditions;
|
||||||
import io.metersphere.request.BaseQueryRequest;
|
import io.metersphere.request.BaseQueryRequest;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
import java.util.List;
|
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);
|
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<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}
|
UPDATE test_plan_test_case SET status = #{execResult}
|
||||||
WHERE case_id = #{testCaseId} AND plan_id = #{testPlanId}
|
WHERE case_id = #{testCaseId} AND plan_id = #{testPlanId}
|
||||||
</update>
|
</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>
|
</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;
|
package io.metersphere.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.pagehelper.Page;
|
||||||
|
import com.github.pagehelper.PageHelper;
|
||||||
import io.metersphere.base.domain.TestCase;
|
import io.metersphere.base.domain.TestCase;
|
||||||
import io.metersphere.commons.constants.MicroServiceName;
|
import io.metersphere.commons.constants.MicroServiceName;
|
||||||
import io.metersphere.dto.BugStatistics;
|
import io.metersphere.commons.utils.PageUtils;
|
||||||
import io.metersphere.dto.TrackCountResult;
|
import io.metersphere.commons.utils.Pager;
|
||||||
import io.metersphere.dto.TrackStatisticsDTO;
|
import io.metersphere.dto.*;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.plan.dto.ChartsData;
|
import io.metersphere.plan.dto.ChartsData;
|
||||||
import io.metersphere.service.TestCaseService;
|
import io.metersphere.service.TestCaseService;
|
||||||
import io.metersphere.service.TrackService;
|
import io.metersphere.service.TrackService;
|
||||||
import io.metersphere.utils.DiscoveryUtil;
|
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.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@ -58,6 +62,32 @@ public class TrackController {
|
||||||
return statistics;
|
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}")
|
@GetMapping("/relevance/count/{projectId}")
|
||||||
public TrackStatisticsDTO getRelevanceCount(@PathVariable String projectId) {
|
public TrackStatisticsDTO getRelevanceCount(@PathVariable String projectId) {
|
||||||
TrackStatisticsDTO statistics = new TrackStatisticsDTO();
|
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;
|
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.ApiExecutionQueueDetailMapper;
|
||||||
import io.metersphere.base.mapper.ApiExecutionQueueMapper;
|
import io.metersphere.base.mapper.ApiExecutionQueueMapper;
|
||||||
import io.metersphere.commons.constants.KafkaTopicConstants;
|
import io.metersphere.commons.constants.KafkaTopicConstants;
|
||||||
import io.metersphere.commons.constants.TestPlanReportStatus;
|
|
||||||
import io.metersphere.plan.service.AutomationCaseExecOverService;
|
import io.metersphere.plan.service.AutomationCaseExecOverService;
|
||||||
import io.metersphere.plan.service.TestPlanReportService;
|
import io.metersphere.plan.service.TestPlanReportService;
|
||||||
import io.metersphere.utils.LoggerUtil;
|
import io.metersphere.utils.LoggerUtil;
|
||||||
|
import io.metersphere.utils.NamedThreadFactory;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||||
import org.springframework.kafka.annotation.KafkaListener;
|
import org.springframework.kafka.annotation.KafkaListener;
|
||||||
|
import org.springframework.kafka.support.Acknowledgment;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
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
|
@Component
|
||||||
public class ExecReportListener {
|
public class ExecReportListener {
|
||||||
|
@ -32,45 +30,40 @@ public class ExecReportListener {
|
||||||
@Resource
|
@Resource
|
||||||
private AutomationCaseExecOverService automationCaseExecOverService;
|
private AutomationCaseExecOverService automationCaseExecOverService;
|
||||||
|
|
||||||
@KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.TEST_PLAN_REPORT_TOPIC, groupId = "${spring.application.name}")
|
// 线程池维护线程的最少数量
|
||||||
public void consume(ConsumerRecord<?, String> record) {
|
private final static int CORE_POOL_SIZE = 5;
|
||||||
Object testIdObj = record.key();
|
// 线程池维护线程的最大数量
|
||||||
if (ObjectUtils.isEmpty(testIdObj)) {
|
private final static int MAX_POOL_SIZE = 5;
|
||||||
LoggerUtil.info("Execute message. received:", record.value());
|
// 线程池维护线程所允许的空闲时间
|
||||||
this.testPlanReportTestEnded(record.value());
|
private final static int KEEP_ALIVE_TIME = 1;
|
||||||
} else {
|
// 线程池所使用的缓冲队列大小
|
||||||
LoggerUtil.info("Execute message. key:[" + testIdObj.toString() + "], received:", record.value());
|
private final static int WORK_QUEUE_SIZE = 10000;
|
||||||
this.automationCaseTestEnd(testIdObj.toString());
|
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) {
|
try {
|
||||||
//自动化用例执行完成之后的后续操作
|
records.forEach(item -> {
|
||||||
automationCaseExecOverService.automationCaseExecOver(testId);
|
LoggerUtil.info("接收到报告【key:" + item.key() + ",value:" + item.value() + "】,加入到结果处理队列");
|
||||||
}
|
ExecReportListenerTask task = new ExecReportListenerTask();
|
||||||
|
task.setApiExecutionQueueMapper(queueMapper);
|
||||||
public void testPlanReportTestEnded(String testPlanReportId) {
|
task.setApiExecutionQueueDetailMapper(executionQueueDetailMapper);
|
||||||
// 检查测试计划中其他队列是否结束
|
task.setAutomationCaseExecOverService(automationCaseExecOverService);
|
||||||
ApiExecutionQueueExample executionQueueExample = new ApiExecutionQueueExample();
|
task.setTestPlanReportService(testPlanReportService);
|
||||||
executionQueueExample.createCriteria().andReportIdEqualTo(testPlanReportId);
|
task.setRecord(item);
|
||||||
List<ApiExecutionQueue> queues = queueMapper.selectByExample(executionQueueExample);
|
threadPool.execute(task);
|
||||||
if (CollectionUtils.isEmpty(queues)) {
|
});
|
||||||
LoggerUtil.info("Normal execution completes, update test plan report status:" + testPlanReportId);
|
} catch (Exception e) {
|
||||||
testPlanReportService.testPlanExecuteOver(testPlanReportId, TestPlanReportStatus.COMPLETED.name());
|
LoggerUtil.error("测试计划自动化用例结束后-KAFKA消费失败:", e);
|
||||||
} else {
|
} finally {
|
||||||
List<String> ids = queues.stream().map(ApiExecutionQueue::getId).collect(Collectors.toList());
|
ack.acknowledge();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.TestPlanApiScenario;
|
||||||
import io.metersphere.base.domain.TestPlanLoadCase;
|
import io.metersphere.base.domain.TestPlanLoadCase;
|
||||||
import io.metersphere.base.domain.TestPlanUiScenario;
|
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.dto.TestPlanCaseStatusDTO;
|
||||||
import io.metersphere.utils.JsonUtils;
|
import io.metersphere.utils.JsonUtils;
|
||||||
|
import io.metersphere.utils.LoggerUtil;
|
||||||
import io.metersphere.websocket.UICaseStatusHandleSocket;
|
import io.metersphere.websocket.UICaseStatusHandleSocket;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
@ -71,8 +75,13 @@ public class AutomationCaseExecOverService {
|
||||||
testCaseSyncStatusService.updateFunctionCaseStatusByAutomationCaseId(automationCaseId, planId, triggerCaseExecResult);
|
testCaseSyncStatusService.updateFunctionCaseStatusByAutomationCaseId(automationCaseId, planId, triggerCaseExecResult);
|
||||||
}
|
}
|
||||||
if (testPlanUiScenario != null) {
|
if (testPlanUiScenario != null) {
|
||||||
//UI执行完成发送Socket
|
try {
|
||||||
UICaseStatusHandleSocket.sendMessageSingle(planId, JsonUtils.toJSONString(TestPlanCaseStatusDTO.builder().planCaseId(testPlanUiScenario.getId()).planCaseStatus(triggerCaseExecResult)));
|
//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.enums.TestCaseReleevanceType;
|
||||||
import io.metersphere.plan.utils.TestCaseSyncStatusUtil;
|
import io.metersphere.plan.utils.TestCaseSyncStatusUtil;
|
||||||
import io.metersphere.service.BaseUserService;
|
import io.metersphere.service.BaseUserService;
|
||||||
|
import io.metersphere.service.FunctionCaseExecutionInfoService;
|
||||||
import io.metersphere.utils.BatchProcessingUtil;
|
import io.metersphere.utils.BatchProcessingUtil;
|
||||||
|
import io.metersphere.utils.LoggerUtil;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -49,6 +52,8 @@ public class TestCaseSyncStatusService {
|
||||||
private BaseUserService baseUserService;
|
private BaseUserService baseUserService;
|
||||||
@Resource
|
@Resource
|
||||||
private TestCaseCommentMapper testCaseCommentMapper;
|
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) {
|
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)) {
|
if (MapUtils.isNotEmpty(successCaseMap)) {
|
||||||
extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(successCaseMap.keySet()), FunctionCaseExecResult.SUCCESS.toString());
|
extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(successCaseMap.keySet()), FunctionCaseExecResult.SUCCESS.toString());
|
||||||
|
functionCaseExecutionInfoService.insertExecutionInfoByIdList(new ArrayList<>(successCaseMap.keySet()), FunctionCaseExecResult.SUCCESS.toString());
|
||||||
}
|
}
|
||||||
if (MapUtils.isNotEmpty(errorCaseMap)) {
|
if (MapUtils.isNotEmpty(errorCaseMap)) {
|
||||||
extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(errorCaseMap.keySet()), FunctionCaseExecResult.ERROR.toString());
|
extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(errorCaseMap.keySet()), FunctionCaseExecResult.ERROR.toString());
|
||||||
|
functionCaseExecutionInfoService.insertExecutionInfoByIdList(new ArrayList<>(errorCaseMap.keySet()), FunctionCaseExecResult.ERROR.toString());
|
||||||
}
|
}
|
||||||
if (MapUtils.isNotEmpty(blockingCaseMap)) {
|
if (MapUtils.isNotEmpty(blockingCaseMap)) {
|
||||||
extTestPlanTestCaseMapper.updateExecResultByTestPlanCaseIdList(new ArrayList<>(blockingCaseMap.keySet()), FunctionCaseExecResult.BLOCKING.toString());
|
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());
|
this.addTestCaseComment(operator, testPlanName, blockingCaseMap, FunctionCaseExecResult.BLOCKING.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Async
|
||||||
public void updateFunctionCaseStatusByAutomationCaseId(String automationCaseId, String testPlanId, String triggerCaseRunResult) {
|
public void updateFunctionCaseStatusByAutomationCaseId(String automationCaseId, String testPlanId, String triggerCaseRunResult) {
|
||||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId);
|
try {
|
||||||
if (testPlan != null && testPlan.getAutomaticStatusUpdate()) {
|
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId);
|
||||||
HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(testPlan.getCreator()));
|
if (testPlan != null && testPlan.getAutomaticStatusUpdate()) {
|
||||||
|
HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(testPlan.getCreator()));
|
||||||
|
|
||||||
Set<String> testCaseIdSet = new HashSet<>();
|
Set<String> testCaseIdSet = new HashSet<>();
|
||||||
List<TestPlanTestCase> testPlanTestCaseList = extTestPlanTestCaseMapper.selectByAutomationCaseIdAndTestPlanId(automationCaseId, testPlanId);
|
List<TestPlanTestCase> testPlanTestCaseList = extTestPlanTestCaseMapper.selectByAutomationCaseIdAndTestPlanId(automationCaseId, testPlanId);
|
||||||
testPlanTestCaseList.forEach(item -> testCaseIdSet.add(item.getCaseId()));
|
testPlanTestCaseList.forEach(item -> testCaseIdSet.add(item.getCaseId()));
|
||||||
|
|
||||||
if (CollectionUtils.isNotEmpty(testCaseIdSet)) {
|
if (CollectionUtils.isNotEmpty(testCaseIdSet)) {
|
||||||
TestCaseTestExample testCaseTestExample = new TestCaseTestExample();
|
TestCaseTestExample testCaseTestExample = new TestCaseTestExample();
|
||||||
testCaseTestExample.createCriteria().andTestCaseIdIn(new ArrayList<>(testCaseIdSet));
|
testCaseTestExample.createCriteria().andTestCaseIdIn(new ArrayList<>(testCaseIdSet));
|
||||||
List<TestCaseTest> testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample);
|
List<TestCaseTest> testCaseTestList = testCaseTestMapper.selectByExample(testCaseTestExample);
|
||||||
Map<String, List<TestCaseTest>> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId));
|
Map<String, List<TestCaseTest>> testCaseTestMap = testCaseTestList.stream().collect(Collectors.groupingBy(TestCaseTest::getTestCaseId));
|
||||||
|
|
||||||
for (Map.Entry<String, List<TestCaseTest>> entry : testCaseTestMap.entrySet()) {
|
for (Map.Entry<String, List<TestCaseTest>> entry : testCaseTestMap.entrySet()) {
|
||||||
TestCaseRelevanceCasesRequest request = this.generateRelevanceCasesRequest(testPlanId, entry.getKey(), entry.getValue());
|
TestCaseRelevanceCasesRequest request = this.generateRelevanceCasesRequest(testPlanId, entry.getKey(), entry.getValue());
|
||||||
CaseExecResult priorityResult = TestCaseSyncStatusUtil.getTestCaseExecResultByRelevance(request.getTestCaseTestList(),
|
CaseExecResult priorityResult = TestCaseSyncStatusUtil.getTestCaseExecResultByRelevance(request.getTestCaseTestList(),
|
||||||
request.getApiAllCaseMap(), request.getScenarioAllCaseMap(), request.getLoadAllCaseMap(), request.getUiAllCaseMap());
|
request.getApiAllCaseMap(), request.getScenarioAllCaseMap(), request.getLoadAllCaseMap(), request.getUiAllCaseMap());
|
||||||
if (priorityResult == null) {
|
if (priorityResult == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String automationCaseResult = null;
|
String automationCaseResult = null;
|
||||||
if (priorityResult != null && StringUtils.isNotEmpty(priorityResult.getExecResult())) {
|
if (priorityResult != null && StringUtils.isNotEmpty(priorityResult.getExecResult())) {
|
||||||
if (StringUtils.equalsIgnoreCase(ApiReportStatus.ERROR.name(), priorityResult.getExecResult())) {
|
if (StringUtils.equalsIgnoreCase(ApiReportStatus.ERROR.name(), priorityResult.getExecResult())) {
|
||||||
automationCaseResult = ApiReportStatus.ERROR.name();
|
automationCaseResult = ApiReportStatus.ERROR.name();
|
||||||
priorityResult.setExecResult(FunctionCaseExecResult.ERROR.toString());
|
priorityResult.setExecResult(FunctionCaseExecResult.ERROR.toString());
|
||||||
} else if (StringUtils.equalsIgnoreCase(ApiReportStatus.FAKE_ERROR.name(), priorityResult.getExecResult())) {
|
} else if (StringUtils.equalsIgnoreCase(ApiReportStatus.FAKE_ERROR.name(), priorityResult.getExecResult())) {
|
||||||
automationCaseResult = ApiReportStatus.FAKE_ERROR.name();
|
automationCaseResult = ApiReportStatus.FAKE_ERROR.name();
|
||||||
priorityResult.setExecResult(FunctionCaseExecResult.BLOCKING.toString());
|
priorityResult.setExecResult(FunctionCaseExecResult.BLOCKING.toString());
|
||||||
} else if (StringUtils.equalsIgnoreCase(ApiReportStatus.SUCCESS.name(), priorityResult.getExecResult())) {
|
} else if (StringUtils.equalsIgnoreCase(ApiReportStatus.SUCCESS.name(), priorityResult.getExecResult())) {
|
||||||
priorityResult.setExecResult(FunctionCaseExecResult.SUCCESS.toString());
|
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();
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
HttpHeaderUtils.clearUser();
|
LoggerUtil.error("更新功能用例状态出错!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import io.metersphere.excel.constants.TestPlanTestCaseStatus;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.log.vo.OperatingLogDetails;
|
import io.metersphere.log.vo.OperatingLogDetails;
|
||||||
import io.metersphere.plan.constant.ApiReportStatus;
|
import io.metersphere.plan.constant.ApiReportStatus;
|
||||||
|
import io.metersphere.plan.dto.TestPlanDTO;
|
||||||
import io.metersphere.plan.dto.*;
|
import io.metersphere.plan.dto.*;
|
||||||
import io.metersphere.plan.request.QueryTestPlanRequest;
|
import io.metersphere.plan.request.QueryTestPlanRequest;
|
||||||
import io.metersphere.plan.request.TestPlanReportSaveRequest;
|
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.DetailColumn;
|
||||||
import io.metersphere.log.vo.OperatingLogDetails;
|
import io.metersphere.log.vo.OperatingLogDetails;
|
||||||
import io.metersphere.log.vo.track.TestPlanReference;
|
import io.metersphere.log.vo.track.TestPlanReference;
|
||||||
|
import io.metersphere.plan.dto.TestPlanDTO;
|
||||||
import io.metersphere.plan.dto.*;
|
import io.metersphere.plan.dto.*;
|
||||||
import io.metersphere.plan.job.TestPlanTestJob;
|
import io.metersphere.plan.job.TestPlanTestJob;
|
||||||
import io.metersphere.plan.request.AddTestPlanRequest;
|
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.FunctionCaseExecutionInfo;
|
||||||
import io.metersphere.base.domain.FunctionCaseExecutionInfoExample;
|
import io.metersphere.base.domain.FunctionCaseExecutionInfoExample;
|
||||||
import io.metersphere.base.mapper.FunctionCaseExecutionInfoMapper;
|
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.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ import java.util.UUID;
|
||||||
public class FunctionCaseExecutionInfoService {
|
public class FunctionCaseExecutionInfoService {
|
||||||
@Resource
|
@Resource
|
||||||
private FunctionCaseExecutionInfoMapper functionCaseExecutionInfoMapper;
|
private FunctionCaseExecutionInfoMapper functionCaseExecutionInfoMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||||
|
|
||||||
public void insertExecutionInfo(String caseId, String result) {
|
public void insertExecutionInfo(String caseId, String result) {
|
||||||
if (!StringUtils.isAnyEmpty(caseId, 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) {
|
public void deleteBySourceIdList(List<String> ids) {
|
||||||
if (CollectionUtils.isNotEmpty(ids)) {
|
if (CollectionUtils.isNotEmpty(ids)) {
|
||||||
FunctionCaseExecutionInfoExample example = new FunctionCaseExecutionInfoExample();
|
FunctionCaseExecutionInfoExample example = new FunctionCaseExecutionInfoExample();
|
||||||
|
|
|
@ -3,18 +3,16 @@ package io.metersphere.service;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.base.domain.CustomField;
|
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.TestPlanMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
|
import io.metersphere.base.mapper.ext.*;
|
||||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
|
||||||
import io.metersphere.commons.constants.CustomFieldScene;
|
import io.metersphere.commons.constants.CustomFieldScene;
|
||||||
import io.metersphere.commons.utils.DateUtils;
|
import io.metersphere.commons.utils.DateUtils;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
import io.metersphere.commons.utils.SessionUtils;
|
||||||
import io.metersphere.constants.IssueStatus;
|
import io.metersphere.constants.IssueStatus;
|
||||||
import io.metersphere.constants.SystemCustomField;
|
import io.metersphere.constants.SystemCustomField;
|
||||||
import io.metersphere.dto.BugStatistics;
|
import io.metersphere.dto.BugStatistics;
|
||||||
import io.metersphere.dto.TestPlanBugCount;
|
import io.metersphere.dto.ExecutedCaseInfoResult;
|
||||||
import io.metersphere.dto.TestPlanDTOWithMetric;
|
import io.metersphere.dto.TestPlanDTOWithMetric;
|
||||||
import io.metersphere.dto.TrackCountResult;
|
import io.metersphere.dto.TrackCountResult;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
@ -23,13 +21,13 @@ import io.metersphere.plan.service.TestPlanService;
|
||||||
import io.metersphere.request.testcase.TrackCount;
|
import io.metersphere.request.testcase.TrackCount;
|
||||||
import io.metersphere.xpack.track.dto.IssuesDao;
|
import io.metersphere.xpack.track.dto.IssuesDao;
|
||||||
import io.metersphere.xpack.track.dto.request.IssuesRequest;
|
import io.metersphere.xpack.track.dto.request.IssuesRequest;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -49,7 +47,12 @@ public class TrackService {
|
||||||
private BaseCustomFieldService baseCustomFieldService;
|
private BaseCustomFieldService baseCustomFieldService;
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanService testPlanService;
|
private TestPlanService testPlanService;
|
||||||
|
@Resource
|
||||||
|
private ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtIssuesMapper extIssuesMapper;
|
private ExtIssuesMapper extIssuesMapper;
|
||||||
|
|
||||||
|
@ -304,4 +307,40 @@ public class TrackService {
|
||||||
testPlanService.calcTestPlanRate(testPlan);
|
testPlanService.calcTestPlanRate(testPlan);
|
||||||
return testPlan.getPassRate();
|
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) {
|
export function getTrackCount(selectProjectId) {
|
||||||
return get("/track/count/" + 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) {
|
export function getTrackRelevanceCount(selectProjectId) {
|
||||||
return get("/track/relevance/count/" + selectProjectId);
|
return get("/track/relevance/count/" + selectProjectId);
|
||||||
}
|
}
|
||||||
|
@ -12,8 +23,21 @@ export function getTrackCaseBar(selectProjectId) {
|
||||||
return get("/track/case/bar/" + selectProjectId);
|
return get("/track/case/bar/" + selectProjectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTrackRunningTask(selectProjectId, currentPage, pageSize, param) {
|
export function getTrackRunningTask(
|
||||||
return post("/task/center/runningTask/" + selectProjectId + "/" + currentPage + "/" + pageSize, param);
|
selectProjectId,
|
||||||
|
currentPage,
|
||||||
|
pageSize,
|
||||||
|
param
|
||||||
|
) {
|
||||||
|
return post(
|
||||||
|
"/task/center/runningTask/" +
|
||||||
|
selectProjectId +
|
||||||
|
"/" +
|
||||||
|
currentPage +
|
||||||
|
"/" +
|
||||||
|
pageSize,
|
||||||
|
param
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTrackBugCount(selectProjectId) {
|
export function getTrackBugCount(selectProjectId) {
|
||||||
|
@ -21,9 +45,10 @@ export function getTrackBugCount(selectProjectId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNumber(param) {
|
export function formatNumber(param) {
|
||||||
let num = (param || 0).toString(), result = '';
|
let num = (param || 0).toString(),
|
||||||
|
result = "";
|
||||||
while (num.length > 3) {
|
while (num.length > 3) {
|
||||||
result = ',' + num.slice(-3) + result;
|
result = "," + num.slice(-3) + result;
|
||||||
num = num.slice(0, num.length - 3);
|
num = num.slice(0, num.length - 3);
|
||||||
}
|
}
|
||||||
if (num) {
|
if (num) {
|
||||||
|
@ -31,4 +56,3 @@ export function formatNumber(param) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,7 @@
|
||||||
|
|
||||||
<el-row style="margin-top: 16px">
|
<el-row style="margin-top: 16px">
|
||||||
<el-col style="background-color: #ffffff">
|
<el-col style="background-color: #ffffff">
|
||||||
<ms-failure-test-case-list
|
<ms-failure-test-case-list @redirectPage="redirectPage" />
|
||||||
:select-function-case="true"
|
|
||||||
@redirectPage="redirectPage"
|
|
||||||
/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,64 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="margin: 24px" class="failure-case-table">
|
<div style="margin: 24px" class="failure-case-table">
|
||||||
<span class="table-title">
|
<span class="table-title">
|
||||||
{{ $t('api_test.home_page.failed_case_list.title') }}
|
{{ $t("api_test.home_page.failed_case_list.title") }}
|
||||||
</span>
|
</span>
|
||||||
<div style="margin-top: 16px" v-loading="loading" element-loading-background="#FFFFFF">
|
<div
|
||||||
<div v-show="loadError"
|
style="margin-top: 16px"
|
||||||
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
|
v-loading="loading"
|
||||||
<img style="height: 100px;width: 100px;"
|
element-loading-background="#FFFFFF"
|
||||||
src="/assets/module/figma/icon_load_error.svg"/>
|
>
|
||||||
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
|
<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>
|
||||||
<div v-show="!loadError">
|
<div v-show="!loadError">
|
||||||
<el-table :data="tableData" class="adjust-table table-content"
|
<el-table
|
||||||
header-cell-class-name="home-table-cell" style="min-height: 228px">
|
:data="tableData"
|
||||||
|
class="adjust-table table-content"
|
||||||
|
header-cell-class-name="home-table-cell"
|
||||||
|
style="min-height: 228px"
|
||||||
|
>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
type="index"
|
type="index"
|
||||||
:label="$t('home.case.index')"
|
:label="$t('home.case.index')"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
width="100px"/>
|
width="100px"
|
||||||
|
/>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="caseName"
|
prop="caseName"
|
||||||
:label="$t('home.case.case_name')"
|
:label="$t('home.case.case_name')"
|
||||||
min-width="200px">
|
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)"
|
<template v-slot:default="{ row }">
|
||||||
:disabled="(row.caseType === 'apiCase' && apiCaseReadOnly) || (row.caseType === 'scenario' && apiScenarioReadOnly) ||
|
<el-link
|
||||||
(row.caseType === 'load' && loadCaseReadOnly) || (row.caseType === 'testCase' && testCaseReadOnly)">
|
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 }}
|
{{ row.caseName }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
|
@ -37,18 +69,28 @@
|
||||||
:label="$t('home.case.case_type')"
|
:label="$t('home.case.case_type')"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
column-key="caseType"
|
column-key="caseType"
|
||||||
width="150px">
|
width="150px"
|
||||||
|
>
|
||||||
<template v-slot:default="scope">
|
<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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="testPlan"
|
prop="testPlan"
|
||||||
:label="$t('home.case.test_plan')"
|
:label="$t('home.case.test_plan')"
|
||||||
width="300px">
|
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']">
|
<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 }}
|
{{ row.testPlan }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
|
@ -58,19 +100,38 @@
|
||||||
prop="failureTimes"
|
prop="failureTimes"
|
||||||
:label="$t('home.case.failure_times')"
|
:label="$t('home.case.failure_times')"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
width="350px"/>
|
width="350px"
|
||||||
|
/>
|
||||||
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div
|
<div
|
||||||
style="width: 100%;height: 238px;display: flex;flex-direction: column;justify-content: center;align-items: center">
|
style="
|
||||||
<img style="height: 100px;width: 100px;margin-bottom: 8px"
|
width: 100%;
|
||||||
src="/assets/module/figma/icon_none.svg"/>
|
height: 238px;
|
||||||
<span class="addition-info-title">{{ $t("home.dashboard.public.no_data") }}</span>
|
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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
<home-pagination v-if="tableData.length > 0" :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize" layout="prev, pager, next, sizes"
|
<home-pagination
|
||||||
:total="total"/>
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,11 +139,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||||
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
import { getCurrentProjectID } from "metersphere-frontend/src/utils/token";
|
||||||
import {homeTestPlanFailureCaseGet} from "@/api/remote/api/api-home";
|
import { hasPermission } from "@/business/utils/sdk-utils";
|
||||||
import {hasPermission} from "@/business/utils/sdk-utils";
|
|
||||||
import HomePagination from "@/business/home/components/pagination/HomePagination";
|
import HomePagination from "@/business/home/components/pagination/HomePagination";
|
||||||
import BasicCaseTypeLabel from "metersphere-frontend/src/components/BasicCaseTypeLabel";
|
import BasicCaseTypeLabel from "metersphere-frontend/src/components/BasicCaseTypeLabel";
|
||||||
|
import { homeTestPlanFailureCaseGet } from "@/api/track";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsFailureTestCaseList",
|
name: "MsFailureTestCaseList",
|
||||||
|
@ -101,10 +162,7 @@ export default {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
total: 0,
|
total: 0,
|
||||||
}
|
};
|
||||||
},
|
|
||||||
props: {
|
|
||||||
selectFunctionCase: Boolean,
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
projectId() {
|
projectId() {
|
||||||
|
@ -116,13 +174,19 @@ export default {
|
||||||
if (this.projectId) {
|
if (this.projectId) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.loadError = false;
|
this.loadError = false;
|
||||||
homeTestPlanFailureCaseGet(this.projectId, this.selectFunctionCase, 10, this.currentPage, this.pageSize)
|
homeTestPlanFailureCaseGet(
|
||||||
|
this.projectId,
|
||||||
|
10,
|
||||||
|
this.currentPage,
|
||||||
|
this.pageSize
|
||||||
|
)
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.loadError = false;
|
this.loadError = false;
|
||||||
this.total = r.data.itemCount;
|
this.total = r.data.itemCount;
|
||||||
this.tableData = r.data.listObject;
|
this.tableData = r.data.listObject;
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.loadError = true;
|
this.loadError = true;
|
||||||
});
|
});
|
||||||
|
@ -131,28 +195,33 @@ export default {
|
||||||
redirect(pageType, param) {
|
redirect(pageType, param) {
|
||||||
switch (pageType) {
|
switch (pageType) {
|
||||||
case "testPlanEdit":
|
case "testPlanEdit":
|
||||||
this.$emit('redirectPage', 'testPlanEdit', null, param);
|
this.$emit("redirectPage", "testPlanEdit", null, param);
|
||||||
break;
|
break;
|
||||||
case "apiCase":
|
case "apiCase":
|
||||||
this.$emit('redirectPage', 'api', 'apiTestCase', 'single:' + param);
|
this.$emit("redirectPage", "api", "apiTestCase", "single:" + param);
|
||||||
break;
|
break;
|
||||||
case "scenario":
|
case "scenario":
|
||||||
this.$emit('redirectPage', 'scenarioWithQuery', 'scenario', 'edit:' + param);
|
this.$emit(
|
||||||
|
"redirectPage",
|
||||||
|
"scenarioWithQuery",
|
||||||
|
"scenario",
|
||||||
|
"edit:" + param
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "testCase":
|
case "testCase":
|
||||||
this.$emit('redirectPage', 'testCase', 'case', 'single:' + param);
|
this.$emit("redirectPage", "testCase", "case", "single:" + param);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
activated() {
|
activated() {
|
||||||
this.search();
|
this.search();
|
||||||
this.testCaseReadOnly = !hasPermission('PROJECT_TRACK_CASE:READ');
|
this.testCaseReadOnly = !hasPermission("PROJECT_TRACK_CASE:READ");
|
||||||
this.apiCaseReadOnly = !hasPermission('PROJECT_API_DEFINITION:READ');
|
this.apiCaseReadOnly = !hasPermission("PROJECT_API_DEFINITION:READ");
|
||||||
this.apiScenarioReadOnly = !hasPermission('PROJECT_API_SCENARIO:READ');
|
this.apiScenarioReadOnly = !hasPermission("PROJECT_API_SCENARIO:READ");
|
||||||
this.loadCaseReadOnly = !hasPermission('PROJECT_PERFORMANCE_TEST:READ');
|
this.loadCaseReadOnly = !hasPermission("PROJECT_PERFORMANCE_TEST:READ");
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
Loading…
Reference in New Issue