diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java index fd23b9c236..8a9e0b4f2e 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java @@ -117,6 +117,15 @@ public interface ExtBugMapper { List projectBugCount(@Param("projectIds") Set projectIds, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("userId") String userId); List userCreateBugCount(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("userIds") Set userIds); - List projectUserBugStatusCount(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("userIds") Set userIds, @Param("platforms") Set platforms); + /** + * 根据处理人排序的处理人状态统计 + * @param projectId 项目ID + * @param startTime 时间筛选条件 + * @param endTime 时间筛选条件 + * @param userIds 处理人筛选 + * @param platforms 平台筛选 + * @return 项目用户状态数量DTO + */ + List projectUserBugStatusCount(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("userIds") List userIds, @Param("platforms") Set platforms); } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml index 9316a27c3e..6c92c0d0ea 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml @@ -395,7 +395,7 @@ diff --git a/backend/services/dashboard/src/main/java/io/metersphere/dashboard/controller/DashboardController.java b/backend/services/dashboard/src/main/java/io/metersphere/dashboard/controller/DashboardController.java index 077f14f12a..49b856336c 100644 --- a/backend/services/dashboard/src/main/java/io/metersphere/dashboard/controller/DashboardController.java +++ b/backend/services/dashboard/src/main/java/io/metersphere/dashboard/controller/DashboardController.java @@ -58,7 +58,12 @@ public class DashboardController { return dashboardService.projectMemberViewCount(request); } - + @PostMapping("/bug_handle_user") + @Operation(summary = "缺陷处理人统计") + @CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization") + public OverViewCountDTO projectBugHandleUser(@Validated @RequestBody DashboardFrontPageRequest request) { + return dashboardService.projectBugHandleUser(request); + } @PostMapping("/case_count") diff --git a/backend/services/dashboard/src/main/java/io/metersphere/dashboard/service/DashboardService.java b/backend/services/dashboard/src/main/java/io/metersphere/dashboard/service/DashboardService.java index 80d737bc44..aae905cf43 100644 --- a/backend/services/dashboard/src/main/java/io/metersphere/dashboard/service/DashboardService.java +++ b/backend/services/dashboard/src/main/java/io/metersphere/dashboard/service/DashboardService.java @@ -3,6 +3,7 @@ package io.metersphere.dashboard.service; import io.metersphere.api.mapper.ExtApiDefinitionMapper; import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.api.mapper.ExtApiTestCaseMapper; +import io.metersphere.bug.enums.BugPlatform; import io.metersphere.bug.mapper.ExtBugMapper; import io.metersphere.bug.service.BugCommonService; import io.metersphere.bug.service.BugStatusService; @@ -19,9 +20,11 @@ import io.metersphere.functional.dto.FunctionalCaseStatisticDTO; import io.metersphere.functional.mapper.ExtCaseReviewMapper; import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; import io.metersphere.plan.mapper.ExtTestPlanMapper; +import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.project.domain.Project; import io.metersphere.project.dto.ProjectCountDTO; import io.metersphere.project.dto.ProjectUserCreateCount; +import io.metersphere.project.dto.ProjectUserStatusCountDTO; import io.metersphere.project.mapper.ExtProjectMapper; import io.metersphere.project.mapper.ExtProjectMemberMapper; import io.metersphere.project.mapper.ProjectMapper; @@ -534,7 +537,7 @@ public class DashboardService { String projectId = request.getProjectIds().getFirst(); StatisticsDTO statisticsDTO = new StatisticsDTO(); if (Boolean.FALSE.equals(checkModule(projectId, FUNCTIONAL_CASE_MODULE))) return statisticsDTO; - ListstatusPercentList = new ArrayList<>(); + List statusPercentList = new ArrayList<>(); Long toStartTime = request.getToStartTime(); Long toEndTime = request.getToEndTime(); List statisticListByProjectId = extFunctionalCaseMapper.getStatisticListByProjectId(projectId, toStartTime, toEndTime); @@ -578,14 +581,14 @@ public class DashboardService { passList.add(hasPass); NameCountDTO unPass = new NameCountDTO(); unPass.setName(Translator.get("functional_case.unPass")); - unPass.setCount(statisticListByProjectId.size()-hasPassList.size()); + unPass.setCount(statisticListByProjectId.size() - hasPassList.size()); passList.add(unPass); return passList; } @NotNull private static List getReviewList(Map> reviewStatusMap, List statisticListByProjectId) { - ListreviewList = new ArrayList<>(); + List reviewList = new ArrayList<>(); List unReviewList = reviewStatusMap.get(FunctionalCaseReviewStatus.UN_REVIEWED.toString()); if (CollectionUtils.isEmpty(unReviewList)) { unReviewList = new ArrayList<>(); @@ -595,13 +598,13 @@ public class DashboardService { if (CollectionUtils.isEmpty(statisticListByProjectId)) { reviewRate.setCount(0); } else { - BigDecimal divide = BigDecimal.valueOf(statisticListByProjectId.size()-unReviewList.size()).divide(BigDecimal.valueOf(statisticListByProjectId.size()), 0, RoundingMode.HALF_UP); + BigDecimal divide = BigDecimal.valueOf(statisticListByProjectId.size() - unReviewList.size()).divide(BigDecimal.valueOf(statisticListByProjectId.size()), 0, RoundingMode.HALF_UP); reviewRate.setCount(Integer.valueOf(String.valueOf(divide.multiply(BigDecimal.valueOf(100))))); } reviewList.add(reviewRate); NameCountDTO hasReview = new NameCountDTO(); hasReview.setName(Translator.get("functional_case.hasReview")); - hasReview.setCount(statisticListByProjectId.size()-unReviewList.size()); + hasReview.setCount(statisticListByProjectId.size() - unReviewList.size()); reviewList.add(hasReview); NameCountDTO unReview = new NameCountDTO(); unReview.setName(Translator.get("functional_case.unReview")); @@ -621,7 +624,7 @@ public class DashboardService { int size = functionalCaseStatisticDTOS.size(); statusPercentDTO.setCount(size); BigDecimal divide = BigDecimal.valueOf(size).divide(BigDecimal.valueOf(statisticListByProjectId.size()), 2, RoundingMode.HALF_UP); - statusPercentDTO.setPercentValue(divide.multiply(BigDecimal.valueOf(100))+"%"); + statusPercentDTO.setPercentValue(divide.multiply(BigDecimal.valueOf(100)) + "%"); } else { statusPercentDTO.setCount(0); statusPercentDTO.setPercentValue("0%"); @@ -638,7 +641,7 @@ public class DashboardService { Long toEndTime = request.getToEndTime(); long caseTestCount = extFunctionalCaseMapper.caseTestCount(projectId, toStartTime, toEndTime); long simpleCaseCount = extFunctionalCaseMapper.simpleCaseCount(projectId, toStartTime, toEndTime); - ListcoverList = new ArrayList<>(); + List coverList = new ArrayList<>(); NameCountDTO coverRate = new NameCountDTO(); if (simpleCaseCount > 0L) { BigDecimal divide = BigDecimal.valueOf(caseTestCount).divide(BigDecimal.valueOf(simpleCaseCount), 0, RoundingMode.HALF_UP); @@ -651,7 +654,7 @@ public class DashboardService { hasCover.setName(Translator.get("functional_case.hasCover")); coverList.add(hasCover); NameCountDTO unCover = new NameCountDTO(); - unCover.setCount((int) (simpleCaseCount-caseTestCount)); + unCover.setCount((int) (simpleCaseCount - caseTestCount)); unCover.setName(Translator.get("functional_case.unCover")); coverList.add(unCover); Map> statusStatisticsMap = new HashMap<>(); @@ -660,7 +663,136 @@ public class DashboardService { return statisticsDTO; } + public OverViewCountDTO projectBugHandleUser(DashboardFrontPageRequest request) { + String projectId = request.getProjectIds().getFirst(); + if (Boolean.FALSE.equals(checkModule(projectId, BUG_MODULE))) return new OverViewCountDTO(null, new ArrayList<>(), new ArrayList<>()); + Long toStartTime = request.getToStartTime(); + Long toEndTime = request.getToEndTime(); + List headerHandlerOption = getHandlerOption(request.getHandleUsers(), projectId); + //获取每个人每个状态有多少数据(已按照用户id排序) + List headerStatusOption = bugStatusService.getHeaderStatusOption(projectId); + Set platforms = getPlatforms(projectId); + List handleUserIds = headerHandlerOption.stream().sorted(Comparator.comparing(SelectOption::getValue)).map(SelectOption::getValue).collect(Collectors.toList()); + List projectUserStatusCountDTOS = extBugMapper.projectUserBugStatusCount(projectId, toStartTime, toEndTime, handleUserIds, platforms); + Map statusMap = headerStatusOption.stream().collect(Collectors.toMap(SelectOption::getValue, t -> t)); + List xaxis = headerHandlerOption.stream().sorted(Comparator.comparing(SelectOption::getValue)).map(SelectOption::getText).toList(); + Map> statusCountArrayMap = getStatusCountArrayMap(projectUserStatusCountDTOS, statusMap, handleUserIds); + return getHandleUserCount(xaxis, statusMap, statusCountArrayMap); + } + @NotNull + private static OverViewCountDTO getHandleUserCount(List xaxis, Map statusMap, Map> statusCountArrayMap) { + OverViewCountDTO overViewCountDTO = new OverViewCountDTO(); + //组装X轴数据 + overViewCountDTO.setXAxis(xaxis); + List projectCountList = getProjectCountList(statusMap, statusCountArrayMap); + overViewCountDTO.setProjectCountList(projectCountList); + return overViewCountDTO; + } + + /** + * 同一状态不同人的数量统计Map转换数据结构增加状态名称 + * + * @param statusMap 状态名称集合 + * @param statusCountArrayMap 同一状态不同人的数量统计Map + * @return List + */ + private static List getProjectCountList(Map statusMap, Map> statusCountArrayMap) { + List projectCountList = new ArrayList<>(); + statusCountArrayMap.forEach((status, countArray) -> { + NameArrayDTO nameArrayDTO = new NameArrayDTO(); + nameArrayDTO.setName(statusMap.get(status).getText()); + nameArrayDTO.setCount(countArray); + projectCountList.add(nameArrayDTO); + }); + return projectCountList; + } + + /** + * 根据处理人排序的处理人状态统计 将同一状态不同人的数量统计到一起 + * + * @param projectUserStatusCountDTOS 根据处理人排序的处理人状态统计集合 + * @return 同一状态不同人的数量统计Map + */ + private static Map> getStatusCountArrayMap(List projectUserStatusCountDTOS, Map statusMap, List handleUserIds) { + Map> statusCountArrayMap = new HashMap<>(); + Map> statusUserArrayMap = new HashMap<>(); + for (ProjectUserStatusCountDTO projectUserStatusCountDTO : projectUserStatusCountDTOS) { + String status = projectUserStatusCountDTO.getStatus(); + List countList = statusCountArrayMap.get(status); + List userIds = statusUserArrayMap.get(status); + if (CollectionUtils.isEmpty(countList)) { + List countArray = new ArrayList<>(); + List userArray = new ArrayList<>(); + countArray.add(projectUserStatusCountDTO.getCount()); + userArray.add(projectUserStatusCountDTO.getUserId()); + statusCountArrayMap.put(status, countArray); + statusUserArrayMap.put(status, userArray); + } else { + userIds.add(projectUserStatusCountDTO.getUserId()); + countList.add(projectUserStatusCountDTO.getCount()); + statusCountArrayMap.put(status, countList); + statusUserArrayMap.put(status, userIds); + } + } + List countArray = new ArrayList<>(); + for (int i = 0; i < handleUserIds.size(); i++) { + countArray.add(0); + } + statusMap.forEach((k, v) -> { + List handleUserCounts = statusCountArrayMap.get(k); + List userIds = statusUserArrayMap.get(k); + if (CollectionUtils.isEmpty(handleUserCounts)) { + statusCountArrayMap.put(k, countArray); + } else { + for (int i = 0; i < handleUserIds.size(); i++) { + if (userIds.size()>i) { + if (!StringUtils.equalsIgnoreCase(userIds.get(i),handleUserIds.get(i))) { + userIds.add(i,handleUserIds.get(i)); + handleUserCounts.add(i,0); + } + } else { + handleUserCounts.add(0); + } + } + } + }); + return statusCountArrayMap; + } + + /** + * 获取当前项目根据筛选有多少处理人 + * + * @param handleUsers 页面选择的处理人 + * @param projectId 项目id + * @return 处理人id 与 名称的集合 + */ + private List getHandlerOption(List handleUsers, String projectId) { + List headerHandlerOption; + if (CollectionUtils.isEmpty(handleUsers)) { + headerHandlerOption = bugCommonService.getHeaderHandlerOption(projectId); + } else { + List headerHandlerOptionList = bugCommonService.getHeaderHandlerOption(projectId); + headerHandlerOption = headerHandlerOptionList.stream().filter(t -> handleUsers.contains(t.getValue())).toList(); + } + return headerHandlerOption; + } + + /** + * 根据项目id获取当前对接平台,与本地进行组装 + * + * @param projectId 项目ID + * @return 本地与对接平台集合 + */ + private Set getPlatforms(String projectId) { + String platformName = projectApplicationService.getPlatformName(projectId); + Set platforms = new HashSet<>(); + platforms.add(BugPlatform.LOCAL.getName()); + if (!StringUtils.equalsIgnoreCase(platformName, BugPlatform.LOCAL.getName())) { + platforms.add(platformName); + } + return platforms; + } } diff --git a/backend/services/dashboard/src/test/java/io/metersphere/dashboard/controller/DashboardFrontPageControllerTests.java b/backend/services/dashboard/src/test/java/io/metersphere/dashboard/controller/DashboardFrontPageControllerTests.java index 964955c3db..a1a60c2867 100644 --- a/backend/services/dashboard/src/test/java/io/metersphere/dashboard/controller/DashboardFrontPageControllerTests.java +++ b/backend/services/dashboard/src/test/java/io/metersphere/dashboard/controller/DashboardFrontPageControllerTests.java @@ -1,12 +1,17 @@ package io.metersphere.dashboard.controller; import io.metersphere.api.utils.ApiDataUtils; +import io.metersphere.bug.domain.Bug; +import io.metersphere.bug.domain.BugExample; +import io.metersphere.bug.mapper.BugMapper; +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.OverViewCountDTO; import io.metersphere.dashboard.response.StatisticsDTO; import io.metersphere.dashboard.service.DashboardService; +import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.project.domain.Project; import io.metersphere.project.domain.ProjectExample; import io.metersphere.project.dto.ProjectUserDTO; @@ -42,6 +47,10 @@ public class DashboardFrontPageControllerTests extends BaseTest { @Resource private ProjectMemberService projectMemberService; + @Resource + private BugStatusService bugStatusService; + @Resource + private BugMapper bugMapper; private static final String EDIT_LAYOUT = "/dashboard/layout/edit/"; private static final String GET_LAYOUT = "/dashboard/layout/get/"; @@ -52,6 +61,8 @@ public class DashboardFrontPageControllerTests extends BaseTest { private static final String PROJECT_MEMBER_VIEW = "/dashboard/project_member_view"; private static final String CASE_COUNT = "/dashboard/case_count"; private static final String ASSOCIATE_CASE_COUNT = "/dashboard/associate_case_count"; + private static final String BUG_HANDLE_USER = "/dashboard/bug_handle_user"; + @Test @@ -64,6 +75,11 @@ public class DashboardFrontPageControllerTests extends BaseTest { dashboardFrontPageRequest.setCurrent(1); dashboardFrontPageRequest.setPageSize(5); dashboardFrontPageRequest.setProjectIds(List.of(DEFAULT_PROJECT_ID)); + MvcResult bugMvcResult = this.requestPostWithOkAndReturn(BUG_HANDLE_USER, dashboardFrontPageRequest); + String bugContentAsString = bugMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder bugResultHolder = JSON.parseObject(bugContentAsString, ResultHolder.class); + OverViewCountDTO bugCount = JSON.parseObject(JSON.toJSONString(bugResultHolder.getData()), OverViewCountDTO.class); + Assertions.assertNotNull(bugCount); MvcResult mvcResult = this.requestPostWithOkAndReturn(CREATE_BY_ME, dashboardFrontPageRequest); String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class); @@ -86,7 +102,6 @@ public class DashboardFrontPageControllerTests extends BaseTest { JSON.parseObject(mvcResultAll.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()), OverViewCountDTO.class); Assertions.assertNotNull(moduleCountAll); - OverViewCountDTO gyq = dashboardService.createByMeCount(dashboardFrontPageRequest, "gyq"); Assertions.assertNotNull(gyq); @@ -105,17 +120,39 @@ public class DashboardFrontPageControllerTests extends BaseTest { JSON.parseObject(mvcResultAll.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()), OverViewCountDTO.class); Assertions.assertNotNull(moduleCountAll); + List headerStatusOption = bugStatusService.getHeaderStatusOption(DEFAULT_PROJECT_ID); + buildBug(headerStatusOption); + bugMvcResult = this.requestPostWithOkAndReturn(BUG_HANDLE_USER, dashboardFrontPageRequest); + bugContentAsString = bugMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + bugResultHolder = JSON.parseObject(bugContentAsString, ResultHolder.class); + bugCount = JSON.parseObject(JSON.toJSONString(bugResultHolder.getData()), OverViewCountDTO.class); + Assertions.assertNotNull(bugCount); + Project project = new Project(); project.setModuleSetting("[]"); project.setId(DEFAULT_PROJECT_ID); projectMapper.updateByPrimaryKeySelective(project); OverViewCountDTO gyq4 = dashboardService.projectViewCount(dashboardFrontPageRequest, "default-dashboard-member-user-gyq"); Assertions.assertTrue(gyq4.getXAxis().isEmpty()); + OverViewCountDTO gyq5 = dashboardService.projectBugHandleUser(dashboardFrontPageRequest); + Assertions.assertTrue(gyq5.getXAxis().isEmpty()); project.setModuleSetting("[\"apiTest\",\"testPlan\",\"caseManagement\",\"bugManagement\"]"); project.setId(DEFAULT_PROJECT_ID); projectMapper.updateByPrimaryKeySelective(project); } + private void buildBug(List headerStatusOption) { + BugExample bugExample = new BugExample(); + bugExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID); + List bugs = bugMapper.selectByExample(bugExample); + for (int i = 0; i < bugs.size(); i++) { + Bug bug = new Bug(); + bug.setId(bugs.get(i).getId()); + bug.setStatus(headerStatusOption.get(i).getValue()); + bugMapper.updateByPrimaryKeySelective(bug); + } + } + @Test @Order(2) public void testLayout() throws Exception { diff --git a/backend/services/dashboard/src/test/resources/dml/init_dashboard.sql b/backend/services/dashboard/src/test/resources/dml/init_dashboard.sql index 6e8c75633d..b835739294 100644 --- a/backend/services/dashboard/src/test/resources/dml/init_dashboard.sql +++ b/backend/services/dashboard/src/test/resources/dml/init_dashboard.sql @@ -61,5 +61,12 @@ INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('dash INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUES('gyq_custom_id1', 'functional_priority', 'FUNCTIONAL', 'SELECT', '', 1, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'test_template_case_trash_id'); +INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) +VALUES ('dashboard_bug1', 100001, 'oasis', 'admin', 'admin', 'admin', 1716185577387, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000); +INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) +VALUES ('dashboard_bug2', 100002, 'oasis2', 'PROJECT', 'PROJECT', 'admin', 1716185577387, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000); + +INSERT INTO bug (id, num, title, handle_users, handle_user, create_user, create_time,update_user, update_time, delete_user, delete_time, project_id, template_id, platform, status, tags, platform_bug_id, deleted, pos) +VALUES ('dashboard_bug3', 100003, 'oasis3', 'admin', 'admin', 'admin', 1716185577387, 'admin', UNIX_TIMESTAMP() * 1000, 'admin', UNIX_TIMESTAMP() * 1000, '100001100001', 'bug-template-id', 'Local', 'open', '["default-tag"]', null, 0, 5000) diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectUserStatusCountDTO.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectUserStatusCountDTO.java index 91641c784e..b97819c797 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectUserStatusCountDTO.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/ProjectUserStatusCountDTO.java @@ -12,7 +12,7 @@ public class ProjectUserStatusCountDTO { @Schema(description = "用户ID") private String userId; - @Schema(description = "用户ID") + @Schema(description = "状态") private String status; @Schema(description = "数量") private int count;