feat(工作台): 新增根据项目id查询计划组与子计划及联关系

This commit is contained in:
guoyuqi 2024-12-02 11:59:10 +08:00 committed by 刘瑞斌
parent 49e65c16ee
commit a56a3b1582
12 changed files with 189 additions and 19 deletions

View File

@ -0,0 +1 @@
select database();

View File

@ -0,0 +1,8 @@
-- set innodb lock wait timeout
SET SESSION innodb_lock_wait_timeout = 7200;
CREATE INDEX idx_type_project_id
ON test_plan (type, project_id);
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -0,0 +1,6 @@
-- set innodb lock wait timeout
SET SESSION innodb_lock_wait_timeout = 7200;
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -8,6 +8,7 @@ import io.metersphere.bug.service.BugCommonService;
import io.metersphere.bug.service.BugService;
import io.metersphere.dashboard.dto.LayoutDTO;
import io.metersphere.dashboard.request.DashboardFrontPageRequest;
import io.metersphere.dashboard.response.CascadeChildrenDTO;
import io.metersphere.dashboard.response.OverViewCountDTO;
import io.metersphere.dashboard.response.StatisticsDTO;
import io.metersphere.dashboard.service.DashboardService;
@ -215,4 +216,11 @@ public class DashboardController {
return bugService.getHeaderOption(projectId);
}
@GetMapping("/plan/option/{projectId}")
@Operation(summary = "获取测试计划列表")
@CheckOwner(resourceId = "#projectId", resourceType = "project")
public List<CascadeChildrenDTO> getPlanOption(@PathVariable String projectId) {
return dashboardService.getPlanOption(projectId);
}
}

View File

@ -0,0 +1,23 @@
package io.metersphere.dashboard.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CascadeDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "值/id")
private String value;
@Schema(description = "名称/标签")
private String label;
}

View File

@ -0,0 +1,26 @@
package io.metersphere.dashboard.response;
import io.metersphere.dashboard.dto.CascadeDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CascadeChildrenDTO {
@Schema(description = "值/id")
private String value;
@Schema(description = "名称/标签")
private String label;
@Schema(description = "关联子集")
private List<CascadeDTO> children;
}

View File

@ -18,11 +18,9 @@ import io.metersphere.bug.mapper.ExtBugMapper;
import io.metersphere.bug.service.BugCommonService;
import io.metersphere.bug.service.BugStatusService;
import io.metersphere.dashboard.constants.DashboardUserLayoutKeys;
import io.metersphere.dashboard.dto.LayoutDTO;
import io.metersphere.dashboard.dto.NameArrayDTO;
import io.metersphere.dashboard.dto.NameCountDTO;
import io.metersphere.dashboard.dto.StatusPercentDTO;
import io.metersphere.dashboard.dto.*;
import io.metersphere.dashboard.request.DashboardFrontPageRequest;
import io.metersphere.dashboard.response.CascadeChildrenDTO;
import io.metersphere.dashboard.response.OverViewCountDTO;
import io.metersphere.dashboard.response.StatisticsDTO;
import io.metersphere.functional.constants.CaseReviewStatus;
@ -33,7 +31,11 @@ import io.metersphere.functional.mapper.ExtCaseReviewMapper;
import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
import io.metersphere.functional.request.CaseReviewPageRequest;
import io.metersphere.functional.service.CaseReviewService;
import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.domain.TestPlanExample;
import io.metersphere.plan.dto.TestPlanAndGroupInfoDTO;
import io.metersphere.plan.mapper.ExtTestPlanMapper;
import io.metersphere.plan.mapper.TestPlanMapper;
import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.project.domain.Project;
import io.metersphere.project.dto.ProjectCountDTO;
@ -120,6 +122,8 @@ public class DashboardService {
@Resource
private UserLayoutMapper userLayoutMapper;
@Resource
private TestPlanMapper testPlanMapper;
@Resource
private BugCommonService bugCommonService;
@Resource
private BugStatusService bugStatusService;
@ -258,7 +262,7 @@ public class DashboardService {
map.put(BUG_COUNT, bugCount);
projectBugCountMap = projectBugCount.stream().collect(Collectors.toMap(ProjectCountDTO::getProjectId, ProjectCountDTO::getCount));
}else {
} else {
projectBugCountMap = new HashMap<>();
}
@ -407,7 +411,7 @@ public class DashboardService {
List<ProjectUserMemberDTO> orgProjectMemberList = extProjectMemberMapper.getOrgProjectMemberList(organizationId, null);
if (CollectionUtils.isEmpty(userLayouts)) {
List<String> userIds = orgProjectMemberList.stream().map(ProjectUserMemberDTO::getId).distinct().toList();
return getDefaultLayoutDTOS(allPermissionProjects.getFirst().getId(),userIds);
return getDefaultLayoutDTOS(allPermissionProjects.getFirst().getId(), userIds);
}
UserLayout userLayout = userLayouts.getFirst();
byte[] configuration = userLayout.getConfiguration();
@ -796,7 +800,7 @@ public class DashboardService {
}
long caseTestCount = extFunctionalCaseMapper.caseTestCount(projectId, null, null);
long simpleCaseCount = extFunctionalCaseMapper.simpleCaseCount(projectId, null, null);
List<NameCountDTO> coverList = getCoverList((int) simpleCaseCount,Translator.get("functional_case.associateRate"), (int) caseTestCount,Translator.get("functional_case.hasAssociate"), (int) (simpleCaseCount - caseTestCount), Translator.get("functional_case.unAssociate"));
List<NameCountDTO> coverList = getCoverList((int) simpleCaseCount, Translator.get("functional_case.associateRate"), (int) caseTestCount, Translator.get("functional_case.hasAssociate"), (int) (simpleCaseCount - caseTestCount), Translator.get("functional_case.unAssociate"));
Map<String, List<NameCountDTO>> statusStatisticsMap = new HashMap<>();
statusStatisticsMap.put("cover", coverList);
statisticsDTO.setStatusStatisticsMap(statusStatisticsMap);
@ -977,7 +981,7 @@ public class DashboardService {
List<FunctionalCaseStatisticDTO> statisticListByProjectId = extFunctionalCaseMapper.getStatisticListByProjectId(projectId, null, null);
List<FunctionalCaseStatisticDTO> unReviewCaseList = statisticListByProjectId.stream().filter(t -> StringUtils.equalsIgnoreCase(t.getReviewStatus(), FunctionalCaseReviewStatus.UN_REVIEWED.toString())).toList();
int reviewCount = statisticListByProjectId.size() - unReviewCaseList.size();
List<NameCountDTO> coverList = getCoverList(statisticListByProjectId.size(), Translator.get("functional_case.reviewRate"), reviewCount,Translator.get("functional_case.hasReview"), unReviewCaseList.size(), Translator.get("functional_case.unReview"));
List<NameCountDTO> coverList = getCoverList(statisticListByProjectId.size(), Translator.get("functional_case.reviewRate"), reviewCount, Translator.get("functional_case.hasReview"), unReviewCaseList.size(), Translator.get("functional_case.unReview"));
Map<String, List<NameCountDTO>> statusStatisticsMap = new HashMap<>();
statusStatisticsMap.put("cover", coverList);
statisticsDTO.setStatusStatisticsMap(statusStatisticsMap);
@ -1061,10 +1065,10 @@ public class DashboardService {
@NotNull
private List<StatusPercentDTO> getStatusPercentList(List<FunctionalCaseStatisticDTO> statisticListByProjectId) {
List<StatusPercentDTO> statusPercentList = new ArrayList<>();
List<OptionDTO>statusNameList = buildStatusNameMap();
List<OptionDTO> statusNameList = buildStatusNameMap();
int totalCount = CollectionUtils.isEmpty(statisticListByProjectId) ? 0 : statisticListByProjectId.size();
Map<String, List<FunctionalCaseStatisticDTO>> reviewStatusMap = statisticListByProjectId.stream().collect(Collectors.groupingBy(FunctionalCaseStatisticDTO::getReviewStatus));
statusNameList.forEach(t->{
statusNameList.forEach(t -> {
StatusPercentDTO statusPercentDTO = new StatusPercentDTO();
List<FunctionalCaseStatisticDTO> functionalCaseStatisticDTOS = reviewStatusMap.get(t.getId());
int count = CollectionUtils.isEmpty(functionalCaseStatisticDTOS) ? 0 : functionalCaseStatisticDTOS.size();
@ -1082,14 +1086,14 @@ public class DashboardService {
}
@NotNull
private static List<NameCountDTO> getCoverList(int totalCount, String rateName, int coverCount,String coverName, int unCoverCount, String unCoverName) {
private static List<NameCountDTO> getCoverList(int totalCount, String rateName, int coverCount, String coverName, int unCoverCount, String unCoverName) {
List<NameCountDTO> coverList = new ArrayList<>();
NameCountDTO coverRate = new NameCountDTO();
if (totalCount > 0) {
BigDecimal divide = BigDecimal.valueOf(coverCount).divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP);
coverRate.setCount(getTurnCount(divide));
}
coverRate.setName(rateName );
coverRate.setName(rateName);
coverList.add(coverRate);
NameCountDTO hasCover = new NameCountDTO();
hasCover.setCount(coverCount);
@ -1103,7 +1107,7 @@ public class DashboardService {
}
private static List<OptionDTO> buildStatusNameMap() {
List<OptionDTO>optionDTOList = new ArrayList<>();
List<OptionDTO> optionDTOList = new ArrayList<>();
optionDTOList.add(new OptionDTO(FunctionalCaseReviewStatus.UN_REVIEWED.toString(), Translator.get("case.review.status.un_reviewed")));
optionDTOList.add(new OptionDTO(FunctionalCaseReviewStatus.UNDER_REVIEWED.toString(), Translator.get("case.review.status.under_reviewed")));
optionDTOList.add(new OptionDTO(FunctionalCaseReviewStatus.PASS.toString(), Translator.get("case.review.status.pass")));
@ -1341,7 +1345,7 @@ public class DashboardService {
statisticsDTO.setErrorCode(NO_PROJECT_PERMISSION.getCode());
return statisticsDTO;
}
Set<String>handleUsers = new HashSet<>();
Set<String> handleUsers = new HashSet<>();
String localHandleUser = hasHandleUser ? userId : null;
if (StringUtils.isNotBlank(localHandleUser)) {
handleUsers.add(localHandleUser);
@ -1357,7 +1361,7 @@ public class DashboardService {
if (hasHandleUser) {
allSimpleList = extBugMapper.getByHandleUser(projectId, null, null, localHandleUser, createUser, handleUser, platformName);
} else {
allSimpleList= extBugMapper.getSimpleList(projectId, null, null, handleUsers, createUser, platforms);
allSimpleList = extBugMapper.getSimpleList(projectId, null, null, handleUsers, createUser, platforms);
}
List<String> localLastStepStatus = getBugEndStatus(projectId, platformName);
List<Bug> statusList = allSimpleList.stream().filter(t -> !localLastStepStatus.contains(t.getStatus())).toList();
@ -1501,7 +1505,7 @@ public class DashboardService {
headerStatusOption.addAll(thirdStatusOptions);
}
}
headerStatusOption = headerStatusOption.stream().filter(t->!endStatus.contains(t.getValue())).distinct().toList();
headerStatusOption = headerStatusOption.stream().filter(t -> !endStatus.contains(t.getValue())).distinct().toList();
return headerStatusOption;
}
@ -1512,6 +1516,37 @@ public class DashboardService {
}
return extSystemProjectMapper.getMemberByProjectId(projectId, keyword);
}
public List<CascadeChildrenDTO> getPlanOption(String projectId) {
List<CascadeChildrenDTO> cascadeDTOList = new ArrayList<>();
List<TestPlanAndGroupInfoDTO> groupAndPlanInfo = extTestPlanMapper.getGroupAndPlanInfo(projectId);
TestPlanExample testPlanExample = new TestPlanExample();
testPlanExample.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(TestPlanConstants.TEST_PLAN_TYPE_PLAN).andGroupIdEqualTo("NONE");
List<TestPlan> testPlans = testPlanMapper.selectByExample(testPlanExample);
Map<String, List<TestPlanAndGroupInfoDTO>> groupMap = groupAndPlanInfo.stream().collect(Collectors.groupingBy(TestPlanAndGroupInfoDTO::getGroupId));
groupMap.forEach((t, list)->{
CascadeChildrenDTO father = new CascadeChildrenDTO();
father.setValue(t);
father.setLabel(list.getFirst().getGroupName());
List<CascadeDTO> children = new ArrayList<>();
for (TestPlanAndGroupInfoDTO testPlanAndGroupInfoDTO : list) {
CascadeDTO cascadeChildrenDTO = new CascadeDTO();
cascadeChildrenDTO.setValue(testPlanAndGroupInfoDTO.getId());
cascadeChildrenDTO.setLabel(testPlanAndGroupInfoDTO.getName());
children.add(cascadeChildrenDTO);
}
father.setChildren(children);
cascadeDTOList.add(father);
});
for (TestPlan testPlan : testPlans) {
CascadeChildrenDTO father = new CascadeChildrenDTO();
father.setValue(testPlan.getId());
father.setLabel(testPlan.getName());
cascadeDTOList.add(father);
}
return cascadeDTOList;
}
}

View File

@ -9,6 +9,7 @@ import io.metersphere.bug.service.BugStatusService;
import io.metersphere.dashboard.constants.DashboardUserLayoutKeys;
import io.metersphere.dashboard.dto.LayoutDTO;
import io.metersphere.dashboard.request.DashboardFrontPageRequest;
import io.metersphere.dashboard.response.CascadeChildrenDTO;
import io.metersphere.dashboard.response.OverViewCountDTO;
import io.metersphere.dashboard.response.StatisticsDTO;
import io.metersphere.dashboard.service.DashboardService;
@ -105,6 +106,9 @@ public class DashboardFrontPageControllerTests extends BaseTest {
private static final String PROJECT_MEMBER_USER_LIST = "/dashboard/member/get-project-member/option/";
private static final String PROJECT_PLAN_LIST = "/dashboard/plan/option/";
@Test
@Order(1)
@Sql(scripts = {"/dml/init_dashboard.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
@ -591,6 +595,22 @@ public class DashboardFrontPageControllerTests extends BaseTest {
Assertions.assertNotNull(list);
}
@Test
@Order(7)
public void testProjectPlanList() throws Exception {
MvcResult mvcResult = this.requestGetWithOkAndReturn(PROJECT_PLAN_LIST + DEFAULT_PROJECT_ID);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
List<CascadeChildrenDTO> list = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), CascadeChildrenDTO.class);
Assertions.assertNotNull(list);
mvcResult = this.requestGetWithOkAndReturn(PROJECT_MEMBER_USER_LIST + "id");
returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(returnData, ResultHolder.class);
list = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), CascadeChildrenDTO.class);
Assertions.assertNotNull(list);
}
private void enableDefaultPlatformConfig() {
ProjectApplication record = new ProjectApplication();
record.setTypeValue("true");

View File

@ -121,6 +121,14 @@ INSERT INTO test_plan(id, num, project_id, group_id, module_id, name, status, ty
VALUE ('dashboard_test-plan-id', 500, '100001100001', 'NONE', 'case_plan_module', 'test_plan_associate_case_name_three', 'NOT_ARCHIVED', 'TEST_PLAN', null, UNIX_TIMESTAMP() * 1000,'admin',
UNIX_TIMESTAMP() * 1000,'admin',UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null);
INSERT INTO test_plan(id, num, project_id, group_id, module_id, name, status, type, tags, create_time, create_user, update_time, update_user, planned_start_time, planned_end_time, actual_start_time, actual_end_time, description)
VALUE ('dashboard_test-plan-id2', 500, '100001100001', 'dashboard_group-plan', 'case_plan_module', 'test_plan_name_three', 'NOT_ARCHIVED', 'TEST_PLAN', null, UNIX_TIMESTAMP() * 1000,'admin',
UNIX_TIMESTAMP() * 1000,'admin',UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null);
INSERT INTO test_plan(id, num, project_id, group_id, module_id, name, status, type, tags, create_time, create_user, update_time, update_user, planned_start_time, planned_end_time, actual_start_time, actual_end_time, description)
VALUE ('dashboard_group-plan', 500, '100001100001', 'NONE', 'case_plan_module', 'test_plan_group_name_three', 'NOT_ARCHIVED', 'GROUP', null, UNIX_TIMESTAMP() * 1000,'admin',
UNIX_TIMESTAMP() * 1000,'admin',UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, null);
INSERT INTO project_application (project_id, type, type_value) VALUES
('100001100001', 'BUG_SYNC_BUG_PLATFORM_CONFIG', '{"jiraKey":"TES","jiraBugTypeId":"10009"}'),
('100001100001', 'BUG_SYNC_PLATFORM_KEY', 'jira'),

View File

@ -0,0 +1,21 @@
package io.metersphere.plan.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class TestPlanAndGroupInfoDTO {
@Schema(description = "计划id")
private String id;
@Schema(description = "计划名称")
private String name;
@Schema(description = "计划组ID")
private String groupId;
@Schema(description = "计划组名称")
private String groupName;
@Schema(description = "项目ID")
private String projectId;
}

View File

@ -1,6 +1,7 @@
package io.metersphere.plan.mapper;
import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.dto.TestPlanAndGroupInfoDTO;
import io.metersphere.plan.dto.TestPlanExecuteHisDTO;
import io.metersphere.plan.dto.TestPlanGroupCountDTO;
import io.metersphere.plan.dto.TestPlanQueryConditions;
@ -97,4 +98,10 @@ public interface ExtTestPlanMapper {
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);
/**
* @param projectId 项目
* 获取项目下计划组和计划的名称
*/
List<TestPlanAndGroupInfoDTO> getGroupAndPlanInfo(@Param("projectId") String projectId);
}

View File

@ -939,6 +939,13 @@
</foreach>
</if>
</select>
<select id="getGroupAndPlanInfo" resultType="io.metersphere.plan.dto.TestPlanAndGroupInfoDTO">
select son.id, son.name, son.group_id, son.project_id, father.name as groupName
from test_plan son
left join test_plan father on father.id = son.group_id
where son.type = 'TEST_PLAN'
and father.type = 'GROUP' and son.project_id = #{projectId};
</select>
<sql id="queryMyFollowGroupByTableRequest">