feat(测试计划): 测试计划统计接口开发

This commit is contained in:
Jianguo-Genius 2024-11-15 10:29:23 +08:00 committed by 刘瑞斌
parent bc912a6fad
commit 72bd8d6b44
19 changed files with 290 additions and 5 deletions

View File

@ -59,6 +59,14 @@ public class TestPlanController {
return testPlanManagementService.page(request); return testPlanManagementService.page(request);
} }
@PostMapping("/rage")
@Operation(summary = "测试计划-测试计划统计")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public TestPlanCoverageDTO rage(@Validated @RequestBody TestPlanCoverageRequest request) {
return testPlanService.rageByProjectIdAndTimestamp(request.getProjectId(), request.getStartTime(), request.getEndTime());
}
@GetMapping("/group-list/{projectId}") @GetMapping("/group-list/{projectId}")
@Operation(summary = "测试计划-测试计划组查询") @Operation(summary = "测试计划-测试计划组查询")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ) @RequiresPermissions(PermissionConstants.TEST_PLAN_READ)

View File

@ -0,0 +1,49 @@
package io.metersphere.plan.dto.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
@Data
public class TestPlanCoverageRequest {
@Schema(description = "固定时间天数")
private int dayNumber = 0;
@Schema(description = "自定义开始时间")
private long startTime = 0;
@Schema(description = "自定义结束时间")
private long endTime = 0;
@Schema(description = "项目ID")
@NotBlank
private String projectId;
public long getStartTime() {
if (startTime == 0 && dayNumber > 0) {
LocalDate now = LocalDate.now();
LocalDate localDate = now.minusDays(dayNumber);
LocalDateTime localDateTime = LocalDateTime.of(localDate, LocalTime.MIN);//当天零点
return localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
} else {
return startTime;
}
}
public long getEndTime() {
if (endTime == 0 && dayNumber > 0) {
LocalDateTime now = LocalDateTime.now();
return now.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
} else {
return endTime;
}
}
}

View File

@ -0,0 +1,59 @@
package io.metersphere.plan.dto.response;
import lombok.Data;
@Data
public class TestPlanCoverageDTO {
/**
* 未执行
* 已执行
*/
private int unExecute = 0;
private int executed = 0;
/**
* 通过
* 未通过
*/
private int passed = 0;
private int notPassed = 0;
/**
* 已完成
* 进行中
* 未开始
* 已归档
*/
private int finished = 0;
private int running = 0;
private int prepared = 0;
private int archived = 0;
public void archivedAutoIncrement() {
archived++;
}
public void notStartedAutoIncrement() {
prepared++;
unExecute++;
notPassed++;
}
public void successAutoIncrement() {
executed++;
passed++;
finished++;
}
public void unSuccessAutoIncrement() {
executed++;
notPassed++;
finished++;
}
public void testPlanRunningAutoIncrement() {
unExecute++;
notPassed++;
running++;
}
}

View File

@ -82,4 +82,6 @@ public interface ExtTestPlanApiCaseMapper {
List<TestPlanResourceExecResultDTO> selectDistinctExecResultByTestPlanIds(@Param("testPlanIds") List<String> testPlanIds); List<TestPlanResourceExecResultDTO> selectDistinctExecResultByTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
Integer countByPlanIds(@Param("planIds") List<String> planIds); Integer countByPlanIds(@Param("planIds") List<String> planIds);
List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
} }

View File

