refactor(测试计划): 优化测试计划组生成明细和明细查询的逻辑

--bug=1048250 --user=宋昌昌 【测试计划】计划组-执行历史-查看停止执行结果-报告未展示用例明细 https://www.tapd.cn/55049933/s/1611441
This commit is contained in:
song-cc-rock 2024-11-18 17:09:49 +08:00 committed by Craftsman
parent 4e4a68b11a
commit 54269d69b4
22 changed files with 397 additions and 314 deletions

View File

@ -25,6 +25,7 @@ api_management.unPassCount=未通过
bug_management.retentionRate=遗留率
bug_management.totalCount=缺陷总数
bug_management.retentionCount=遗留缺陷数
get_platform_end_status_error=获取平台结束状态失败

View File

@ -25,3 +25,4 @@ api_management.unPassCount=Un pass
bug_management.retentionRate=Retention rate
bug_management.totalCount=Bug total count
bug_management.retentionCount=Retention count
get_platform_end_status_error=Failed to get the end status of the platform

View File

@ -26,3 +26,4 @@ api_management.unPassCount=未通过
bug_management.retentionRate=遗留率
bug_management.totalCount=缺陷总数
bug_management.retentionCount=遗留缺陷数
get_platform_end_status_error=获取平台结束状态失败

View File

@ -136,5 +136,5 @@ public interface ExtBugMapper {
*/
List<ProjectUserStatusCountDTO> projectUserBugStatusCount(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime, @Param("userIds") List<String> userIds, @Param("platforms") Set<String> platforms);
List<Bug>getSimpleList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime,@Param("handleUser") String handleUser,@Param("createUser") String createUser, @Param("platforms") Set<String> platforms);
List<Bug>getSimpleList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime,@Param("handleUser") String handleUser,@Param("createUser") String createUser, @Param("platforms") Set<String> platforms, @Param("currentUser") String currentUser);
}

View File

@ -457,8 +457,16 @@
<if test="startTime != null and endTime != null">
AND bug.create_time BETWEEN #{startTime} AND #{endTime}
</if>
<if test="handleUser != null and handleUser != ''">
AND bug.handle_user = #{handleUser}
<if test="handleUser != null or currentUser != null">
AND (
FALSE
<if test="handleUser != null and handleUser != ''">
OR bug.handle_user = #{handleUser}
</if>
<if test="currentUser != null and currentUser != ''">
OR bug.handle_user = #{currentUser}
</if>
)
</if>
<if test="createUser != null and createUser != ''">
AND bug.create_user = #{createUser}

View File

@ -25,6 +25,7 @@ import io.metersphere.system.domain.ServiceIntegration;
import io.metersphere.system.service.FileService;
import io.metersphere.system.service.PlatformPluginService;
import io.metersphere.system.service.PluginLoadService;
import io.metersphere.system.service.UserPlatformAccountService;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
@ -34,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@ -68,6 +70,8 @@ public class BugCommonService {
private BugLocalAttachmentMapper bugLocalAttachmentMapper;
@Resource
private ProjectApplicationService projectApplicationService;
@Resource
private UserPlatformAccountService userPlatformAccountService;
/**
* 获取表头处理人选项
@ -211,4 +215,26 @@ public class BugCommonService {
List<SelectOption> platformLastSteps = platform.getStatusTransitionsLastSteps(projectApplicationService.getProjectBugThirdPartConfig(projectId));
return platformLastSteps.stream().map(SelectOption::getValue).toList();
}
/**
* 获取登录用户平台处理人
* @param projectId 项目ID
* @param currentUserId 当前用户ID
* @param currentOrgId 当前组织ID
* @return 平台处理人
*/
public String getPlatformHandlerUser(String projectId, String currentUserId, String currentOrgId) {
ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(projectId, true);
String platformUserName = userPlatformAccountService.getPlatformUserName(currentUserId, currentOrgId, serviceIntegration.getPluginId());
List<SelectOption> headerHandlerOption = getHeaderHandlerOption(projectId);
if (StringUtils.isNotBlank(platformUserName)) {
Optional<SelectOption> handleOption = headerHandlerOption.stream().filter(option -> StringUtils.containsAnyIgnoreCase(option.getText(), platformUserName))
.findFirst();
if (handleOption.isPresent()) {
return handleOption.get().getValue();
}
return platformUserName;
}
return null;
}
}

View File

