feat(测试计划): 报告用例详情支持测试集查询

This commit is contained in:
song-cc-rock 2024-09-11 17:07:52 +08:00 committed by Craftsman
parent 02b1b6cf95
commit 3c5d0c3d51
14 changed files with 166 additions and 1 deletions

View File

@ -0,0 +1,17 @@
package io.metersphere.plan.constants;
public class CollectionQueryType {
/**
* 功能用例
*/
public static final String FUNCTIONAL = "functional";
/**
* 接口用例
*/
public static final String API = "api";
/**
* 场景用例
*/
public static final String SCENARIO = "scenario";
}

View File

@ -9,6 +9,7 @@ import io.metersphere.plan.domain.TestPlanReportComponent;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse; import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailResponse; import io.metersphere.plan.dto.response.TestPlanReportDetailResponse;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.service.*; import io.metersphere.plan.service.*;
@ -23,6 +24,8 @@ import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils; import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -228,4 +231,19 @@ public class TestPlanReportController {
public void exportLog(@PathVariable String reportId) { public void exportLog(@PathVariable String reportId) {
testPlanReportService.exportLog(reportId, SessionUtils.getUserId()); testPlanReportService.exportLog(reportId, SessionUtils.getUserId());
} }
@PostMapping("/detail/{type}/collection/page")
@Operation(summary = "测试计划-报告-详情-测试集分页查询(不同用例类型)")
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReportId()", resourceType = "test_plan_report")
@Parameter(name = "type", description = "用例类型", schema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED), example = "functional, api, scenario")
public Pager<List<TestPlanReportDetailCollectionResponse>> collectionPage(@PathVariable String type, @Validated @RequestBody TestPlanReportDetailPageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tpc.pos desc");
if (!request.getStartPager()) {
page.close();
page.setOrderBy("tpc.pos desc");
}
return PageUtils.setPageInfo(page, testPlanReportService.listReportCollection(request, type));
}
} }

View File

@ -16,4 +16,7 @@ public class TestPlanReportDetailPageRequest extends BasePageRequest {
@Schema(description = "是否分页", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "是否分页", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Boolean startPager = true; private Boolean startPager = true;
@Schema(description = "测试集ID")
private String collectionId;
} }

View File

@ -0,0 +1,17 @@
package io.metersphere.plan.dto.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class TestPlanReportDetailCollectionResponse {
@Schema(description = "测试集ID")
private String id;
@Schema(description = "测试集名称")
private String name;
@Schema(description = "测试集用例数量")
private Long count;
@Schema(description = "计划名称")
private String planName;
}

View File

@ -5,6 +5,7 @@ import io.metersphere.plan.dto.CaseStatusCountMap;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
import io.metersphere.plan.dto.TestPlanBaseModule; import io.metersphere.plan.dto.TestPlanBaseModule;
import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest; import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@ -41,5 +42,12 @@ public interface ExtTestPlanReportApiCaseMapper {
*/ */
List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request); List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request);
/**
* 分页查询报告关联的测试集(接口)
* @param request 请求参数
* @return 关联的测试集集合
*/
List<TestPlanReportDetailCollectionResponse> listCollection(@Param("request") TestPlanReportDetailPageRequest request);
List<String> getIdsByReportIdAndCollectionId(@Param("testPlanReportId") String testPlanReportId, @Param("collectionId") String collectionId); List<String> getIdsByReportIdAndCollectionId(@Param("testPlanReportId") String testPlanReportId, @Param("collectionId") String collectionId);
} }

View File

@ -38,14 +38,31 @@
ifnull(tprac.api_case_execute_result, 'PENDING') as executeResult, tprac.api_case_execute_user as executeUser, atc.project_id projectId ifnull(tprac.api_case_execute_result, 'PENDING') as executeResult, tprac.api_case_execute_user as executeUser, atc.project_id projectId
from test_plan_report_api_case tprac left join api_test_case atc on tprac.api_case_id = atc.id from test_plan_report_api_case tprac left join api_test_case atc on tprac.api_case_id = atc.id
where tprac.test_plan_report_id = #{request.reportId} where tprac.test_plan_report_id = #{request.reportId}
<if test="request.keyword != null and request.keyword != ''">
and (tprac.api_case_num like concat('%', #{request.keyword}, '%')
or tprac.api_case_name like concat('%', #{request.keyword}, '%'))
</if>
<if test="request.collectionId != null and request.collectionId != ''">
and tprac.test_plan_collection_id = #{request.collectionId}
</if>
<include refid="filter"/> <include refid="filter"/>
</select> </select>
<select id="listCollection" resultType="io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse">
select tpc.id as id, tpc.name as name, tp.name as planName, count(tprac.id) as count
from test_plan_report_api_case tprac
join test_plan_collection tpc on tprac.test_plan_collection_id = tpc.id and tpc.type = 'API'
join test_plan tp on tpc.test_plan_id = tp.id
where tprac.test_plan_report_id = #{request.reportId}
group by tprac.test_plan_collection_id
</select>
<select id="getIdsByReportIdAndCollectionId" resultType="java.lang.String"> <select id="getIdsByReportIdAndCollectionId" resultType="java.lang.String">
select id from test_plan_report_api_case select id from test_plan_report_api_case
where test_plan_report_id = #{testPlanReportId} and test_plan_collection_id = #{collectionId} where test_plan_report_id = #{testPlanReportId} and test_plan_collection_id = #{collectionId}
order by pos desc order by pos desc
</select> </select>
<select id="getPlanExecuteCasesId" resultType="java.lang.String"> <select id="getPlanExecuteCasesId" resultType="java.lang.String">
select tpac.id select tpac.id
from test_plan_api_case tpac from test_plan_api_case tpac

View File

@ -5,6 +5,7 @@ import io.metersphere.plan.dto.CaseStatusCountMap;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
import io.metersphere.plan.dto.TestPlanBaseModule; import io.metersphere.plan.dto.TestPlanBaseModule;
import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest; import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@ -41,4 +42,11 @@ public interface ExtTestPlanReportApiScenarioMapper {
* @return 关联的用例集合 * @return 关联的用例集合
*/ */
List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request); List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request);
/**
* 分页查询报告关联的测试集(场景)
* @param request 请求参数
* @return 关联的测试集集合
*/
List<TestPlanReportDetailCollectionResponse> listCollection(@Param("request") TestPlanReportDetailPageRequest request);
} }

View File