@ -892,6 +892,23 @@
</foreach> </foreach>
AND test_plan.status != 'ARCHIVED' AND test_plan.status != 'ARCHIVED'
</select> </select>
<select id="selectDistinctLastExecResultByTestPlanIds"
resultType="io.metersphere.plan.dto.TestPlanResourceExecResultDTO">
select distinct resource.test_plan_id AS testPlanId,
CASE
WHEN resource.last_exec_result is null
THEN 'PENDING'
WHEN resource.last_exec_result = '-'
THEN 'PENDING'
ELSE resource.last_exec_result
END AS execResult
from test_plan_api_case resource
where resource.test_plan_id IN
<foreach collection="testPlanIds" item="testPlanId" separator="," open="(" close=")">
#{testPlanId}
</foreach>
</select>
<select id="getBatchRunInfoByIds" resultType="io.metersphere.plan.dto.TestPlanApiCaseBatchRunDTO"> <select id="getBatchRunInfoByIds" resultType="io.metersphere.plan.dto.TestPlanApiCaseBatchRunDTO">
SELECT t.id as id, t.test_plan_collection_id as test_plan_collection_id, atc.name as name, atc.id as caseId SELECT t.id as id, t.test_plan_collection_id as test_plan_collection_id, atc.name as name, atc.id as caseId
FROM test_plan_api_case t FROM test_plan_api_case t

View File

@ -79,4 +79,6 @@ public interface ExtTestPlanApiScenarioMapper {
List<TestPlanApiScenarioBatchRunDTO> getBatchRunInfoByIds(@Param("ids") List<String> ids); List<TestPlanApiScenarioBatchRunDTO> getBatchRunInfoByIds(@Param("ids") List<String> ids);
Integer countByPlanIds(@Param("planIds") List<String> planIds); Integer countByPlanIds(@Param("planIds") List<String> planIds);
List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
} }

View File

@ -663,4 +663,20 @@
</foreach> </foreach>
</where> </where>
</select> </select>
<select id="selectDistinctLastExecResultByTestPlanIds"
resultType="io.metersphere.plan.dto.TestPlanResourceExecResultDTO">
select distinct resource.test_plan_id AS testPlanId,
CASE
WHEN resource.last_exec_result is null
THEN 'PENDING'
WHEN resource.last_exec_result = '-'
THEN 'PENDING'
ELSE resource.last_exec_result
END AS execResult
from test_plan_api_scenario resource
where resource.test_plan_id IN
<foreach collection="testPlanIds" item="testPlanId" separator="," open="(" close=")">
#{testPlanId}
</foreach>
</select>
</mapper> </mapper>

View File

@ -87,4 +87,6 @@ public interface ExtTestPlanFunctionalCaseMapper {
Collection<String> selectIdsByModuleIds(@Param("request") TestPlanCaseMinderRequest request, @Param("minderModuleIds") List<String> minderModuleIds); Collection<String> selectIdsByModuleIds(@Param("request") TestPlanCaseMinderRequest request, @Param("minderModuleIds") List<String> minderModuleIds);
Collection<String> selectIdsByRootIds(@Param("rootIds") List<String> rootIds, @Param("testPlanId") String testPlanId); Collection<String> selectIdsByRootIds(@Param("rootIds") List<String> rootIds, @Param("testPlanId") String testPlanId);
List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(@Param("testPlanIds") List<String> testPlanIds);
} }

View File

@ -780,5 +780,21 @@
</foreach> </foreach>
and functional_case.module_id = 'root' and functional_case.module_id = 'root'
</select> </select>
<select id="selectDistinctLastExecResultByTestPlanIds"
resultType="io.metersphere.plan.dto.TestPlanResourceExecResultDTO">
select distinct resource.test_plan_id AS testPlanId,
CASE
WHEN resource.last_exec_result is null
THEN 'PENDING'
WHEN resource.last_exec_result = '-'
THEN 'PENDING'
ELSE resource.last_exec_result
END AS execResult
from test_plan_functional_case resource
where resource.test_plan_id IN
<foreach collection="testPlanIds" item="testPlanId" separator="," open="(" close=")">
#{testPlanId}
</foreach>
</select>
</mapper> </mapper>

View File

@ -93,4 +93,6 @@ public interface ExtTestPlanMapper {
* @return List<SelectOption> * @return List<SelectOption>
*/ */
List<SelectOption> getPlanBugList(@Param("projectId") String projectId, @Param("type") String type, @Param("platforms") List<String> platform, @Param("statusList") List<String> statusList); List<SelectOption> getPlanBugList(@Param("projectId") String projectId, @Param("type") String type, @Param("platforms") List<String> platform, @Param("statusList") List<String> statusList);
List<TestPlan> selectIdAndStatusByProjectIdAndCreateTimeRangeAndType(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("type") String testPlanTypePlan);
} }