@ -3,6 +3,7 @@ package io.metersphere.dashboard.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiDefinitionUpdateDTO;
import io.metersphere.bug.service.BugCommonService;
import io.metersphere.dashboard.dto.LayoutDTO;
import io.metersphere.dashboard.request.DashboardFrontPageRequest;
import io.metersphere.dashboard.response.OverViewCountDTO;
@ -35,6 +36,8 @@ public class DashboardController {
@Resource
private DashboardService dashboardService;
@Resource
private BugCommonService bugCommonService;
@Resource
private PermissionCheckService permissionCheckService;
@PostMapping("/layout/edit/{organizationId}")
@ -133,21 +136,22 @@ public class DashboardController {
@Operation(summary = "缺陷数量统计")
@CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization")
public StatisticsDTO projectBugCount(@Validated @RequestBody DashboardFrontPageRequest request) {
return dashboardService.projectBugCount(request, SessionUtils.getUserId());
return dashboardService.projectBugCount(request, SessionUtils.getUserId(), null);
}
@PostMapping("/create_bug_by_me")
@Operation(summary = "我创建的缺陷")
@CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization")
public StatisticsDTO projectBugCountCreateByMe(@Validated @RequestBody DashboardFrontPageRequest request) {
return dashboardService.projectBugCountCreateByMe(request, SessionUtils.getUserId());
return dashboardService.projectBugCountCreateByMe(request, SessionUtils.getUserId(), null);
}
@PostMapping("/handle_bug_by_me")
@Operation(summary = "待我处理的缺陷")
@CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization")
public StatisticsDTO projectBugCountHandleByMe(@Validated @RequestBody DashboardFrontPageRequest request) {
return dashboardService.projectBugCountHandleByMe(request, SessionUtils.getUserId());
String platformHandlerUser = bugCommonService.getPlatformHandlerUser(request.getProjectIds().getFirst(), SessionUtils.getUserId(), SessionUtils.getCurrentOrganizationId());
return dashboardService.projectBugCountHandleByMe(request, SessionUtils.getUserId(), platformHandlerUser);
}
@PostMapping("/plan_legacy_bug")

View File

@ -16,10 +16,8 @@ import io.metersphere.plan.dto.response.TestPlanResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
import io.metersphere.plan.service.TestPlanManagementService;
import io.metersphere.plan.service.TestPlanStatisticsService;
import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.project.service.ProjectApplicationService;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.domain.ServiceIntegration;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.service.UserPlatformAccountService;
import io.metersphere.system.utils.PageUtils;
@ -118,15 +116,8 @@ public class ToDoController {
return todoParam;
}
todoParam.setCurrentPlatform(platformName);
List<String> platformLastStepStatus = bugCommonService.getPlatformLastStepStatus(request.getProjectId());
todoParam.setPlatformLastStatus(platformLastStepStatus);
ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(request.getProjectId(), true);
String platformUserName = userPlatformAccountService.getPlatformUserName(currentUserId, currentOrgId, serviceIntegration.getPluginId());
List<SelectOption> headerHandlerOption = bugCommonService.getHeaderHandlerOption(request.getProjectId());
if (StringUtils.isNotBlank(platformUserName)) {
headerHandlerOption.stream().filter(option -> StringUtils.containsAnyIgnoreCase(option.getText(), platformUserName))
.findFirst().ifPresent(option -> todoParam.setPlatformUser(option.getValue()));
}
todoParam.setPlatformLastStatus(bugCommonService.getPlatformLastStepStatus(request.getProjectId()));
todoParam.setPlatformUser(bugCommonService.getPlatformHandlerUser(request.getProjectId(), currentUserId, currentOrgId));
} catch (Exception e) {
// 设置平台参数异常时, 无法正常过滤平台非结束的缺陷
LogUtils.error(e.getMessage());

View File

@ -52,6 +52,7 @@ import io.metersphere.sdk.constants.TestPlanConstants;
import io.metersphere.sdk.dto.CombineCondition;
import io.metersphere.sdk.dto.CombineSearch;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.UserLayout;
import io.metersphere.system.domain.UserLayoutExample;
@ -1234,17 +1235,18 @@ public class DashboardService {
return statisticsDTO;
}
public StatisticsDTO baseProjectBugCount(DashboardFrontPageRequest request, String userId, Boolean hasHandleUser, Boolean hasCreateUser) {
public StatisticsDTO baseProjectBugCount(DashboardFrontPageRequest request, String userId, String handleUserId, Boolean hasHandleUser, Boolean hasCreateUser) {
String projectId = request.getProjectIds().getFirst();
StatisticsDTO statisticsDTO = new StatisticsDTO();
if (Boolean.FALSE.equals(permissionCheckService.checkModule(projectId, BUG_MODULE, userId, PermissionConstants.PROJECT_BUG_READ))) {
statisticsDTO.setErrorCode(NO_PROJECT_PERMISSION.getCode());
return statisticsDTO;
}
String handleUser = hasHandleUser ? userId : null;
String localHandleUser = hasHandleUser ? userId : null;
String handleUser = hasHandleUser ? handleUserId : null;
String createUser = hasCreateUser ? userId : null;
Set<String> platforms = getPlatforms(projectId);
List<Bug> allSimpleList = extBugMapper.getSimpleList(projectId, null, null, handleUser, createUser, platforms);
List<Bug> allSimpleList = extBugMapper.getSimpleList(projectId, null, null, handleUser, createUser, platforms, localHandleUser);
List<String> localLastStepStatus = getBugEndStatus(projectId);
List<Bug> statusList = allSimpleList.stream().filter(t -> !localLastStepStatus.contains(t.getStatus())).toList();
int statusSize = CollectionUtils.isEmpty(statusList) ? 0 : statusList.size();
@ -1263,11 +1265,17 @@ public class DashboardService {
@NotNull
private List<String> getBugEndStatus(String projectId) {
List<String> localLastStepStatus = bugCommonService.getLocalLastStepStatus(projectId);
String platformName = projectApplicationService.getPlatformName(projectId);
if (StringUtils.equalsIgnoreCase(platformName, BugPlatform.LOCAL.getName())) {
return localLastStepStatus;
}
// 项目对接三方平台
List<String> platformLastStepStatus = new ArrayList<>();
try {
platformLastStepStatus = bugCommonService.getPlatformLastStepStatus(projectId);
} catch (Exception e) {
throw new RuntimeException(e);
// 获取三方平台结束状态失败, 只过滤本地结束状态
LogUtils.error(Translator.get("get_platform_end_status_error"));
}
localLastStepStatus.addAll(platformLastStepStatus);
return localLastStepStatus;
@ -1312,16 +1320,16 @@ public class DashboardService {
return statusPercentList;
}
public StatisticsDTO projectBugCount(DashboardFrontPageRequest request, String userId) {
return baseProjectBugCount(request, userId, false, false);
public StatisticsDTO projectBugCount(DashboardFrontPageRequest request, String userId, String handlerUser) {
return baseProjectBugCount(request, userId, handlerUser, false, false);
}
public StatisticsDTO projectBugCountCreateByMe(DashboardFrontPageRequest request, String userId) {
return baseProjectBugCount(request, userId, false, true);
public StatisticsDTO projectBugCountCreateByMe(DashboardFrontPageRequest request, String userId, String handlerUser) {
return baseProjectBugCount(request, userId, handlerUser, false, true);
}
public StatisticsDTO projectBugCountHandleByMe(DashboardFrontPageRequest request, String userId) {
return baseProjectBugCount(request, userId, true, false);
public StatisticsDTO projectBugCountHandleByMe(DashboardFrontPageRequest request, String userId, String handlerUser) {
return baseProjectBugCount(request, userId, handlerUser, true, false);
}
public StatisticsDTO projectPlanLegacyBug(DashboardFrontPageRequest request, String userId) {

View File

@ -163,6 +163,7 @@ public class TestPlanReportController {
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReportId()", resourceType = "test_plan_report")
public Pager<List<BugDTO>> pageBug(@Validated @RequestBody TestPlanReportDetailPageRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tprb.bug_num, tprb.id desc");
if (!request.getStartPager()) {
@ -177,6 +178,7 @@ public class TestPlanReportController {
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReportId()", resourceType = "test_plan_report")
public Pager<List<ReportDetailCasePageDTO>> pageFunctionalCase(@Validated @RequestBody TestPlanReportDetailPageRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tprfc.pos desc");
if (!request.getStartPager()) {
@ -201,6 +203,7 @@ public class TestPlanReportController {
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReportId()", resourceType = "test_plan_report")
public Pager<List<ReportDetailCasePageDTO>> pageApiCase(@Validated @RequestBody TestPlanReportDetailPageRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tprac.pos desc");
if (!request.getStartPager()) {
@ -218,6 +221,7 @@ public class TestPlanReportController {
@RequiresPermissions(value = {PermissionConstants.TEST_PLAN_REPORT_READ, PermissionConstants.TEST_PLAN_READ_EXECUTE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReportId()", resourceType = "test_plan_report")
public Pager<List<ReportDetailCasePageDTO>> pageScenarioCase(@Validated @RequestBody TestPlanReportDetailPageRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tpras.pos desc");
if (!request.getStartPager()) {
@ -249,7 +253,6 @@ public class TestPlanReportController {
return testPlanReportService.previewMd(projectId, fileId, compressed);
}
@PostMapping("/export/{reportId}")
@Operation(summary = "测试计划-报告-导出日志")
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_EXPORT)
@ -264,13 +267,13 @@ public class TestPlanReportController {
testPlanReportService.batchExportLog(request, SessionUtils.getUserId(), SessionUtils.getCurrentProjectId());
}
@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) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
// 默认按照测试集的位序升序
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), "tpc.pos asc");
return PageUtils.setPageInfo(page, testPlanReportService.listReportCollection(request, type));

View File

@ -94,6 +94,7 @@ public class TestPlanReportShareController {
@PostMapping("/detail/bug/page")
@Operation(summary = "测试计划-报告-详情-缺陷分页查询")
public Pager<List<BugDTO>> pageBug(@Validated @RequestBody TestPlanShareReportDetailRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
ShareInfo shareInfo = testPlanReportShareService.checkResource(request.getShareId());
testPlanReportShareService.validateExpired(shareInfo);
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
@ -104,6 +105,7 @@ public class TestPlanReportShareController {
@PostMapping("/detail/functional/case/page")
@Operation(summary = "测试计划-报告-详情-功能用例分页查询")
public Pager<List<ReportDetailCasePageDTO>> pageFunctionalCase(@Validated @RequestBody TestPlanShareReportDetailRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
ShareInfo shareInfo = testPlanReportShareService.checkResource(request.getShareId());
testPlanReportShareService.validateExpired(shareInfo);
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
@ -114,6 +116,7 @@ public class TestPlanReportShareController {
@PostMapping("/detail/api/case/page")
@Operation(summary = "测试计划-报告-详情-接口用例分页查询")
public Pager<List<ReportDetailCasePageDTO>> pageApiCase(@Validated @RequestBody TestPlanShareReportDetailRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
ShareInfo shareInfo = testPlanReportShareService.checkResource(request.getShareId());
testPlanReportShareService.validateExpired(shareInfo);
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
@ -124,6 +127,7 @@ public class TestPlanReportShareController {
@PostMapping("/detail/scenario/case/page")
@Operation(summary = "测试计划-报告-详情-场景用例分页查询")
public Pager<List<ReportDetailCasePageDTO>> pageScenarioCase(@Validated @RequestBody TestPlanShareReportDetailRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
ShareInfo shareInfo = testPlanReportShareService.checkResource(request.getShareId());
testPlanReportShareService.validateExpired(shareInfo);
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
@ -189,6 +193,7 @@ public class TestPlanReportShareController {
@Operation(summary = "测试计划-报告-详情-测试集分页查询(不同用例类型)")
@Parameter(name = "type", description = "用例类型", schema = @Schema(requiredMode = Schema.RequiredMode.REQUIRED), example = "functional, api, scenario")
public Pager<List<TestPlanReportDetailCollectionResponse>> collectionPage(@PathVariable String type, @Validated @RequestBody TestPlanShareReportDetailRequest request) {
request.setDetailReportIds(testPlanReportService.getActualReportIds(request.getReportId()));
ShareInfo shareInfo = testPlanReportShareService.checkResource(request.getShareId());
testPlanReportShareService.validateExpired(shareInfo);
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),

View File

@ -6,6 +6,11 @@ import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author song-cc-rock
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TestPlanReportDetailPageRequest extends BasePageRequest {
@ -19,4 +24,7 @@ public class TestPlanReportDetailPageRequest extends BasePageRequest {
@Schema(description = "测试集ID")
private String collectionId;
@Schema(description = "报告ID集合")
private List<String> detailReportIds;
}

View File

@ -14,10 +14,10 @@ public interface ExtTestPlanReportApiCaseMapper {
/**
* 统计报告中接口用例执行情况
* @param reportId 报告ID
* @param reportIds 报告ID集合
* @return 用例数量
*/
List<CaseStatusCountMap> countExecuteResult(@Param("id") String reportId);
List<CaseStatusCountMap> countExecuteResult(@Param("ids") List<String> reportIds);
/**
* 获取计划关联的接口用例

View File

@ -3,7 +3,10 @@
<mapper namespace="io.metersphere.plan.mapper.ExtTestPlanReportApiCaseMapper">
<select id="countExecuteResult" resultType="io.metersphere.plan.dto.CaseStatusCountMap">
select ifnull(tprac.api_case_execute_result, 'PENDING') as status, count(id) as count from test_plan_report_api_case tprac
where tprac.test_plan_report_id = #{id}
where tprac.test_plan_report_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
group by tprac.api_case_execute_result
</select>
@ -38,7 +41,10 @@
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
left join test_plan_collection tpc on tprac.test_plan_collection_id = tpc.id
where tprac.test_plan_report_id = #{request.reportId}
where tprac.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<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}, '%')
@ -55,7 +61,10 @@
from test_plan_report_api_case tprac
right 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}
where tprac.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<if test="request.keyword != null and request.keyword != ''">
and (tprac.api_case_name like concat('%', #{request.keyword}, '%')
or tprac.api_case_num like concat('%', #{request.keyword}, '%')

View File

@ -14,10 +14,10 @@ public interface ExtTestPlanReportApiScenarioMapper {
/**
* 统计报告中场景用例执行情况
* @param reportId 报告ID
* @param reportIds 报告ID集合
* @return 用例数量
*/
List<CaseStatusCountMap> countExecuteResult(@Param("id") String reportId);
List<CaseStatusCountMap> countExecuteResult(@Param("ids") List<String> reportIds);
/**
* 获取计划关联的场景用例

View File

@ -3,7 +3,10 @@
<mapper namespace="io.metersphere.plan.mapper.ExtTestPlanReportApiScenarioMapper">
<select id="countExecuteResult" resultType="io.metersphere.plan.dto.CaseStatusCountMap">
select ifnull(tpras.api_scenario_execute_result, 'PENDING') as status, count(id) as count from test_plan_report_api_scenario tpras
where tpras.test_plan_report_id = #{id}
where tpras.test_plan_report_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
group by tpras.api_scenario_execute_result
</select>
@ -37,7 +40,10 @@
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
left join test_plan_collection tpc on tpras.test_plan_collection_id = tpc.id
where tpras.test_plan_report_id = #{request.reportId}
where tpras.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<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}, '%')
@ -54,7 +60,10 @@
from test_plan_report_api_scenario tpras
right 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}
where tpras.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<if test="request.keyword != null and request.keyword != ''">
and (tpras.api_scenario_name like concat('%', #{request.keyword}, '%')
or tpras.api_scenario_num like concat('%', #{request.keyword}, '%')

View File

@ -26,7 +26,10 @@
select distinct tprb.bug_id as id, tprb.bug_num as num, tprb.bug_title as title, tprb.bug_status as status, tprb.bug_status as statusName, tprb.bug_handle_user as handleUserName,
ifnull(tprb.bug_case_count, 0) as relationCaseCount
from test_plan_report_bug tprb
where tprb.test_plan_report_id = #{request.reportId}
where tprb.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<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>

View File

@ -43,10 +43,10 @@ public interface ExtTestPlanReportFunctionalCaseMapper {
/**
* 统计报告中功能用例执行情况
* @param reportId 报告ID
* @param reportIds 报告ID集合
* @return 用例数量
*/
List<CaseStatusCountMap> countExecuteResult(@Param("id") String reportId);
List<CaseStatusCountMap> countExecuteResult(@Param("ids") List<String> reportIds);
/**
* 分页查询报告关联的用例

View File

@ -45,7 +45,10 @@
<select id="countExecuteResult" resultType="io.metersphere.plan.dto.CaseStatusCountMap">
select ifnull(tprfc.function_case_execute_result, 'PENDING') as status, count(id) as count from test_plan_report_function_case tprfc
where tprfc.test_plan_report_id = #{id}
where tprfc.test_plan_report_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
group by tprfc.function_case_execute_result
</select>
@ -58,7 +61,10 @@
from test_plan_report_function_case tprfc
left join functional_case fc on fc.id = tprfc.function_case_id
left join test_plan_collection tpc on tpc.id = tprfc.test_plan_collection_id
where tprfc.test_plan_report_id = #{request.reportId}
where tprfc.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<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}, '%')
@ -75,7 +81,10 @@
from test_plan_report_function_case tprfc
right 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}
where tprfc.test_plan_report_id in
<foreach collection="request.detailReportIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
<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}, '%')

View File

@ -75,4 +75,6 @@ public interface ExtTestPlanReportMapper {
List<TestPlanReport> getChildrenReport(@Param("reportId") String reportId);
void resetRerunReport(@Param("reportId") String reportId);
List<TestPlanReport> getPlanChildrenTask(@Param("reportId") String reportId);
}

View File

@ -550,7 +550,12 @@
<select id="getChildrenReport" resultType="io.metersphere.plan.domain.TestPlanReport">
select id from test_plan_report where parent_id = #{reportId} and id != #{reportId}
select id from test_plan_report where parent_id = #{reportId} and id != #{reportId} order by test_plan_report.create_time desc
</select>
<select id="getPlanChildrenTask" resultType="io.metersphere.plan.domain.TestPlanReport">
select test_plan_report.id, test_plan.name from test_plan_report left join test_plan on test_plan_report.test_plan_id = test_plan.id
where parent_id = #{reportId} and integrated = false order by test_plan_report.create_time desc
</select>
<update id="resetRerunReport">

View File

@ -60,6 +60,9 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
/**
* @author song-cc-rock
*/
@Service
public class TestPlanReportService {
@ -126,8 +129,6 @@ public class TestPlanReportService {
@Resource
private ApiReportRelateTaskMapper apiReportRelateTaskMapper;
@Resource
private TestPlanCollectionMapper testPlanCollectionMapper;
@Resource
private ExecTaskMapper execTaskMapper;
private static final int MAX_REPORT_NAME_LENGTH = 300;
@ -337,7 +338,6 @@ public class TestPlanReportService {
return genReport(prepareReportId, taskId, request, false, currentUser, null);
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public Map<String, String> genReport(String prepareReportId, TestPlanReportGenRequest request, boolean manual, String currentUser, String manualReportName) {
return genReport(prepareReportId, null, request, manual, currentUser, manualReportName);
}
@ -464,188 +464,216 @@ public class TestPlanReportService {
*/
private TestPlanReportDetailCaseDTO genReportDetail(TestPlanReportGenPreParam genParam, TestPlanReport report) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
// 缺陷数
List<ReportBugCountDTO> bugCountList = extTestPlanReportBugMapper.countPlanBug(genParam.getTestPlanId());
Map<String, Long> bugCountMap = bugCountList.stream().collect(Collectors.toMap(ReportBugCountDTO::getRefCaseId, ReportBugCountDTO::getBugCount));
Project project = projectMapper.selectByPrimaryKey(genParam.getProjectId());
AtomicLong funcCaseCount = new AtomicLong();
AtomicLong apiCaseCount = new AtomicLong();
AtomicLong apiScenarioCount = new AtomicLong();
AtomicLong bugCount = new AtomicLong();
// 功能用例
{
List<String> funcCaseIdList = extTestPlanReportFunctionalCaseMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(funcCaseIdList)) {
SubListUtils.dealForSubList(funcCaseIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportFunctionCase> reportFunctionCases = extTestPlanReportFunctionalCaseMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
funcCaseCount.addAndGet(reportFunctionCases.size());
// 用例等级
List<String> ids = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getFunctionCaseId).distinct().toList();
List<SelectOption> options = extTestPlanReportFunctionalCaseMapper.getCasePriorityByIds(ids);
Map<String, String> casePriorityMap = options.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
// 用例模块
List<String> moduleIds = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getFunctionCaseModule).filter(Objects::nonNull).toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.FUNCTIONAL_CASE.getKey());
// 关联的功能用例最新一次执行历史
List<String> relateIds = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getTestPlanFunctionCaseId).toList();
TestPlanCaseExecuteHistoryExample example = new TestPlanCaseExecuteHistoryExample();
example.createCriteria().andTestPlanCaseIdIn(relateIds);
List<TestPlanCaseExecuteHistory> functionalExecHisList = testPlanCaseExecuteHistoryMapper.selectByExample(example);
Map<String, List<TestPlanCaseExecuteHistory>> functionalExecMap = functionalExecHisList.stream().collect(Collectors.groupingBy(TestPlanCaseExecuteHistory::getTestPlanCaseId));
for (TestPlanReportFunctionCase reportFunctionalCase : reportFunctionCases) {
reportFunctionalCase.setId(IDGenerator.nextStr());
reportFunctionalCase.setTestPlanReportId(report.getId());
reportFunctionalCase.setTestPlanName(genParam.getTestPlanName());
if (reportFunctionalCase.getFunctionCaseModule() != null) {
reportFunctionalCase.setFunctionCaseModule(moduleMap.getOrDefault(reportFunctionalCase.getFunctionCaseModule(), reportFunctionalCase.getFunctionCaseModule()));
}
reportFunctionalCase.setFunctionCasePriority(casePriorityMap.get(reportFunctionalCase.getFunctionCaseId()));
List<TestPlanCaseExecuteHistory> hisList = functionalExecMap.get(reportFunctionalCase.getTestPlanFunctionCaseId());
if (CollectionUtils.isNotEmpty(hisList)) {
Optional<String> lastExecuteHisOpt = hisList.stream().sorted(Comparator.comparing(TestPlanCaseExecuteHistory::getCreateTime).reversed()).map(TestPlanCaseExecuteHistory::getId).findFirst();
reportFunctionalCase.setFunctionCaseExecuteReportId(lastExecuteHisOpt.orElse(null));
} else {
reportFunctionalCase.setFunctionCaseExecuteReportId(null);
}
reportFunctionalCase.setFunctionCaseBugCount(bugCountMap.containsKey(reportFunctionalCase.getTestPlanFunctionCaseId()) ? bugCountMap.get(reportFunctionalCase.getTestPlanFunctionCaseId()) : 0);
}
// 插入计划功能用例关联数据 -> 报告内容
TestPlanReportFunctionCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportFunctionCaseMapper.class);
batchMapper.batchInsert(reportFunctionCases);
});
}
funcCaseIdList = null;
}
// 接口用例
{
List<String> testPlanReportApiCaseIdList = extTestPlanReportApiCaseMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(testPlanReportApiCaseIdList)) {
SubListUtils.dealForSubList(testPlanReportApiCaseIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportApiCase> reportApiCases = extTestPlanReportApiCaseMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
apiCaseCount.addAndGet(reportApiCases.size());
List<String> moduleIds = reportApiCases.stream().map(TestPlanReportApiCase::getApiCaseModule).filter(Objects::nonNull).distinct().toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.API_CASE.getKey());
for (TestPlanReportApiCase reportApiCase : reportApiCases) {
reportApiCase.setId(IDGenerator.nextStr());
reportApiCase.setTestPlanReportId(report.getId());
reportApiCase.setTestPlanName(genParam.getTestPlanName());
if (reportApiCase.getApiCaseModule() != null) {
reportApiCase.setApiCaseModule(moduleMap.getOrDefault(reportApiCase.getApiCaseModule(), reportApiCase.getApiCaseModule()));
}
// 根据不超过数据库字段最大长度压缩模块名
reportApiCase.setApiCaseModule(ServiceUtils.compressName(reportApiCase.getApiCaseModule(), 450));
if (!genParam.getUseManual()) {
// 接口执行时才更新结果
reportApiCase.setApiCaseExecuteResult(null);
reportApiCase.setApiCaseExecuteUser(null);
}
reportApiCase.setApiCaseBugCount(bugCountMap.containsKey(reportApiCase.getTestPlanApiCaseId()) ? bugCountMap.get(reportApiCase.getTestPlanApiCaseId()) : 0);
}
// 插入计划接口用例关联数据 -> 报告内容
TestPlanReportApiCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportApiCaseMapper.class);
batchMapper.batchInsert(reportApiCases);
sqlSession.flushStatements();
if (StringUtils.isNotBlank(genParam.getTaskId())) {
reportApiCases.sort(Comparator.comparing(TestPlanReportApiCase::getPos).reversed());
initApiCaseExecTaskItem(genParam.getTaskId(), genParam.getTestPlanId(), reportApiCases, report.getCreateUser(), project);
}
});
}
testPlanReportApiCaseIdList = null;
}
// 场景用例
{
List<String> reportApiScenarioIdList = extTestPlanReportApiScenarioMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(reportApiScenarioIdList)) {
SubListUtils.dealForSubList(reportApiScenarioIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportApiScenario> reportApiScenarios = extTestPlanReportApiScenarioMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
apiScenarioCount.addAndGet(reportApiScenarios.size());
List<String> moduleIds = reportApiScenarios.stream().map(TestPlanReportApiScenario::getApiScenarioModule).filter(Objects::nonNull).distinct().toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.SCENARIO_CASE.getKey());
for (TestPlanReportApiScenario reportApiScenario : reportApiScenarios) {
reportApiScenario.setId(IDGenerator.nextStr());
reportApiScenario.setTestPlanReportId(report.getId());
reportApiScenario.setTestPlanName(genParam.getTestPlanName());
if (reportApiScenario.getApiScenarioModule() != null) {
reportApiScenario.setApiScenarioModule(moduleMap.getOrDefault(reportApiScenario.getApiScenarioModule(), reportApiScenario.getApiScenarioModule()));
}
// 根据不超过数据库字段最大长度压缩模块名
reportApiScenario.setApiScenarioModule(ServiceUtils.compressName(reportApiScenario.getApiScenarioModule(), 450));
if (!genParam.getUseManual()) {
// 接口执行时才更新结果
reportApiScenario.setApiScenarioExecuteResult(null);
reportApiScenario.setApiScenarioExecuteUser(null);
}
reportApiScenario.setApiScenarioBugCount(bugCountMap.containsKey(reportApiScenario.getTestPlanApiScenarioId()) ? bugCountMap.get(reportApiScenario.getTestPlanApiScenarioId()) : 0);
}
// 插入计划场景用例关联数据 -> 报告内容
TestPlanReportApiScenarioMapper batchMapper = sqlSession.getMapper(TestPlanReportApiScenarioMapper.class);
batchMapper.batchInsert(reportApiScenarios);
sqlSession.flushStatements();
if (StringUtils.isNotBlank(genParam.getTaskId())) {
reportApiScenarios.sort(Comparator.comparing(TestPlanReportApiScenario::getPos).reversed());
initScenarioExecTaskItem(genParam.getTaskId(), genParam.getTestPlanId(), reportApiScenarios, report.getCreateUser(), project);
}
});
}
reportApiScenarioIdList = null;
}
// 计划报告缺陷内容
{
List<String> reportBugIdList = extTestPlanReportBugMapper.getPlanBugsId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(reportBugIdList)) {
SubListUtils.dealForSubList(reportBugIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportBug> reportBugs = extTestPlanReportBugMapper.getPlanBugs(genParam.getTestPlanId(), subList);
bugCount.addAndGet(reportBugs.size());
// MS处理人会与第三方的值冲突, 分开查询
List<SelectOption> headerOptions = bugCommonService.getHeaderHandlerOption(genParam.getProjectId());
Map<String, String> headerHandleUserMap = headerOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
List<SelectOption> localOptions = bugCommonService.getLocalHandlerOption(genParam.getProjectId());
Map<String, String> localHandleUserMap = localOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
Map<String, String> allStatusMap = bugCommonService.getAllStatusMap(genParam.getProjectId());
reportBugs.forEach(reportBug -> {
reportBug.setId(IDGenerator.nextStr());
reportBug.setTestPlanReportId(report.getId());
reportBug.setBugHandleUser(headerHandleUserMap.containsKey(reportBug.getBugHandleUser()) ?
headerHandleUserMap.get(reportBug.getBugHandleUser()) : localHandleUserMap.get(reportBug.getBugHandleUser()));
reportBug.setBugStatus(allStatusMap.get(reportBug.getBugStatus()));
});
// 插入计划关联用例缺陷数据(去重) -> 报告内容
TestPlanReportBugMapper batchMapper = sqlSession.getMapper(TestPlanReportBugMapper.class);
batchMapper.batchInsert(reportBugs);
sqlSession.flushStatements();
});
}
}
long beforeFlush = System.currentTimeMillis();
sqlSession.flushStatements();
long funcCount = initReportFunctionCase(sqlSession, genParam, report, bugCountMap);
long apiCount = initReportApiCase(sqlSession, genParam, report, project, bugCountMap);
long scenarioCount = initReportApiScenario(sqlSession, genParam, report, project, bugCountMap);
long bugCount = initReportBug(sqlSession, genParam, report);
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
return TestPlanReportDetailCaseDTO.builder()
.functionCaseCount(funcCaseCount.get()).apiCaseCount(apiCaseCount.get()).apiScenarioCount(apiScenarioCount.get()).bugCount(bugCount.get()).build();
.functionCaseCount(funcCount).apiCaseCount(apiCount).apiScenarioCount(scenarioCount).bugCount(bugCount).build();
}
/**
* 批量初始化报告关联功能用例数据
* @param sqlSession sql操作对象
* @param genParam 报告生成参数
* @param report 报告
* @param bugCountMap 缺陷-用例 统计Map
* @return 执行条数
*/
private long initReportFunctionCase(SqlSession sqlSession, TestPlanReportGenPreParam genParam, TestPlanReport report, Map<String, Long> bugCountMap) {
AtomicLong funcCaseCount = new AtomicLong();
List<String> funcCaseIdList = extTestPlanReportFunctionalCaseMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(funcCaseIdList)) {
SubListUtils.dealForSubList(funcCaseIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportFunctionCase> reportFunctionCases = extTestPlanReportFunctionalCaseMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
funcCaseCount.addAndGet(reportFunctionCases.size());
// 用例等级
List<String> ids = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getFunctionCaseId).distinct().toList();
List<SelectOption> options = extTestPlanReportFunctionalCaseMapper.getCasePriorityByIds(ids);
Map<String, String> casePriorityMap = options.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
// 用例模块
List<String> moduleIds = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getFunctionCaseModule).filter(Objects::nonNull).toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.FUNCTIONAL_CASE.getKey());
// 关联的功能用例最新一次执行历史
List<String> relateIds = reportFunctionCases.stream().map(TestPlanReportFunctionCase::getTestPlanFunctionCaseId).toList();
TestPlanCaseExecuteHistoryExample example = new TestPlanCaseExecuteHistoryExample();
example.createCriteria().andTestPlanCaseIdIn(relateIds);
List<TestPlanCaseExecuteHistory> functionalExecHisList = testPlanCaseExecuteHistoryMapper.selectByExample(example);
Map<String, List<TestPlanCaseExecuteHistory>> functionalExecMap = functionalExecHisList.stream().collect(Collectors.groupingBy(TestPlanCaseExecuteHistory::getTestPlanCaseId));
for (TestPlanReportFunctionCase reportFunctionalCase : reportFunctionCases) {
reportFunctionalCase.setId(IDGenerator.nextStr());
reportFunctionalCase.setTestPlanReportId(report.getId());
reportFunctionalCase.setTestPlanName(genParam.getTestPlanName());
if (reportFunctionalCase.getFunctionCaseModule() != null) {
reportFunctionalCase.setFunctionCaseModule(moduleMap.getOrDefault(reportFunctionalCase.getFunctionCaseModule(), reportFunctionalCase.getFunctionCaseModule()));
}
reportFunctionalCase.setFunctionCasePriority(casePriorityMap.get(reportFunctionalCase.getFunctionCaseId()));
List<TestPlanCaseExecuteHistory> hisList = functionalExecMap.get(reportFunctionalCase.getTestPlanFunctionCaseId());
if (CollectionUtils.isNotEmpty(hisList)) {
Optional<String> lastExecuteHisOpt = hisList.stream().sorted(Comparator.comparing(TestPlanCaseExecuteHistory::getCreateTime).reversed()).map(TestPlanCaseExecuteHistory::getId).findFirst();
reportFunctionalCase.setFunctionCaseExecuteReportId(lastExecuteHisOpt.orElse(null));
} else {
reportFunctionalCase.setFunctionCaseExecuteReportId(null);
}
reportFunctionalCase.setFunctionCaseBugCount(bugCountMap.containsKey(reportFunctionalCase.getTestPlanFunctionCaseId()) ? bugCountMap.get(reportFunctionalCase.getTestPlanFunctionCaseId()) : 0);
}
// 插入计划功能用例关联数据 -> 报告内容
TestPlanReportFunctionCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportFunctionCaseMapper.class);
batchMapper.batchInsert(reportFunctionCases);
sqlSession.flushStatements();
});
}
return funcCaseCount.get();
}
/**
* 批量初始化报告关联接口用例数据
* @param sqlSession sql操作对象
* @param genParam 报告生成参数
* @param report 报告
* @param project 项目
* @param bugCountMap 缺陷-用例 统计Map
* @return 执行条数
*/
private long initReportApiCase(SqlSession sqlSession, TestPlanReportGenPreParam genParam, TestPlanReport report, Project project, Map<String, Long> bugCountMap) {
AtomicLong apiCaseCount = new AtomicLong();
List<String> testPlanReportApiCaseIdList = extTestPlanReportApiCaseMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(testPlanReportApiCaseIdList)) {
SubListUtils.dealForSubList(testPlanReportApiCaseIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportApiCase> reportApiCases = extTestPlanReportApiCaseMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
apiCaseCount.addAndGet(reportApiCases.size());
List<String> moduleIds = reportApiCases.stream().map(TestPlanReportApiCase::getApiCaseModule).filter(Objects::nonNull).distinct().toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.API_CASE.getKey());
for (TestPlanReportApiCase reportApiCase : reportApiCases) {
reportApiCase.setId(IDGenerator.nextStr());
reportApiCase.setTestPlanReportId(report.getId());
reportApiCase.setTestPlanName(genParam.getTestPlanName());
if (reportApiCase.getApiCaseModule() != null) {
reportApiCase.setApiCaseModule(moduleMap.getOrDefault(reportApiCase.getApiCaseModule(), reportApiCase.getApiCaseModule()));
}
// 根据不超过数据库字段最大长度压缩模块名
reportApiCase.setApiCaseModule(ServiceUtils.compressName(reportApiCase.getApiCaseModule(), 450));
if (!genParam.getUseManual()) {
// 接口执行时才更新结果
reportApiCase.setApiCaseExecuteResult(null);
reportApiCase.setApiCaseExecuteUser(null);
}
reportApiCase.setApiCaseBugCount(bugCountMap.containsKey(reportApiCase.getTestPlanApiCaseId()) ? bugCountMap.get(reportApiCase.getTestPlanApiCaseId()) : 0);
}
// 插入计划接口用例关联数据 -> 报告内容
TestPlanReportApiCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportApiCaseMapper.class);
batchMapper.batchInsert(reportApiCases);
sqlSession.flushStatements();
if (StringUtils.isNotBlank(genParam.getTaskId())) {
reportApiCases.sort(Comparator.comparing(TestPlanReportApiCase::getPos).reversed());
initApiCaseExecTaskItem(genParam.getTaskId(), genParam.getTestPlanId(), reportApiCases, report.getCreateUser(), project);
}
});
}
return apiCaseCount.get();
}
/**
* 批量初始化报告关联场景用例数据
* @param sqlSession sql操作对象
* @param genParam 报告生成参数
* @param report 报告
* @param project 项目
* @param bugCountMap 缺陷-用例 统计Map
* @return 执行条数
*/
private long initReportApiScenario(SqlSession sqlSession, TestPlanReportGenPreParam genParam, TestPlanReport report, Project project, Map<String, Long> bugCountMap) {
AtomicLong apiScenarioCount = new AtomicLong();
List<String> reportApiScenarioIdList = extTestPlanReportApiScenarioMapper.getPlanExecuteCasesId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(reportApiScenarioIdList)) {
SubListUtils.dealForSubList(reportApiScenarioIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportApiScenario> reportApiScenarios = extTestPlanReportApiScenarioMapper.getPlanExecuteCases(genParam.getTestPlanId(), subList);
apiScenarioCount.addAndGet(reportApiScenarios.size());
List<String> moduleIds = reportApiScenarios.stream().map(TestPlanReportApiScenario::getApiScenarioModule).filter(Objects::nonNull).distinct().toList();
Map<String, String> moduleMap = getModuleMapByIds(moduleIds, CaseType.SCENARIO_CASE.getKey());
for (TestPlanReportApiScenario reportApiScenario : reportApiScenarios) {
reportApiScenario.setId(IDGenerator.nextStr());
reportApiScenario.setTestPlanReportId(report.getId());
reportApiScenario.setTestPlanName(genParam.getTestPlanName());
if (reportApiScenario.getApiScenarioModule() != null) {
reportApiScenario.setApiScenarioModule(moduleMap.getOrDefault(reportApiScenario.getApiScenarioModule(), reportApiScenario.getApiScenarioModule()));
}
// 根据不超过数据库字段最大长度压缩模块名
reportApiScenario.setApiScenarioModule(ServiceUtils.compressName(reportApiScenario.getApiScenarioModule(), 450));
if (!genParam.getUseManual()) {
// 接口执行时才更新结果
reportApiScenario.setApiScenarioExecuteResult(null);
reportApiScenario.setApiScenarioExecuteUser(null);
}
reportApiScenario.setApiScenarioBugCount(bugCountMap.containsKey(reportApiScenario.getTestPlanApiScenarioId()) ? bugCountMap.get(reportApiScenario.getTestPlanApiScenarioId()) : 0);
}
// 插入计划场景用例关联数据 -> 报告内容
TestPlanReportApiScenarioMapper batchMapper = sqlSession.getMapper(TestPlanReportApiScenarioMapper.class);
batchMapper.batchInsert(reportApiScenarios);
sqlSession.flushStatements();
if (StringUtils.isNotBlank(genParam.getTaskId())) {
reportApiScenarios.sort(Comparator.comparing(TestPlanReportApiScenario::getPos).reversed());
initScenarioExecTaskItem(genParam.getTaskId(), genParam.getTestPlanId(), reportApiScenarios, report.getCreateUser(), project);
}
});
}
return apiScenarioCount.get();
}
/**
* 批量初始化报告关联缺陷数据
* @param sqlSession sql操作对象
* @param genParam 报告生成参数
* @param report 报告
* @return 执行条数
*/
private long initReportBug(SqlSession sqlSession, TestPlanReportGenPreParam genParam, TestPlanReport report) {
AtomicLong bugCount = new AtomicLong();
List<String> reportBugIdList = extTestPlanReportBugMapper.getPlanBugsId(genParam.getTestPlanId());
if (CollectionUtils.isNotEmpty(reportBugIdList)) {
SubListUtils.dealForSubList(reportBugIdList, 200, (subList) -> {
if (CollectionUtils.isEmpty(subList)) {
return;
}
List<TestPlanReportBug> reportBugs = extTestPlanReportBugMapper.getPlanBugs(genParam.getTestPlanId(), subList);
bugCount.addAndGet(reportBugs.size());
// MS处理人会与第三方的值冲突, 分开查询
List<SelectOption> headerOptions = bugCommonService.getHeaderHandlerOption(genParam.getProjectId());
Map<String, String> headerHandleUserMap = headerOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
List<SelectOption> localOptions = bugCommonService.getLocalHandlerOption(genParam.getProjectId());
Map<String, String> localHandleUserMap = localOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
Map<String, String> allStatusMap = bugCommonService.getAllStatusMap(genParam.getProjectId());
reportBugs.forEach(reportBug -> {
reportBug.setId(IDGenerator.nextStr());
reportBug.setTestPlanReportId(report.getId());
reportBug.setBugHandleUser(headerHandleUserMap.containsKey(reportBug.getBugHandleUser()) ?
headerHandleUserMap.get(reportBug.getBugHandleUser()) : localHandleUserMap.get(reportBug.getBugHandleUser()));
reportBug.setBugStatus(allStatusMap.get(reportBug.getBugStatus()));
});
// 插入计划关联用例缺陷数据(去重) -> 报告内容
TestPlanReportBugMapper batchMapper = sqlSession.getMapper(TestPlanReportBugMapper.class);
batchMapper.batchInsert(reportBugs);
sqlSession.flushStatements();
});
}
return bugCount.get();
}
private void initApiCaseExecTaskItem(String taskId, String testPlanId, List<TestPlanReportApiCase> apiTestCases, String userId, Project project) {
@ -793,20 +821,15 @@ public class TestPlanReportService {
return planReportDetail;
}
/**
* 获取计划任务执行结果
* @param taskId 任务ID
* @return 计划执行结果
*/
public TestPlanTaskReportResponse getTaskDetail(String taskId) {
TestPlanTaskReportResponse testPlanTaskReportResponse = new TestPlanTaskReportResponse();
ExecTask task = execTaskMapper.selectByPrimaryKey(taskId);
BeanUtils.copyBean(testPlanTaskReportResponse, task);
TestPlanExample planExample = new TestPlanExample();
planExample.createCriteria().andGroupIdEqualTo(task.getResourceId());
List<TestPlan> testPlans = testPlanMapper.selectByExample(planExample);
List<TestPlanTaskReportResponse.ChildPlan> childPlans = testPlans.stream().map(plan -> {
TestPlanTaskReportResponse.ChildPlan childPlan = new TestPlanTaskReportResponse.ChildPlan();
childPlan.setId(plan.getId());
childPlan.setName(plan.getName());
return childPlan;
}).toList();
testPlanTaskReportResponse.setChildPlans(childPlans);
ApiReportRelateTaskExample example = new ApiReportRelateTaskExample();
example.createCriteria().andTaskResourceIdEqualTo(taskId);
List<ApiReportRelateTask> taskReports = apiReportRelateTaskMapper.selectByExample(example);
@ -815,6 +838,14 @@ public class TestPlanReportService {
return testPlanTaskReportResponse;
}
String reportId = taskReports.getFirst().getReportId();
List<TestPlanReport> planChildrenTask = extTestPlanReportMapper.getPlanChildrenTask(reportId);
List<TestPlanTaskReportResponse.ChildPlan> childPlans = planChildrenTask.stream().map(childTask -> {
TestPlanTaskReportResponse.ChildPlan childPlan = new TestPlanTaskReportResponse.ChildPlan();
childPlan.setId(childTask.getId());
childPlan.setName(childTask.getName());
return childPlan;
}).toList();
testPlanTaskReportResponse.setChildPlans(childPlans);
return calcTaskExecActual(reportId, testPlanTaskReportResponse);
}
@ -878,9 +909,7 @@ public class TestPlanReportService {
List<String> distinctUserIds = detailCases.stream().map(ReportDetailCasePageDTO::getExecuteUser).distinct().collect(Collectors.toList());
distinctUserIds.removeIf(StringUtils::isEmpty);
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;
}
@ -915,13 +944,13 @@ public class TestPlanReportService {
return;
}
// 功能用例
List<CaseStatusCountMap> functionalCountMapList = extTestPlanReportFunctionalCaseMapper.countExecuteResult(reportId);
List<CaseStatusCountMap> functionalCountMapList = extTestPlanReportFunctionalCaseMapper.countExecuteResult(List.of(reportId));
CaseCount functionalCaseCount = countMap(functionalCountMapList);
// 接口用例
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(reportId);
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(List.of(reportId));
CaseCount apiCaseCount = countMap(apiCountMapList);
// 场景用例
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(reportId);
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(List.of(reportId));
CaseCount scenarioCaseCount = countMap(scenarioCountMapList);
// 规划整体的汇总数据
@ -950,7 +979,7 @@ public class TestPlanReportService {
summaryExample.createCriteria().andTestPlanReportIdEqualTo(reportId);
List<TestPlanReportSummary> testPlanReportSummaries = testPlanReportSummaryMapper.selectByExample(summaryExample);
if (CollectionUtils.isEmpty(testPlanReportSummaries)) {
// 报告详情不存在
// 计划组报告详情不存在
return;
}
TestPlanReportSummary groupSummary = testPlanReportSummaries.getFirst();
@ -963,69 +992,27 @@ public class TestPlanReportService {
return;
}
// 汇总子报告关联的数据并入库
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
// 汇总子报告关联的数据 (计划组的用例明细不入库)
List<String> ids = testPlanReports.stream().map(TestPlanReport::getId).toList();
// 功能用例
// 汇总功能用例
TestPlanReportFunctionCaseExample functionCaseExample = new TestPlanReportFunctionCaseExample();
functionCaseExample.createCriteria().andTestPlanReportIdIn(ids);
List<TestPlanReportFunctionCase> reportFunctionCases = testPlanReportFunctionCaseMapper.selectByExample(functionCaseExample);
if (CollectionUtils.isNotEmpty(reportFunctionCases)) {
groupSummary.setFunctionalCaseCount((long) reportFunctionCases.size());
reportFunctionCases.forEach(reportFunctionCase -> {
reportFunctionCase.setId(IDGenerator.nextStr());
reportFunctionCase.setTestPlanReportId(reportId);
});
// 插入计划组报告, 功能用例关联数据
TestPlanReportFunctionCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportFunctionCaseMapper.class);
batchMapper.batchInsert(reportFunctionCases);
}
// 接口用例
groupSummary.setFunctionalCaseCount(testPlanReportFunctionCaseMapper.countByExample(functionCaseExample));
// 汇总接口用例
TestPlanReportApiCaseExample apiCaseExample = new TestPlanReportApiCaseExample();
apiCaseExample.createCriteria().andTestPlanReportIdIn(ids);
List<TestPlanReportApiCase> reportApiCases = testPlanReportApiCaseMapper.selectByExample(apiCaseExample);
if (CollectionUtils.isNotEmpty(reportApiCases)) {
groupSummary.setApiCaseCount((long) reportApiCases.size());
reportApiCases.forEach(reportApiCase -> {
reportApiCase.setId(IDGenerator.nextStr());
reportApiCase.setTestPlanReportId(reportId);
});
// 插入计划组报告, 接口用例关联数据
TestPlanReportApiCaseMapper batchMapper = sqlSession.getMapper(TestPlanReportApiCaseMapper.class);
batchMapper.batchInsert(reportApiCases);
}
// 场景用例
groupSummary.setApiCaseCount(testPlanReportApiCaseMapper.countByExample(apiCaseExample));
// 汇总场景用例
TestPlanReportApiScenarioExample scenarioExample = new TestPlanReportApiScenarioExample();
scenarioExample.createCriteria().andTestPlanReportIdIn(ids);
List<TestPlanReportApiScenario> reportApiScenarios = testPlanReportApiScenarioMapper.selectByExample(scenarioExample);
if (CollectionUtils.isNotEmpty(reportApiScenarios)) {
groupSummary.setApiScenarioCount((long) reportApiScenarios.size());
reportApiScenarios.forEach(reportApiScenario -> {
reportApiScenario.setId(IDGenerator.nextStr());
reportApiScenario.setTestPlanReportId(reportId);
});
// 插入计划组报告, 场景用例关联数据
TestPlanReportApiScenarioMapper batchMapper = sqlSession.getMapper(TestPlanReportApiScenarioMapper.class);
batchMapper.batchInsert(reportApiScenarios);
}
// 缺陷明细
groupSummary.setApiScenarioCount(testPlanReportApiScenarioMapper.countByExample(scenarioExample));
// 汇总缺陷明细 (子计划同一缺陷去重)
TestPlanReportBugExample bugExample = new TestPlanReportBugExample();
bugExample.createCriteria().andTestPlanReportIdIn(ids);
List<TestPlanReportBug> reportBugs = testPlanReportBugMapper.selectByExample(bugExample);
if (CollectionUtils.isNotEmpty(reportBugs)) {
groupSummary.setBugCount((long) reportBugs.size());
reportBugs.forEach(reportBug -> {
reportBug.setId(IDGenerator.nextStr());
reportBug.setTestPlanReportId(reportId);
});
// 插入计划组关联用例缺陷数据
TestPlanReportBugMapper batchMapper = sqlSession.getMapper(TestPlanReportBugMapper.class);
batchMapper.batchInsert(reportBugs);
}
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
groupSummary.setBugCount(reportBugs.stream().map(TestPlanReportBug::getBugId).distinct().count());
// 汇总并计算子报告执行的数据
// 汇总并计算子报告执行的用例数据
summaryExample.clear();
summaryExample.createCriteria().andTestPlanReportIdIn(ids);
List<TestPlanReportSummary> summaryList = testPlanReportSummaryMapper.selectByExampleWithBLOBs(summaryExample);
@ -1281,22 +1268,6 @@ public class TestPlanReportService {
return userOptions.stream().collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName));
}
/**
* 获取测试点集合
*
* @param collectionIds 测试点ID集合
* @return 测试点集合
*/
private Map<String, String> getCollectionMap(List<String> collectionIds) {
if (CollectionUtils.isEmpty(collectionIds)) {
return new HashMap<>(16);
}
TestPlanCollectionExample example = new TestPlanCollectionExample();
example.createCriteria().andIdIn(collectionIds);
List<TestPlanCollection> collections = testPlanCollectionMapper.selectByExample(example);
return collections.stream().collect(Collectors.toMap(TestPlanCollection::getId, TestPlanCollection::getName));
}
/**
* 计划报告列表
*
@ -1430,11 +1401,12 @@ public class TestPlanReportService {
*/
private TestPlanTaskReportResponse calcTaskExecActual(String reportId, TestPlanTaskReportResponse testPlanTaskReportResponse) {
testPlanTaskReportResponse.setReportId(reportId);
List<String> calcReportIds = getActualReportIds(reportId);
// 计算接口用例
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(reportId);
List<CaseStatusCountMap> apiCountMapList = extTestPlanReportApiCaseMapper.countExecuteResult(calcReportIds);
CaseCount apiCaseCount = countMap(apiCountMapList);
// 计算场景用例
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(reportId);
List<CaseStatusCountMap> scenarioCountMapList = extTestPlanReportApiScenarioMapper.countExecuteResult(calcReportIds);
CaseCount scenarioCaseCount = countMap(scenarioCountMapList);
// 汇总接口&&场景用例的执行情况
CaseCount caseCount = CountUtils.summarizeProperties(List.of(apiCaseCount, scenarioCaseCount));
@ -1444,4 +1416,22 @@ public class TestPlanReportService {
0 : RateCalculateUtils.divWithPrecision(caseCount.sum() - caseCount.getPending(), caseCount.sum(), 2));
return testPlanTaskReportResponse;
}
/**
* 获取实际的报告ID集合 (计划组报告则会返回多个)
* @param reportId 报告ID
* @return 报告ID集合
*/
public List<String> getActualReportIds(String reportId) {
// 计划组任务结果, 取多个子计划的结果计算完成率
TestPlanReport report = testPlanReportMapper.selectByPrimaryKey(reportId);
if (report.getIntegrated()) {
TestPlanReportExample example = new TestPlanReportExample();
example.createCriteria().andParentIdEqualTo(reportId).andIntegratedEqualTo(false);
List<TestPlanReport> testPlanReports = testPlanReportMapper.selectByExample(example);
return testPlanReports.stream().map(TestPlanReport::getId).toList();
} else {
return List.of(reportId);
}
}
}