@ -37,8 +37,25 @@
ifnull(tpras.api_scenario_execute_result, 'PENDING') as executeResult, tpras.api_scenario_execute_user as executeUser, aso.project_id projectId ifnull(tpras.api_scenario_execute_result, 'PENDING') as executeResult, tpras.api_scenario_execute_user as executeUser, aso.project_id projectId
from test_plan_report_api_scenario tpras left join api_scenario aso on tpras.api_scenario_id = aso.id from test_plan_report_api_scenario tpras left join api_scenario aso on tpras.api_scenario_id = aso.id
where tpras.test_plan_report_id = #{request.reportId} where tpras.test_plan_report_id = #{request.reportId}
<if test="request.keyword != null and request.keyword != ''">
and (tpras.api_scenario_num like concat('%', #{request.keyword}, '%')
or tpras.api_scenario_name like concat('%', #{request.keyword}, '%'))
</if>
<if test="request.collectionId != null and request.collectionId != ''">
and tpras.test_plan_collection_id = #{request.collectionId}
</if>
<include refid="filter"/> <include refid="filter"/>
</select> </select>
<select id="listCollection" resultType="io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse">
select tpc.id as id, tpc.name as name, tp.name as planName, count(tpras.id) as count
from test_plan_report_api_scenario tpras
join test_plan_collection tpc on tpras.test_plan_collection_id = tpc.id and tpc.type = 'SCENARIO'
join test_plan tp on tpc.test_plan_id = tp.id
where tpras.test_plan_report_id = #{request.reportId}
group by tpras.test_plan_collection_id
</select>
<select id="getPlanExecuteCasesId" resultType="java.lang.String"> <select id="getPlanExecuteCasesId" resultType="java.lang.String">
select tpas.id select tpas.id
from test_plan_api_scenario tpas from test_plan_api_scenario tpas

View File

@ -27,6 +27,9 @@
ifnull(tprb.bug_case_count, 0) as relationCaseCount ifnull(tprb.bug_case_count, 0) as relationCaseCount
from test_plan_report_bug tprb from test_plan_report_bug tprb
where tprb.test_plan_report_id = #{request.reportId} where tprb.test_plan_report_id = #{request.reportId}
<if test="request.keyword != null and request.keyword != ''">
and (tprb.bug_num like concat('%', #{request.keyword}, '%') or tprb.bug_title like concat('%', #{request.keyword}, '%'))
</if>
</select> </select>
<select id="countBug" resultType="io.metersphere.plan.dto.ReportBugSumDTO"> <select id="countBug" resultType="io.metersphere.plan.dto.ReportBugSumDTO">

View File

@ -5,6 +5,7 @@ import io.metersphere.plan.dto.CaseStatusCountMap;
import io.metersphere.plan.dto.ReportDetailCasePageDTO; import io.metersphere.plan.dto.ReportDetailCasePageDTO;
import io.metersphere.plan.dto.TestPlanBaseModule; import io.metersphere.plan.dto.TestPlanBaseModule;
import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest; import io.metersphere.plan.dto.request.TestPlanReportDetailPageRequest;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.plugin.platform.dto.SelectOption;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -54,5 +55,12 @@ public interface ExtTestPlanReportFunctionalCaseMapper {
*/ */
List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request); List<ReportDetailCasePageDTO> list(@Param("request") TestPlanReportDetailPageRequest request);
/**
* 分页查询报告关联的测试集(功能)
* @param request 请求参数
* @return 关联的测试集集合
*/
List<TestPlanReportDetailCollectionResponse> listCollection(@Param("request") TestPlanReportDetailPageRequest request);
List<String> getPlanExecuteCasesId(@Param("id") String testPlanId); List<String> getPlanExecuteCasesId(@Param("id") String testPlanId);
} }

View File

@ -58,8 +58,25 @@
from test_plan_report_function_case tprfc from test_plan_report_function_case tprfc
left join functional_case fc on fc.id = tprfc.function_case_id left join functional_case fc on fc.id = tprfc.function_case_id
where tprfc.test_plan_report_id = #{request.reportId} where tprfc.test_plan_report_id = #{request.reportId}
<if test="request.keyword != null and request.keyword != ''">
and (tprfc.function_case_num like concat('%', #{request.keyword}, '%')
or tprfc.function_case_name like concat('%', #{request.keyword}, '%'))
</if>
<if test="request.collectionId != null and request.collectionId != ''">
and tprfc.test_plan_collection_id = #{request.collectionId}
</if>
<include refid="filter"/> <include refid="filter"/>
</select> </select>
<select id="listCollection" resultType="io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse">
select tpc.id as id, tpc.name as name, tp.name as planName, count(tprfc.id) as count
from test_plan_report_function_case tprfc
join test_plan_collection tpc on tprfc.test_plan_collection_id = tpc.id and tpc.type = 'FUNCTIONAL'
join test_plan tp on tpc.test_plan_id = tp.id
where tprfc.test_plan_report_id = #{request.reportId}
group by tprfc.test_plan_collection_id
</select>
<select id="getPlanExecuteCasesId" resultType="java.lang.String"> <select id="getPlanExecuteCasesId" resultType="java.lang.String">
select tpfc.id select tpfc.id
from test_plan_functional_case tpfc from test_plan_functional_case tpfc

View File

@ -281,6 +281,9 @@
<if test="request.reportId != null and request.reportId != ''"> <if test="request.reportId != null and request.reportId != ''">
and tpr.parent_id = #{request.reportId} and tpr.integrated = false and tpr.parent_id = #{request.reportId} and tpr.integrated = false
</if> </if>
<if test="request.keyword != null and request.keyword != ''">
and tpr.name like concat('%', #{request.keyword},'%')
</if>
<include refid="filter"/> <include refid="filter"/>
<include refid="combine"> <include refid="combine">
<property name="condition" value="request.combine"/> <property name="condition" value="request.combine"/>

View File

@ -4,10 +4,12 @@ import com.google.common.collect.Maps;
import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.bug.service.BugCommonService; import io.metersphere.bug.service.BugCommonService;
import io.metersphere.plan.constants.AssociateCaseType; import io.metersphere.plan.constants.AssociateCaseType;
import io.metersphere.plan.constants.CollectionQueryType;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.*; import io.metersphere.plan.dto.*;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse; import io.metersphere.plan.dto.response.TestPlanCaseExecHistoryResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailCollectionResponse;
import io.metersphere.plan.dto.response.TestPlanReportDetailResponse; import io.metersphere.plan.dto.response.TestPlanReportDetailResponse;
import io.metersphere.plan.dto.response.TestPlanReportPageResponse; import io.metersphere.plan.dto.response.TestPlanReportPageResponse;
import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType; import io.metersphere.plan.enums.TestPlanReportAttachmentSourceType;
@ -768,7 +770,8 @@ public class TestPlanReportService {
case AssociateCaseType.API_SCENARIO -> detailCases = extTestPlanReportApiScenarioMapper.list(request); case AssociateCaseType.API_SCENARIO -> detailCases = extTestPlanReportApiScenarioMapper.list(request);
default -> detailCases = new ArrayList<>(); default -> detailCases = new ArrayList<>();
} }
List<String> distinctUserIds = detailCases.stream().map(ReportDetailCasePageDTO::getExecuteUser).distinct().toList(); List<String> distinctUserIds = detailCases.stream().map(ReportDetailCasePageDTO::getExecuteUser).distinct().collect(Collectors.toList());
distinctUserIds.removeIf(StringUtils::isEmpty);
Map<String, String> userMap = getUserMap(distinctUserIds); Map<String, String> userMap = getUserMap(distinctUserIds);
detailCases.forEach(detailCase -> detailCase.setExecuteUser(userMap.getOrDefault(detailCase.getExecuteUser(), detailCase.getExecuteUser()))); detailCases.forEach(detailCase -> detailCase.setExecuteUser(userMap.getOrDefault(detailCase.getExecuteUser(), detailCase.getExecuteUser())));
return detailCases; return detailCases;
@ -1181,6 +1184,23 @@ public class TestPlanReportService {
return extTestPlanReportMapper.getPlanReportListById(request); return extTestPlanReportMapper.getPlanReportListById(request);
} }
/**
* 计划报告测试集列表
* @param request 请求参数
* @param caseType 用例类型
* @return 测试集列表
*/
public List<TestPlanReportDetailCollectionResponse> listReportCollection(TestPlanReportDetailPageRequest request, String caseType) {
List<TestPlanReportDetailCollectionResponse> collections;
switch (caseType) {
case CollectionQueryType.FUNCTIONAL -> collections = extTestPlanReportFunctionalCaseMapper.listCollection(request);
case CollectionQueryType.API -> collections = extTestPlanReportApiCaseMapper.listCollection(request);
case CollectionQueryType.SCENARIO -> collections = extTestPlanReportApiScenarioMapper.listCollection(request);
default -> collections = new ArrayList<>();
}
return collections;
}
/** /**
* 预览富文本文件 * 预览富文本文件
* *

View File

@ -1,5 +1,6 @@
package io.metersphere.plan.controller; package io.metersphere.plan.controller;
import io.metersphere.plan.constants.CollectionQueryType;
import io.metersphere.plan.domain.*; import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.TestPlanShareInfo; import io.metersphere.plan.dto.TestPlanShareInfo;
import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.request.*;
@ -57,6 +58,7 @@ public class TestPlanReportControllerTests extends BaseTest {
private static final String GET_PLAN_REPORT_DETAIL_API_PAGE = "/test-plan/report/detail/api/case/page"; private static final String GET_PLAN_REPORT_DETAIL_API_PAGE = "/test-plan/report/detail/api/case/page";
private static final String GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE = "/test-plan/report/detail/scenario/case/page"; private static final String GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE = "/test-plan/report/detail/scenario/case/page";
private static final String GET_PLAN_REPORT_DETAIL_PLAN_PAGE = "/test-plan/report/detail/plan/report/page"; private static final String GET_PLAN_REPORT_DETAIL_PLAN_PAGE = "/test-plan/report/detail/plan/report/page";
private static final String GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE = "/test-plan/report/detail/{1}/collection/page";
private static final String GEN_AND_SHARE = "/test-plan/report/share/gen"; private static final String GEN_AND_SHARE = "/test-plan/report/share/gen";
private static final String GET_SHARE_INFO = "/test-plan/report/share/get"; private static final String GET_SHARE_INFO = "/test-plan/report/share/get";
private static final String GET_SHARE_REPORT_LAYOUT = "/test-plan/report/share/get-layout"; private static final String GET_SHARE_REPORT_LAYOUT = "/test-plan/report/share/get-layout";
@ -315,13 +317,20 @@ public class TestPlanReportControllerTests extends BaseTest {
request.setPageSize(10); request.setPageSize(10);
request.setReportId(GEN_REPORT_ID); request.setReportId(GEN_REPORT_ID);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.FUNCTIONAL);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_API_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_API_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.API);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.SCENARIO);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_PLAN_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_PLAN_PAGE, request);
request.setSort(Map.of("num", "asc")); request.setSort(Map.of("num", "asc"));
request.setStartPager(false);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_FUNCTIONAL_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.FUNCTIONAL);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_API_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_API_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.API);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE, request); this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_SCENARIO_PAGE, request);
this.requestPostWithOk(GET_PLAN_REPORT_DETAIL_COLLECTION_PAGE, request, CollectionQueryType.SCENARIO);
} }
@Test @Test