diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index 1ca1ce7d30..e6b16f5f77 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -213,6 +213,7 @@ public class PermissionConstants { /*------ start: FUNCTIONAL_CASE ------*/ public static final String FUNCTIONAL_CASE_READ = "FUNCTIONAL_CASE:READ"; public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD"; + public static final String FUNCTIONAL_CASE_COMMENT_READ = "FUNCTIONAL_CASE_COMMENT:READ"; public static final String FUNCTIONAL_CASE_READ_UPDATE = "FUNCTIONAL_CASE:READ+UPDATE"; public static final String FUNCTIONAL_CASE_COMMENT_READ_ADD = "FUNCTIONAL_CASE_COMMENT:READ+ADD"; public static final String FUNCTIONAL_CASE_COMMENT_READ_DELETE = "FUNCTIONAL_CASE_COMMENT:READ+DELETE"; diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/CaseReviewController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/CaseReviewController.java index b4c5b76625..eed1c20bf9 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/CaseReviewController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/CaseReviewController.java @@ -1,6 +1,6 @@ package io.metersphere.functional.controller; -import io.metersphere.functional.dto.CaseReviewDto; +import io.metersphere.functional.dto.CaseReviewDTO; import io.metersphere.functional.service.CaseReviewService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.GetMapping; @@ -18,7 +18,7 @@ public class CaseReviewController { @GetMapping("/get/{id}") - public CaseReviewDto get(@PathVariable String id) { + public CaseReviewDTO get(@PathVariable String id) { return caseReviewService.get(id); } } \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseCommentController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseCommentController.java index 9bef47cec9..afa6d33eca 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseCommentController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseCommentController.java @@ -1,6 +1,7 @@ package io.metersphere.functional.controller; import io.metersphere.functional.domain.FunctionalCaseComment; +import io.metersphere.functional.dto.FunctionalCaseCommentDTO; import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.functional.service.FunctionalCaseCommentService; import io.metersphere.sdk.constants.PermissionConstants; @@ -13,6 +14,8 @@ import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + @Tag(name = "用例管理-功能用例-用例评论") @RestController @RequestMapping("/functional/case/comment") @@ -34,4 +37,11 @@ public class FunctionalCaseCommentController { public void deleteComment(@PathVariable String commentId) { functionalCaseCommentService.deleteComment(commentId); } + + @GetMapping("/get/list/{caseId}") + @Operation(summary = "用例管理-功能用例-用例评论-获取用例评论") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_COMMENT_READ) + public List getCommentList(@PathVariable String caseId) { + return functionalCaseCommentService.getCommentList(caseId); + } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDto.java b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDTO.java similarity index 66% rename from backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDto.java rename to backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDTO.java index 0bb6cf8d01..21d8d198d7 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDto.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/CaseReviewDTO.java @@ -2,5 +2,5 @@ package io.metersphere.functional.dto; import io.metersphere.functional.domain.CaseReview; -public class CaseReviewDto extends CaseReview { +public class CaseReviewDTO extends CaseReview { } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseCommentDTO.java b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseCommentDTO.java new file mode 100644 index 0000000000..bd4297ebd2 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseCommentDTO.java @@ -0,0 +1,24 @@ +package io.metersphere.functional.dto; + +import io.metersphere.functional.domain.FunctionalCaseComment; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class FunctionalCaseCommentDTO extends FunctionalCaseComment { + + @Schema(description = "评论的人名") + private String userName; + + @Schema(description = "被回复的人名") + private String replyUserName; + + @Schema(description = "被回复的人头像") + private String replyUserLogo; + + @Schema(description = "该条评论下的所有回复数据") + private List replies; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java index a5667fe533..23c466d170 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewService.java @@ -1,7 +1,7 @@ package io.metersphere.functional.service; -import io.metersphere.functional.dto.CaseReviewDto; +import io.metersphere.functional.dto.CaseReviewDTO; import org.springframework.stereotype.Service; /** @@ -13,8 +13,8 @@ import org.springframework.stereotype.Service; public class CaseReviewService { - public CaseReviewDto get(String id) { - CaseReviewDto caseReviewDto = new CaseReviewDto(); + public CaseReviewDTO get(String id) { + CaseReviewDTO caseReviewDto = new CaseReviewDTO(); return caseReviewDto; } } \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseCommentService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseCommentService.java index e3d6bb5736..86a9721dcc 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseCommentService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseCommentService.java @@ -1,16 +1,20 @@ package io.metersphere.functional.service; + import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.domain.FunctionalCaseComment; import io.metersphere.functional.domain.FunctionalCaseCommentExample; import io.metersphere.functional.dto.CommentEnum; +import io.metersphere.functional.dto.FunctionalCaseCommentDTO; import io.metersphere.functional.dto.FunctionalCaseDTO; import io.metersphere.functional.mapper.FunctionalCaseCommentMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.Translator; import io.metersphere.system.domain.User; +import io.metersphere.system.domain.UserExample; import io.metersphere.system.mapper.UserMapper; import io.metersphere.system.notice.NoticeModel; import io.metersphere.system.notice.constants.NoticeConstants; @@ -22,8 +26,13 @@ import org.apache.commons.beanutils.BeanMap; import org.apache.commons.lang3.StringUtils; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.apache.commons.collections.CollectionUtils; + + import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author guoyuqi @@ -55,7 +64,7 @@ public class FunctionalCaseCommentService { * @return FunctionalCaseComment */ public FunctionalCaseComment saveComment(FunctionalCaseCommentRequest functionalCaseCommentRequest, String userId) { - checkCase(functionalCaseCommentRequest); + checkCase(functionalCaseCommentRequest.getCaseId()); FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest, userId); if (StringUtils.equals(functionalCaseCommentRequest.getEvent(), NoticeConstants.Event.REPLY)) { return saveCommentWidthNotice(functionalCaseCommentRequest, functionalCaseComment, userId); @@ -85,8 +94,8 @@ public class FunctionalCaseCommentService { return functionalCaseComment; } - private void checkCase(FunctionalCaseCommentRequest functionalCaseCommentRequest) { - FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId()); + private void checkCase(String caseId) { + FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(caseId); if (functionalCase == null) { throw new MSException(Translator.get("case_comment.case_is_null")); } @@ -198,4 +207,84 @@ public class FunctionalCaseCommentService { functionalCaseCommentExample.createCriteria().andIdIn(commentIds); functionalCaseCommentMapper.deleteByExample(functionalCaseCommentExample); } + + public List getCommentList(String caseId) { + checkCase(caseId); + //查询出当前用例下的所有数据 + FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId); + List functionalCaseComments = functionalCaseCommentMapper.selectByExampleWithBLOBs(functionalCaseCommentExample); + List userIds = getUserIds(functionalCaseComments); + Map userMap = getUserMap(userIds); + return buildData(functionalCaseComments, userMap); + } + + /** + * 组装需要返回前端的数据结构 + * @param functionalCaseComments 查出来的所有当前用例的评论信息 + * @param userMap 用户信息 + */ + private List buildData(List functionalCaseComments, Map userMap) { + Listlist = new ArrayList<>(); + List rootList = functionalCaseComments.stream().filter(t -> StringUtils.isBlank(t.getParentId())).toList(); + List replyList = functionalCaseComments.stream().filter(t -> StringUtils.isNotBlank(t.getParentId())).toList(); + Map> commentMap = replyList.stream().collect(Collectors.groupingBy(FunctionalCaseComment::getParentId)); + for (FunctionalCaseComment functionalCaseComment : rootList) { + FunctionalCaseCommentDTO functionalCaseCommentDTO = new FunctionalCaseCommentDTO(); + BeanUtils.copyBean(functionalCaseCommentDTO,functionalCaseComment); + functionalCaseCommentDTO.setUserName(userMap.get(functionalCaseComment.getCreateUser()).getName()); + List replyComments = commentMap.get(functionalCaseComment.getId()); + if (CollectionUtils.isNotEmpty(replyComments)) { + List replies = getReplies(userMap, functionalCaseComment, replyComments); + functionalCaseCommentDTO.setReplies(replies); + } + list.add(functionalCaseCommentDTO); + } + return list; + } + + private List getReplies(Map userMap, FunctionalCaseComment functionalCaseComment, List replyComments) { + List replies = new ArrayList<>(); + for (FunctionalCaseComment replyComment : replyComments) { + FunctionalCaseCommentDTO functionalCaseCommentDTOReply = new FunctionalCaseCommentDTO(); + BeanUtils.copyBean(functionalCaseCommentDTOReply,replyComment); + functionalCaseCommentDTOReply.setUserName(userMap.get(replyComment.getCreateUser()).getName()); + if (StringUtils.isBlank(replyComment.getReplyUser())) { + functionalCaseCommentDTOReply.setReplyUserName(userMap.get(functionalCaseComment.getCreateUser()).getName()); + } else { + functionalCaseCommentDTOReply.setReplyUserName(userMap.get(replyComment.getReplyUser()).getName()); + } + replies.add(functionalCaseCommentDTOReply); + } + return replies.stream().sorted(Comparator.comparing(FunctionalCaseComment::getCreateTime).reversed()).toList(); + } + + /** + * 根据userIds 获取user信息 + * @param userIds userIds + * @return Map + */ + private Map getUserMap(List userIds) { + UserExample userExample = new UserExample(); + userExample.createCriteria().andIdIn(userIds); + List users = userMapper.selectByExample(userExample); + return users.stream().collect(Collectors.toMap(User::getId, item -> item)); + } + + /** + * 获取评论里所有人员信息 + * @param functionalCaseComments 评论集合 + * @return ListuserIds + */ + private static List getUserIds(List functionalCaseComments) { + List userIds = new ArrayList<>(functionalCaseComments.stream().flatMap(functionalCaseComment -> Stream.of(functionalCaseComment.getCreateUser(), functionalCaseComment.getReplyUser())).toList()); + List notifierList = functionalCaseComments.stream().map(FunctionalCaseComment::getNotifier).filter(StringUtils::isNotBlank).toList(); + for (String notifierStr : notifierList) { + List notifiers = Arrays.asList(notifierStr.split(";")); + userIds.addAll(notifiers); + } + userIds = userIds.stream().distinct().toList(); + return userIds; + } + } diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseCommentControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseCommentControllerTests.java index 45cec6fce4..d158f1e86e 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseCommentControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseCommentControllerTests.java @@ -4,6 +4,7 @@ import com.jayway.jsonpath.JsonPath; import io.metersphere.functional.domain.FunctionalCaseComment; import io.metersphere.functional.domain.FunctionalCaseCommentExample; import io.metersphere.functional.domain.FunctionalCaseCustomField; +import io.metersphere.functional.dto.FunctionalCaseCommentDTO; import io.metersphere.functional.mapper.FunctionalCaseCommentMapper; import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper; import io.metersphere.functional.request.FunctionalCaseCommentRequest; @@ -61,6 +62,7 @@ public class FunctionalCaseCommentControllerTests { public static final String SAVE_URL = "/functional/case/comment/save"; public static final String DELETE_URL = "/functional/case/comment/delete/"; + public static final String GET_URL = "/functional/case/comment/get/list/"; private static String sessionId; @@ -345,6 +347,33 @@ public class FunctionalCaseCommentControllerTests { @Test @Order(13) + public void getCommentSuccess() throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(GET_URL + "xiaomeinvGTest").header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.CURRENT_PROJECT, projectId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class); + List list = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), FunctionalCaseCommentDTO.class); + System.out.println(JSON.toJSONString(list)); + } + + @Test + @Order(14) + public void getCommentFalse() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(GET_URL + "xiaomeinvGTestNo").header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.CURRENT_PROJECT, projectId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + } + + + @Test + @Order(15) public void deleteCommentSuccess() throws Exception { FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); functionalCaseCommentExample.createCriteria().andCaseIdEqualTo("xiaomeinvGTest").andNotifierEqualTo("default-project-member-user-guo;default-project-member-user-guo-4;"); @@ -363,7 +392,7 @@ public class FunctionalCaseCommentControllerTests { } @Test - @Order(14) + @Order(16) public void deleteNoCommentSuccess() throws Exception { delFunctionalCaseComment("no_comment"); FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); @@ -375,6 +404,19 @@ public class FunctionalCaseCommentControllerTests { } + @Test + @Order(17) + public void deleteCommentFirstSuccess() throws Exception { + delFunctionalCaseComment("xiaomeinvGTest"); + FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andParentIdEqualTo("xiaomeinvGTest"); + List functionalCaseComments1 = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + Assertions.assertTrue(functionalCaseComments1.isEmpty()); + FunctionalCaseComment functionalCaseComment = functionalCaseCommentMapper.selectByPrimaryKey("xiaomeinvGTest"); + Assertions.assertNull(functionalCaseComment); + + } + private FunctionalCaseComment getFunctionalCaseComment(FunctionalCaseCommentRequest functionalCaseCommentRequest) throws Exception { MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) diff --git a/backend/services/case-management/src/test/resources/dml/init_case_comment.sql b/backend/services/case-management/src/test/resources/dml/init_case_comment.sql index 340935d4a3..2bdbb01e27 100644 --- a/backend/services/case-management/src/test/resources/dml/init_case_comment.sql +++ b/backend/services/case-management/src/test/resources/dml/init_case_comment.sql @@ -70,6 +70,7 @@ VALUES (UUID(), 'default-project-member-user-guo', 'project_admin', '10000110000 INSERT INTO user_role_permission(id, role_id, permission_id) VALUES ('user_role_guo_permission1', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ+ADD'), ('user_role_guo_permission2', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ+DELETE'), - ('user_role_guo_permission3', 'project_admin', 'FUNCTIONAL_CASE:READ+ADD'); + ('user_role_guo_permission3', 'project_admin', 'FUNCTIONAL_CASE:READ+ADD'), + ('user_role_guo_permission4', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ');