diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.java b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.java index e54e2d1216..c345f367b6 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.java @@ -7,6 +7,7 @@ import io.metersphere.functional.request.CaseReviewPageRequest; import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.ProjectCountDTO; import io.metersphere.project.dto.ProjectUserCreateCount; +import io.metersphere.project.dto.ProjectUserStatusCountDTO; import io.metersphere.system.interceptor.BaseConditionFilter; import org.apache.ibatis.annotations.Param; @@ -47,5 +48,13 @@ public interface ExtCaseReviewMapper { List userCreateReviewCount(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime, @Param("userIds") Set userIds); + /** + * 获取各种状态总数量的评审 + * @param projectId 项目ID + * @param startTime 时间过滤条件 + * @param endTime 时间过滤条件 + * @return ProjectUserStatusCountDTO userId 在这里不返回 + */ + List statusReviewCount(@Param("projectId") String projectId, @Param("startTime") long startTime, @Param("endTime") long endTime); } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.xml b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.xml index 1899f359f4..073b2d86c1 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.xml +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtCaseReviewMapper.xml @@ -211,6 +211,9 @@ + + + @@ -268,6 +271,9 @@ + + + @@ -373,5 +379,13 @@ group by case_review.create_user; + \ No newline at end of file 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 49b856336c..03edd54f5c 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 @@ -1,19 +1,33 @@ package io.metersphere.dashboard.controller; +import com.alibaba.excel.util.StringUtils; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; 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.functional.constants.CaseReviewStatus; +import io.metersphere.functional.dto.CaseReviewDTO; +import io.metersphere.functional.request.CaseReviewPageRequest; +import io.metersphere.functional.service.CaseReviewService; +import io.metersphere.sdk.dto.CombineCondition; +import io.metersphere.sdk.dto.CombineSearch; import io.metersphere.system.security.CheckOwner; +import io.metersphere.system.utils.PageUtils; +import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.SessionUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import org.jetbrains.annotations.NotNull; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Tag(name = "工作台-首页") @RestController @@ -22,6 +36,8 @@ public class DashboardController { @Resource private DashboardService dashboardService; + @Resource + private CaseReviewService caseReviewService; @PostMapping("/layout/edit/{organizationId}") @Operation(summary = "编辑用户布局") @@ -65,7 +81,6 @@ public class DashboardController { return dashboardService.projectBugHandleUser(request); } - @PostMapping("/case_count") @Operation(summary = "用例数") @CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization") @@ -80,4 +95,60 @@ public class DashboardController { return dashboardService.projectAssociateCaseCount(request); } + @PostMapping("/review_case_count") + @Operation(summary = "用例评审数") + @CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization") + public StatisticsDTO projectReviewCaseCount(@Validated @RequestBody DashboardFrontPageRequest request) { + return dashboardService.projectReviewCaseCount(request); + } + + @PostMapping("/reviewing_by_me") + @Operation(summary = "待我评审") + @CheckOwner(resourceId = "#request.getOrganizationId()", resourceType = "organization") + public Pager> getFunctionalCasePage(@Validated @RequestBody DashboardFrontPageRequest request) { + CaseReviewPageRequest reviewRequest = getCaseReviewPageRequest(request); + Page page = PageHelper.startPage(reviewRequest.getCurrent(), reviewRequest.getPageSize(), + StringUtils.isNotBlank(reviewRequest.getSortString()) ? reviewRequest.getSortString() : "pos desc"); + return PageUtils.setPageInfo(page, caseReviewService.getCaseReviewPage(reviewRequest)); + } + + @NotNull + private static CaseReviewPageRequest getCaseReviewPageRequest(DashboardFrontPageRequest request) { + String projectId = request.getProjectIds().getFirst(); + CaseReviewPageRequest reviewRequest = new CaseReviewPageRequest(); + reviewRequest.setProjectId(projectId); + reviewRequest.setPageSize(request.getPageSize()); + reviewRequest.setCurrent(request.getCurrent()); + reviewRequest.setSort(request.getSort()); + CombineSearch combineSearch = getCombineSearch(request); + reviewRequest.setCombineSearch(combineSearch); + return reviewRequest; + } + + @NotNull + private static CombineSearch getCombineSearch(DashboardFrontPageRequest request) { + CombineSearch combineSearch = new CombineSearch(); + combineSearch.setSearchMode(CombineSearch.SearchMode.AND.name()); + List conditions = new ArrayList<>(); + CombineCondition userCombineCondition = getCombineCondition(List.of(Objects.requireNonNull(SessionUtils.getUserId())), "reviewers",CombineCondition.CombineConditionOperator.IN.toString()); + conditions.add(userCombineCondition); + CombineCondition statusCombineCondition = getCombineCondition(List.of(CaseReviewStatus.PREPARED.toString(), CaseReviewStatus.UNDERWAY.toString()), "status",CombineCondition.CombineConditionOperator.IN.toString()); + conditions.add(statusCombineCondition); + CombineCondition createTimeCombineCondition = getCombineCondition(List.of(request.getToStartTime(), request.getToEndTime()), "createTime",CombineCondition.CombineConditionOperator.BETWEEN.toString()); + conditions.add(createTimeCombineCondition); + combineSearch.setConditions(conditions); + return combineSearch; + } + + @NotNull + private static CombineCondition getCombineCondition(List value, String reviewers, String operator) { + CombineCondition userCombineCondition = new CombineCondition(); + userCombineCondition.setValue(value); + userCombineCondition.setName(reviewers); + userCombineCondition.setOperator(operator); + userCombineCondition.setCustomField(false); + userCombineCondition.setCustomFieldType(""); + return userCombineCondition; + } + } 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 aae905cf43..71f4f2dee0 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 @@ -15,6 +15,7 @@ import io.metersphere.dashboard.dto.StatusPercentDTO; import io.metersphere.dashboard.request.DashboardFrontPageRequest; import io.metersphere.dashboard.response.OverViewCountDTO; import io.metersphere.dashboard.response.StatisticsDTO; +import io.metersphere.functional.constants.CaseReviewStatus; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.dto.FunctionalCaseStatisticDTO; import io.metersphere.functional.mapper.ExtCaseReviewMapper; @@ -641,22 +642,7 @@ public class DashboardService { Long toEndTime = request.getToEndTime(); long caseTestCount = extFunctionalCaseMapper.caseTestCount(projectId, toStartTime, toEndTime); long simpleCaseCount = extFunctionalCaseMapper.simpleCaseCount(projectId, toStartTime, toEndTime); - List coverList = new ArrayList<>(); - NameCountDTO coverRate = new NameCountDTO(); - if (simpleCaseCount > 0L) { - BigDecimal divide = BigDecimal.valueOf(caseTestCount).divide(BigDecimal.valueOf(simpleCaseCount), 0, RoundingMode.HALF_UP); - coverRate.setCount(Integer.valueOf(String.valueOf(divide.multiply(BigDecimal.valueOf(100))))); - } - coverRate.setName(Translator.get("functional_case.coverRate")); - coverList.add(coverRate); - NameCountDTO hasCover = new NameCountDTO(); - hasCover.setCount((int) caseTestCount); - hasCover.setName(Translator.get("functional_case.hasCover")); - coverList.add(hasCover); - NameCountDTO unCover = new NameCountDTO(); - unCover.setCount((int) (simpleCaseCount - caseTestCount)); - unCover.setName(Translator.get("functional_case.unCover")); - coverList.add(unCover); + List coverList = getCoverList((int) simpleCaseCount, (int) caseTestCount, (int) (simpleCaseCount - caseTestCount)); Map> statusStatisticsMap = new HashMap<>(); statusStatisticsMap.put("cover", coverList); statisticsDTO.setStatusStatisticsMap(statusStatisticsMap); @@ -665,7 +651,8 @@ public class DashboardService { 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<>()); + 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); @@ -746,10 +733,10 @@ public class DashboardService { 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); + 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); @@ -793,6 +780,80 @@ public class DashboardService { } return platforms; } + + public StatisticsDTO projectReviewCaseCount(DashboardFrontPageRequest request) { + String projectId = request.getProjectIds().getFirst(); + StatisticsDTO statisticsDTO = new StatisticsDTO(); + if (Boolean.FALSE.equals(checkModule(projectId, FUNCTIONAL_CASE_MODULE))) return statisticsDTO; + Long toStartTime = request.getToStartTime(); + Long toEndTime = request.getToEndTime(); + List statisticListByProjectId = extFunctionalCaseMapper.getStatisticListByProjectId(projectId, toStartTime, toEndTime); + List unReviewCaseList = statisticListByProjectId.stream().filter(t -> StringUtils.equalsIgnoreCase(t.getReviewStatus(), FunctionalCaseReviewStatus.UN_REVIEWED.toString())).toList(); + int reviewCount = statisticListByProjectId.size() - unReviewCaseList.size(); + List coverList = getCoverList(statisticListByProjectId.size(), reviewCount, unReviewCaseList.size()); + Map> statusStatisticsMap = new HashMap<>(); + statusStatisticsMap.put("cover", coverList); + statisticsDTO.setStatusStatisticsMap(statusStatisticsMap); + List statusPercentList = getStatusPercentList(projectId, toStartTime, toEndTime); + statisticsDTO.setStatusPercentList(statusPercentList); + return statisticsDTO; + } + + @NotNull + private List getStatusPercentList(String projectId, Long toStartTime, Long toEndTime) { + List statusPercentList = new ArrayList<>(); + Map statusNameMap = buildStatusNameMap(); + List projectUserStatusCountDTOS = extCaseReviewMapper.statusReviewCount(projectId, toStartTime, toEndTime); + Map statusCountMap = projectUserStatusCountDTOS.stream().collect(Collectors.toMap(ProjectUserStatusCountDTO::getStatus, ProjectUserStatusCountDTO::getCount)); + statusNameMap.forEach((k, v) -> { + StatusPercentDTO statusPercentDTO = new StatusPercentDTO(); + Integer count = statusCountMap.get(k); + statusPercentDTO.setStatus(v); + if (count != null) { + statusPercentDTO.setCount(count); + } else { + count = 0; + statusPercentDTO.setCount(0); + } + if (CollectionUtils.isNotEmpty(projectUserStatusCountDTOS)) { + BigDecimal divide = BigDecimal.valueOf(count).divide(BigDecimal.valueOf(projectUserStatusCountDTOS.size()), 2, RoundingMode.HALF_UP); + statusPercentDTO.setPercentValue(divide.multiply(BigDecimal.valueOf(100)) + "%"); + } else { + statusPercentDTO.setPercentValue("0%"); + } + statusPercentList.add(statusPercentDTO); + }); + return statusPercentList; + } + + @NotNull + private static List getCoverList(int totalCount, int coverCount, int unCoverCount) { + List coverList = new ArrayList<>(); + NameCountDTO coverRate = new NameCountDTO(); + if (totalCount>0) { + BigDecimal divide = BigDecimal.valueOf(coverCount).divide(BigDecimal.valueOf(totalCount), 0, RoundingMode.HALF_UP); + coverRate.setCount(Integer.valueOf(String.valueOf(divide.multiply(BigDecimal.valueOf(100))))); + } + coverRate.setName(Translator.get("functional_case.coverRate")); + coverList.add(coverRate); + NameCountDTO hasCover = new NameCountDTO(); + hasCover.setCount(coverCount); + hasCover.setName(Translator.get("functional_case.hasCover")); + coverList.add(hasCover); + NameCountDTO unCover = new NameCountDTO(); + unCover.setCount(unCoverCount); + unCover.setName(Translator.get("functional_case.unCover")); + coverList.add(unCover); + return coverList; + } + + private static Map buildStatusNameMap() { + Map statusNameMap = new HashMap<>(); + statusNameMap.put(CaseReviewStatus.PREPARED.toString(), Translator.get("case_review.prepared")); + statusNameMap.put(CaseReviewStatus.UNDERWAY.toString(), Translator.get("case_review.underway")); + statusNameMap.put(CaseReviewStatus.COMPLETED.toString(), Translator.get("case_review.completed")); + return statusNameMap; + } } 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 a1a60c2867..5680f1a200 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 @@ -11,6 +11,7 @@ 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.functional.dto.CaseReviewDTO; import io.metersphere.plugin.platform.dto.SelectOption; import io.metersphere.project.domain.Project; import io.metersphere.project.domain.ProjectExample; @@ -21,6 +22,7 @@ import io.metersphere.project.service.ProjectMemberService; import io.metersphere.sdk.util.JSON; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; import org.junit.jupiter.api.*; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -61,8 +63,10 @@ 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 REVIEW_CASE_COUNT = "/dashboard/review_case_count"; private static final String BUG_HANDLE_USER = "/dashboard/bug_handle_user"; + private static final String REVIEWING_BY_ME = "/dashboard/reviewing_by_me"; @Test @@ -263,6 +267,11 @@ public class DashboardFrontPageControllerTests extends BaseTest { ResultHolder associateResultHolder = JSON.parseObject(associateContent, ResultHolder.class); StatisticsDTO associateCount = JSON.parseObject(JSON.toJSONString(associateResultHolder.getData()), StatisticsDTO.class); Assertions.assertNotNull(associateCount); + MvcResult reviewMvcResult = this.requestPostWithOkAndReturn(REVIEW_CASE_COUNT, dashboardFrontPageRequest); + String reviewContent = reviewMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder reviewResultHolder = JSON.parseObject(reviewContent, ResultHolder.class); + StatisticsDTO reviewCount = JSON.parseObject(JSON.toJSONString(reviewResultHolder.getData()), StatisticsDTO.class); + Assertions.assertNotNull(reviewCount); Project project = new Project(); project.setModuleSetting("[]"); project.setId(DEFAULT_PROJECT_ID); @@ -277,6 +286,11 @@ public class DashboardFrontPageControllerTests extends BaseTest { associateResultHolder = JSON.parseObject(associateContent, ResultHolder.class); associateCount = JSON.parseObject(JSON.toJSONString(associateResultHolder.getData()), StatisticsDTO.class); Assertions.assertNotNull(associateCount); + reviewMvcResult = this.requestPostWithOkAndReturn(REVIEW_CASE_COUNT, dashboardFrontPageRequest); + reviewContent = reviewMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + reviewResultHolder = JSON.parseObject(reviewContent, ResultHolder.class); + reviewCount = JSON.parseObject(JSON.toJSONString(reviewResultHolder.getData()), StatisticsDTO.class); + Assertions.assertNotNull(reviewCount); project.setModuleSetting("[\"apiTest\",\"testPlan\",\"caseManagement\",\"bugManagement\"]"); project.setId(DEFAULT_PROJECT_ID); projectMapper.updateByPrimaryKeySelective(project); @@ -293,8 +307,38 @@ public class DashboardFrontPageControllerTests extends BaseTest { associateResultHolder = JSON.parseObject(associateContent, ResultHolder.class); associateCount = JSON.parseObject(JSON.toJSONString(associateResultHolder.getData()), StatisticsDTO.class); Assertions.assertNotNull(associateCount); + reviewMvcResult = this.requestPostWithOkAndReturn(REVIEW_CASE_COUNT, dashboardFrontPageRequest); + reviewContent = reviewMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + reviewResultHolder = JSON.parseObject(reviewContent, ResultHolder.class); + reviewCount = JSON.parseObject(JSON.toJSONString(reviewResultHolder.getData()), StatisticsDTO.class); + Assertions.assertNotNull(reviewCount); } + @Test + @Order(4) + public void testList() throws Exception { + DashboardFrontPageRequest dashboardFrontPageRequest = new DashboardFrontPageRequest(); + dashboardFrontPageRequest.setOrganizationId(DEFAULT_ORGANIZATION_ID); + dashboardFrontPageRequest.setDayNumber(3); + dashboardFrontPageRequest.setCurrent(1); + dashboardFrontPageRequest.setPageSize(5); + dashboardFrontPageRequest.setProjectIds(List.of(DEFAULT_PROJECT_ID)); + MvcResult mvcResult = this.requestPostWithOkAndReturn(REVIEWING_BY_ME, dashboardFrontPageRequest); + Pager> tableData = JSON.parseObject(JSON.toJSONString( + JSON.parseObject(mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()), + Pager.class); + List list = tableData.getList(); + Assertions.assertNotNull(list); + dashboardFrontPageRequest.setStartTime(1697971947000L); + dashboardFrontPageRequest.setEndTime(1700650347000L); + mvcResult = this.requestPostWithOkAndReturn(REVIEWING_BY_ME, dashboardFrontPageRequest); + tableData = JSON.parseObject(JSON.toJSONString( + JSON.parseObject(mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()), + Pager.class); + list = tableData.getList(); + Assertions.assertNotNull(list); + } + }