View File

@ -868,6 +868,13 @@
AND test_plan.create_time BETWEEN #{startTime} AND #{endTime} AND test_plan.create_time BETWEEN #{startTime} AND #{endTime}
</if> </if>
</select> </select>
<select id="selectIdAndStatusByProjectIdAndCreateTimeRangeAndType" resultType="io.metersphere.plan.domain.TestPlan">
SELECT id, status
FROM test_plan
WHERE project_id = #{projectId}
AND create_time BETWEEN #{startTime} AND #{endTime}
AND type = #{type}
</select>
<select id="getPlanBugList" resultType="io.metersphere.plugin.platform.dto.SelectOption"> <select id="getPlanBugList" resultType="io.metersphere.plugin.platform.dto.SelectOption">
select distinct bug.id as `value`, bug.status as `text` select distinct bug.id as `value`, bug.status as `text`
from test_plan from test_plan

View File

@ -145,6 +145,11 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
return extTestPlanApiCaseMapper.selectDistinctExecResultByTestPlanIds(testPlanIds); return extTestPlanApiCaseMapper.selectDistinctExecResultByTestPlanIds(testPlanIds);
} }
@Override
public List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(List<String> testPlanIds) {
return extTestPlanApiCaseMapper.selectDistinctLastExecResultByTestPlanIds(testPlanIds);
}
@Override @Override
public void deleteBatchByTestPlanId(List<String> testPlanIdList) { public void deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanApiCaseExample example = new TestPlanApiCaseExample(); TestPlanApiCaseExample example = new TestPlanApiCaseExample();

View File

@ -133,6 +133,11 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
return extTestPlanApiScenarioMapper.selectDistinctExecResultByTestPlanIds(testPlanIds); return extTestPlanApiScenarioMapper.selectDistinctExecResultByTestPlanIds(testPlanIds);
} }
@Override
public List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(List<String> testPlanIds) {
return extTestPlanApiScenarioMapper.selectDistinctLastExecResultByTestPlanIds(testPlanIds);
}
@Override @Override
public void deleteBatchByTestPlanId(List<String> testPlanIdList) { public void deleteBatchByTestPlanId(List<String> testPlanIdList) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();

View File

@ -73,6 +73,11 @@ public class TestPlanBugService extends TestPlanResourceService {
return List.of(); return List.of();
} }
@Override
public List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(List<String> testPlanIds) {
return List.of();
}
@Override @Override
public long getNextOrder(String testPlanId) { public long getNextOrder(String testPlanId) {

View File

@ -143,6 +143,11 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService {
return extTestPlanFunctionalCaseMapper.selectDistinctExecResultByTestPlanIds(testPlanIds); return extTestPlanFunctionalCaseMapper.selectDistinctExecResultByTestPlanIds(testPlanIds);
} }
@Override
public List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(List<String> testPlanIds) {
return extTestPlanFunctionalCaseMapper.selectDistinctLastExecResultByTestPlanIds(testPlanIds);
}
@Override @Override
public long copyResource(String originalTestPlanId, String newTestPlanId, Map<String, String> oldCollectionIdToNewCollectionId, String operator, long operatorTime) { public long copyResource(String originalTestPlanId, String newTestPlanId, Map<String, String> oldCollectionIdToNewCollectionId, String operator, long operatorTime) {
List<TestPlanFunctionalCase> copyList = new ArrayList<>(); List<TestPlanFunctionalCase> copyList = new ArrayList<>();

View File

@ -642,11 +642,7 @@ public class TestPlanReportService {
long beforeFlush = System.currentTimeMillis(); long beforeFlush = System.currentTimeMillis();
sqlSession.flushStatements(); sqlSession.flushStatements();
long beforeClose = System.currentTimeMillis();
System.out.println("flush time: " + (beforeClose - beforeFlush));
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
long afterFlush = System.currentTimeMillis();
System.out.println("close time: " + (afterFlush - beforeClose));
return TestPlanReportDetailCaseDTO.builder() return TestPlanReportDetailCaseDTO.builder()
.functionCaseCount(funcCaseCount.get()).apiCaseCount(apiCaseCount.get()).apiScenarioCount(apiScenarioCount.get()).bugCount(bugCount.get()).build(); .functionCaseCount(funcCaseCount.get()).apiCaseCount(apiCaseCount.get()).apiScenarioCount(apiScenarioCount.get()).bugCount(bugCount.get()).build();

View File

@ -9,7 +9,10 @@ import io.metersphere.bug.service.BugStatusService;
import io.metersphere.dto.BugProviderDTO; import io.metersphere.dto.BugProviderDTO;
import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.domain.TestPlanCollectionExample; import io.metersphere.plan.domain.TestPlanCollectionExample;
import io.metersphere.plan.dto.*; import io.metersphere.plan.dto.TestPlanCaseBugDTO;
import io.metersphere.plan.dto.TestPlanCollectionDTO;
import io.metersphere.plan.dto.TestPlanResourceAssociationParam;
import io.metersphere.plan.dto.TestPlanResourceExecResultDTO;
import io.metersphere.plan.dto.request.BaseCollectionAssociateRequest; import io.metersphere.plan.dto.request.BaseCollectionAssociateRequest;
import io.metersphere.plan.dto.request.BasePlanCaseBatchRequest; import io.metersphere.plan.dto.request.BasePlanCaseBatchRequest;
import io.metersphere.plan.dto.request.TestPlanCaseAssociateBugRequest; import io.metersphere.plan.dto.request.TestPlanCaseAssociateBugRequest;
@ -104,6 +107,8 @@ public abstract class TestPlanResourceService extends TestPlanSortService {
public abstract List<TestPlanResourceExecResultDTO> selectDistinctExecResultByTestPlanIds(List<String> testPlanIds); public abstract List<TestPlanResourceExecResultDTO> selectDistinctExecResultByTestPlanIds(List<String> testPlanIds);
public abstract List<TestPlanResourceExecResultDTO> selectDistinctLastExecResultByTestPlanIds(List<String> testPlanIds);
/** /**
* 关联用例 * 关联用例
* *

View File

@ -3,7 +3,9 @@ package io.metersphere.plan.service;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanCollectionDTO; import io.metersphere.plan.dto.TestPlanCollectionDTO;
import io.metersphere.plan.dto.TestPlanExecuteHisDTO; import io.metersphere.plan.dto.TestPlanExecuteHisDTO;
import io.metersphere.plan.dto.TestPlanResourceExecResultDTO;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCoverageDTO;
import io.metersphere.plan.dto.response.TestPlanDetailResponse; import io.metersphere.plan.dto.response.TestPlanDetailResponse;
import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse; import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
@ -37,6 +39,7 @@ import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.BatchProcessUtils; import io.metersphere.system.utils.BatchProcessUtils;
import io.metersphere.system.utils.ServiceUtils; import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -1012,4 +1015,65 @@ public class TestPlanService extends TestPlanBaseUtilsService {
testPlanReportService.updateExecuteTimeAndStatus(testPlanReportId); testPlanReportService.updateExecuteTimeAndStatus(testPlanReportId);
} }
} }
public TestPlanCoverageDTO rageByProjectIdAndTimestamp(@NotBlank String projectId, long startTime, long endTime) {
TestPlanCoverageDTO returnDTO = new TestPlanCoverageDTO();
TestPlanExample testPlanExample = new TestPlanExample();
testPlanExample.createCriteria().andProjectIdEqualTo(projectId).andCreateTimeBetween(startTime, endTime).andTypeEqualTo(TestPlanConstants.TEST_PLAN_TYPE_PLAN);
List<TestPlan> testPlanList = extTestPlanMapper.selectIdAndStatusByProjectIdAndCreateTimeRangeAndType(projectId, startTime, endTime, TestPlanConstants.TEST_PLAN_TYPE_PLAN);
List<String> notArchivedList = new ArrayList<>();
testPlanList.forEach(item -> {
if (StringUtils.equalsIgnoreCase(item.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) {
returnDTO.archivedAutoIncrement();
} else {
notArchivedList.add(item.getId());
}
});
// 将当前项目下未归档的测试计划结果查询出来进行下列符合条件的筛选
Map<String, TestPlanResourceService> beansOfType = applicationContext.getBeansOfType(TestPlanResourceService.class);
// 批量处理
SubListUtils.dealForSubList(notArchivedList, SubListUtils.DEFAULT_BATCH_SIZE, dealList -> {
TestPlanConfigExample testPlanConfigExample = new TestPlanConfigExample();
testPlanConfigExample.createCriteria().andTestPlanIdIn(dealList);
List<TestPlanConfig> testPlanConfigList = testPlanConfigMapper.selectByExample(testPlanConfigExample);
Map<String, TestPlanConfig> testPlanConfigMap = testPlanConfigList.stream().collect(Collectors.toMap(TestPlanConfig::getTestPlanId, v -> v));
List<TestPlanResourceExecResultDTO> execResults = new ArrayList<>();
beansOfType.forEach((k, v) -> execResults.addAll(v.selectDistinctLastExecResultByTestPlanIds(dealList)));
Map<String, List<String>> testPlanExecResultMap = execResults.stream().collect(Collectors.groupingBy(TestPlanResourceExecResultDTO::getTestPlanId, Collectors.mapping(TestPlanResourceExecResultDTO::getExecResult, Collectors.toList())));
for (String testPlanId : dealList) {
List<String> executeResultList = testPlanExecResultMap.get(testPlanId);
if (CollectionUtils.isEmpty(executeResultList)) {
// 未运行
returnDTO.notStartedAutoIncrement();
} else {
List<String> calculateList = executeResultList.stream().distinct().toList();
//目前只有三个状态如果同时包含多种状态(进行中/未开始进行中/已完成已完成/未开始进行中/未开始/已完成),根据算法可得测试计划都会是进行中
if (calculateList.size() == 1) {
if (calculateList.contains(ResultStatus.SUCCESS.name())) {
returnDTO.successAutoIncrement();
} else if (calculateList.contains(ExecStatus.PENDING.name())) {
returnDTO.notStartedAutoIncrement();
} else {
returnDTO.unSuccessAutoIncrement();
}
} else {
if (calculateList.contains(ExecStatus.PENDING.name())) {
// 存在还未完成的用例测试计划为进行中
returnDTO.testPlanRunningAutoIncrement();
} else {
returnDTO.unSuccessAutoIncrement();
}
}
}
}
});
return returnDTO;
}
} }

View File

@ -7,6 +7,7 @@ import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.plan.constants.TestPlanResourceConfig; import io.metersphere.plan.constants.TestPlanResourceConfig;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCoverageDTO;
import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.dto.response.TestPlanResponse; import io.metersphere.plan.dto.response.TestPlanResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse; import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
@ -1650,6 +1651,25 @@ public class TestPlanTests extends BaseTest {
Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig() == null); Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig() == null);
} }
@Test
@Order(62)
public void testPlanRage() throws Exception {
if (StringUtils.isAnyBlank(groupTestPlanId7, groupTestPlanId15)) {
this.testPlanAddTest();
}
TestPlanCoverageRequest request = new TestPlanCoverageRequest();
request.setProjectId(project.getId());
request.setDayNumber(7);
MvcResult mvcResult = this.requestPostWithOkAndReturn("/test-plan/rage", request);
TestPlanCoverageDTO coverageDTO = this.getResultData(mvcResult, TestPlanCoverageDTO.class);
Assertions.assertEquals(coverageDTO.getUnExecute() + coverageDTO.getExecuted(), coverageDTO.getPassed() + coverageDTO.getNotPassed());
Assertions.assertEquals(coverageDTO.getFinished() + coverageDTO.getRunning() + coverageDTO.getPrepared(), coverageDTO.getPassed() + coverageDTO.getNotPassed());
}
@Test @Test
@Order(81) @Order(81)
public void copyTestPlan() throws Exception { public void copyTestPlan() throws Exception {