From c09f15ed11f53351b0061e7137fc2e4b4774a31b Mon Sep 17 00:00:00 2001 From: guoyuqi Date: Thu, 26 Oct 2023 13:57:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E5=8A=9F=E8=83=BD=E7=94=A8=E4=BE=8B):?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3=E9=80=BB=E8=BE=91=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/constants/PermissionConstants.java | 2 + .../src/main/resources/i18n/case.properties | 6 +- .../main/resources/i18n/case_en_US.properties | 6 +- .../main/resources/i18n/case_zh_CN.properties | 6 +- .../main/resources/i18n/case_zh_TW.properties | 6 +- .../FunctionalCaseCommentController.java | 16 +- .../functional/dto/FunctionalCaseDTO.java | 6 + .../request/FunctionalCaseCommentRequest.java | 6 +- .../service/FunctionalCaseCommentService.java | 158 ++++++++- .../service/FunctionalCaseNoticeService.java | 96 ++++- .../FunctionalCaseCommentControllerTests.java | 333 ++++++++++++++++-- .../test/resources/dml/init_case_comment.sql | 42 ++- .../service/CreateRobotResourceService.java | 56 ++- .../controller/CreateRobotResourceTests.java | 10 + .../notice/constants/NoticeConstants.java | 2 +- .../notice/sender/AbstractNoticeSender.java | 21 +- .../AfterReturningNoticeSendService.java | 21 +- .../notice/sender/SendNoticeAspect.java | 34 +- .../system/service/MessageDetailService.java | 17 +- .../AfterReturningNoticeSendServiceTests.java | 115 ++++++ .../controller/dto/FunctionalCaseDTO.java | 19 + .../src/test/resources/dml/init_aspect.sql | 35 ++ 22 files changed, 913 insertions(+), 100 deletions(-) create mode 100644 backend/services/system-setting/src/test/java/io/metersphere/system/controller/AfterReturningNoticeSendServiceTests.java create mode 100644 backend/services/system-setting/src/test/java/io/metersphere/system/controller/dto/FunctionalCaseDTO.java create mode 100644 backend/services/system-setting/src/test/resources/dml/init_aspect.sql 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 61193562a3..1ca1ce7d30 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 @@ -215,6 +215,8 @@ public class PermissionConstants { public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD"; 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"; + /*------ end: FUNCTIONAL_CASE ------*/ } diff --git a/backend/framework/sdk/src/main/resources/i18n/case.properties b/backend/framework/sdk/src/main/resources/i18n/case.properties index 625e49d54f..c2def6ab5b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case.properties @@ -120,4 +120,8 @@ case_review_follow.follow_id.not_blank=关注人不能为空 custom_field_test_case.resource_id.not_blank=资源ID不能为空 custom_field_test_case.field_id.not_blank=字段ID不能为空 #comment -case_comment.case_is_null=功能用例不存在 \ No newline at end of file +case_comment.case_is_null=功能用例不存在 +case_comment.parent_id_is_null=当前回复的评论id为空 +case_comment.parent_case_is_null=当前回复的评论不存在 +case_comment.reply_user_is_null=回复的用户为空 +case_comment.id_is_null=当前评论id为空 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties index 196ac6d7f3..0c5b911a5c 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties @@ -137,4 +137,8 @@ custom_field_test_case.resource_id.not_blank=Resource ID cannot be empty custom_field_test_case.field_id.not_blank=Field ID cannot be empty default_template_not_found=Default template not found #comment -case_comment.case_is_null=Function use case does not exist \ No newline at end of file +case_comment.case_is_null=Function use case does not exist +case_comment.parent_id_is_null=The comment id of the current reply is empty +case_comment.parent_case_is_null=The comment currently being replied to does not exist +case_comment.reply_user_is_null=The user who replied is empty +case_comment.id_is_null=The current comment id is empty \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties index a256cbdfce..3d9a66e05b 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties @@ -137,4 +137,8 @@ custom_field_test_case.resource_id.not_blank=资源ID不能为空 custom_field_test_case.field_id.not_blank=字段ID不能为空 default_template_not_found=默认模板不存在 #comment -case_comment.case_is_null=功能用例不存在 \ No newline at end of file +case_comment.case_is_null=功能用例不存在 +case_comment.parent_id_is_null=当前回复的评论id为空 +case_comment.parent_case_is_null=当前回复的评论不存在 +case_comment.reply_user_is_null=回复的用户为空 +case_comment.id_is_null=当前评论id为空 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties index 18d9348021..dd68a88f3a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties @@ -137,4 +137,8 @@ custom_field_test_case.resource_id.not_blank=資源ID不能為空 custom_field_test_case.field_id.not_blank=字段ID不能為空 default_template_not_found=默認模板不存在 #comment -case_comment.case_is_null=功能用例不存在 \ No newline at end of file +case_comment.case_is_null=功能用例不存在 +case_comment.parent_id_is_null=目前回覆的評論id為空 +case_comment.parent_case_is_null=目前回應的評論不存在 +case_comment.reply_user_is_null=回覆的用戶為空 +case_comment.id_is_null=目前評論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 f790124422..9bef47cec9 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 @@ -3,10 +3,7 @@ package io.metersphere.functional.controller; import io.metersphere.functional.domain.FunctionalCaseComment; import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.functional.service.FunctionalCaseCommentService; -import io.metersphere.functional.service.FunctionalCaseNoticeService; import io.metersphere.sdk.constants.PermissionConstants; -import io.metersphere.system.notice.annotation.SendNotice; -import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.utils.SessionUtils; import io.metersphere.validation.groups.Created; import io.swagger.v3.oas.annotations.Operation; @@ -14,10 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Tag(name = "用例管理-功能用例-用例评论") @RestController @@ -30,8 +24,14 @@ public class FunctionalCaseCommentController { @PostMapping("/save") @Operation(summary = "用例管理-功能用例-用例评论-创建评论") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_COMMENT_READ_ADD) - @SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.AT, target = "#targetClass.getRelatedUsers(#functionalCaseCommentRequest)", targetClass = FunctionalCaseNoticeService.class) public FunctionalCaseComment saveComment(@Validated({Created.class}) @RequestBody FunctionalCaseCommentRequest functionalCaseCommentRequest) { return functionalCaseCommentService.saveComment(functionalCaseCommentRequest, SessionUtils.getUserId()); } + + @GetMapping("/delete/{commentId}") + @Operation(summary = "用例管理-功能用例-用例评论-删除评论") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_COMMENT_READ_DELETE) + public void deleteComment(@PathVariable String commentId) { + functionalCaseCommentService.deleteComment(commentId); + } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseDTO.java b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseDTO.java index 62fbf46833..fb16adff6d 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseDTO.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseDTO.java @@ -1,13 +1,19 @@ package io.metersphere.functional.dto; import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.system.dto.sdk.OptionDTO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.util.List; + @Data public class FunctionalCaseDTO extends FunctionalCase { @Schema(description = "评论@的人, 多个以';'隔开") private String relatedUsers; + @Schema(description = "自定义字段的值") + private List fields; + } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseCommentRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseCommentRequest.java index e3e69a752b..4f02ec3482 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseCommentRequest.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseCommentRequest.java @@ -14,7 +14,7 @@ public class FunctionalCaseCommentRequest { @NotBlank(message = "{functional_case_comment.case_id.not_blank}", groups = {Created.class}) private String caseId; - @Schema(description = "评论@的人, 多个以';'隔开") + @Schema(description = "评论@的人的Id, 多个以';'隔开") private String notifier; @Schema(description = "回复人") @@ -27,4 +27,8 @@ public class FunctionalCaseCommentRequest { @NotBlank(message = "{functional_case_comment.content.not_blank}", groups = {Created.class}) private String content; + @Schema(description = "任务事件(仅评论: ’COMMENT‘; 评论并@: ’AT‘; 回复评论/回复并@: ’REPLAY‘;)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{functional_case_comment.event.not_blank}", groups = {Created.class}) + private String event; + } 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 8d262096ef..e3d6bb5736 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 @@ -2,17 +2,29 @@ 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.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.Translator; +import io.metersphere.system.domain.User; +import io.metersphere.system.mapper.UserMapper; +import io.metersphere.system.notice.NoticeModel; +import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.notice.utils.MessageTemplateUtils; +import io.metersphere.system.service.NoticeSendService; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import org.apache.commons.beanutils.BeanMap; import org.apache.commons.lang3.StringUtils; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import java.util.*; + /** * @author guoyuqi */ @@ -26,14 +38,42 @@ public class FunctionalCaseCommentService { @Resource private FunctionalCaseMapper functionalCaseMapper; + @Resource + private UserMapper userMapper; + + @Resource + private FunctionalCaseNoticeService functionalCaseNoticeService; + + @Resource + private NoticeSendService noticeSendService; + /** * 新增评论 + * * @param functionalCaseCommentRequest functionalCaseCommentDTO - * @param userId 当前操作用户 + * @param userId 当前操作用户 * @return FunctionalCaseComment */ public FunctionalCaseComment saveComment(FunctionalCaseCommentRequest functionalCaseCommentRequest, String userId) { checkCase(functionalCaseCommentRequest); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest, userId); + if (StringUtils.equals(functionalCaseCommentRequest.getEvent(), NoticeConstants.Event.REPLY)) { + return saveCommentWidthNotice(functionalCaseCommentRequest, functionalCaseComment, userId); + } else { + return saveCommentWidthOutNotice(functionalCaseCommentRequest, functionalCaseComment, userId); + } + } + + + + /** + * 组装除通知人,被回复的id外的其他用例评论属性 + * + * @param functionalCaseCommentRequest 页面参数 + * @param userId 当前操作人 + * @return FunctionalCaseComment + */ + private static FunctionalCaseComment getFunctionalCaseComment(FunctionalCaseCommentRequest functionalCaseCommentRequest, String userId) { FunctionalCaseComment functionalCaseComment = new FunctionalCaseComment(); functionalCaseComment.setId(IDGenerator.nextStr()); functionalCaseComment.setCaseId(functionalCaseCommentRequest.getCaseId()); @@ -42,20 +82,120 @@ public class FunctionalCaseCommentService { functionalCaseComment.setCreateTime(System.currentTimeMillis()); functionalCaseComment.setUpdateTime(System.currentTimeMillis()); functionalCaseComment.setType(CommentEnum.CASE.toString()); - if (StringUtils.isNotBlank(functionalCaseCommentRequest.getNotifier())) { - functionalCaseComment.setNotifier(functionalCaseCommentRequest.getNotifier()); - } - if (StringUtils.isNotBlank(functionalCaseCommentRequest.getParentId())) { - functionalCaseComment.setParentId(functionalCaseCommentRequest.getParentId()); - } - functionalCaseCommentMapper.insert(functionalCaseComment); return functionalCaseComment; } private void checkCase(FunctionalCaseCommentRequest functionalCaseCommentRequest) { FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId()); - if (functionalCase ==null) { + if (functionalCase == null) { throw new MSException(Translator.get("case_comment.case_is_null")); } } + + /** + * 非REPLAY事件,保存 + * @param functionalCaseCommentRequest 页面参数 + * @param functionalCaseComment 被组装的半份数据 + * @return FunctionalCaseComment + */ + public FunctionalCaseComment saveCommentWidthOutNotice(FunctionalCaseCommentRequest functionalCaseCommentRequest, FunctionalCaseComment functionalCaseComment, String userId) { + if (StringUtils.isNotBlank(functionalCaseCommentRequest.getNotifier())) { + functionalCaseComment.setNotifier(functionalCaseCommentRequest.getNotifier()); + } + functionalCaseCommentMapper.insert(functionalCaseComment); + FunctionalCaseDTO functionalCaseDTO = functionalCaseNoticeService.getFunctionalCaseDTO(functionalCaseCommentRequest); + sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTO); + return functionalCaseComment; + } + + /** + * 如果是REPLAY事件,需要再次发送回复被@的通知 + * @param functionalCaseCommentRequest 页面参数 + * @param functionalCaseComment 被组装的半份数据 + * @return FunctionalCaseComment + */ + public FunctionalCaseComment saveCommentWidthNotice(FunctionalCaseCommentRequest functionalCaseCommentRequest, FunctionalCaseComment functionalCaseComment, String userId) { + checkParentId(functionalCaseCommentRequest, functionalCaseComment); + if (StringUtils.isBlank(functionalCaseCommentRequest.getReplyUser())) { + throw new MSException(Translator.get("case_comment.reply_user_is_null")); + } + functionalCaseCommentRequest.setReplyUser(functionalCaseCommentRequest.getReplyUser()); + if (StringUtils.isNotBlank(functionalCaseCommentRequest.getNotifier())) { + functionalCaseComment.setNotifier(functionalCaseCommentRequest.getNotifier()); + } + functionalCaseCommentMapper.insert(functionalCaseComment); + FunctionalCaseDTO functionalCaseDTOReply = functionalCaseNoticeService.getFunctionalCaseDTO(functionalCaseCommentRequest); + sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTOReply); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + FunctionalCaseDTO functionalCaseDTO = functionalCaseNoticeService.getFunctionalCaseDTO(functionalCaseCommentRequest); + //发通知 + sendNotice(functionalCaseCommentRequest, userId, functionalCaseDTO); + return functionalCaseComment; + } + + @Async + public void sendNotice(FunctionalCaseCommentRequest functionalCaseCommentRequest, String userId, FunctionalCaseDTO functionalCaseDTO) { + Map defaultTemplateMap = MessageTemplateUtils.getDefaultTemplateMap(); + String template = defaultTemplateMap.get(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK + "_" + functionalCaseCommentRequest.getEvent()); + Map defaultSubjectMap = MessageTemplateUtils.getDefaultTemplateSubjectMap(); + String subject = defaultSubjectMap.get(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK + "_" + functionalCaseCommentRequest.getEvent()); + List relatedUsers = getRelatedUsers(functionalCaseDTO.getRelatedUsers()); + User user = userMapper.selectByPrimaryKey(userId); + BeanMap beanMap = new BeanMap(functionalCaseDTO); + Map paramMap = new HashMap<>(beanMap); + paramMap.put(NoticeConstants.RelatedUser.OPERATOR, user.getName()); + NoticeModel noticeModel = NoticeModel.builder() + .operator(userId) + .context(template) + .subject(subject) + .paramMap(paramMap) + .event(functionalCaseCommentRequest.getEvent()) + .status((String) paramMap.get("status")) + .excludeSelf(true) + .relatedUsers(relatedUsers) + .build(); + noticeSendService.send(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, noticeModel); + } + + /** + * 检查被回复的评论是否为空 + * @param functionalCaseCommentRequest 页面参数 + * @param functionalCaseComment 被组装的半份数据 + */ + private void checkParentId(FunctionalCaseCommentRequest functionalCaseCommentRequest, FunctionalCaseComment functionalCaseComment) { + String parentId = functionalCaseCommentRequest.getParentId(); + if (StringUtils.isBlank(parentId)) { + throw new MSException(Translator.get("case_comment.parent_id_is_null")); + } + FunctionalCaseComment parentComment = functionalCaseCommentMapper.selectByPrimaryKey(parentId); + if (parentComment==null) { + throw new MSException(Translator.get("case_comment.parent_case_is_null")); + } + functionalCaseComment.setParentId(parentId); + } + + private List getRelatedUsers(Object relatedUsers) { + String relatedUser = (String) relatedUsers; + List relatedUserList = new ArrayList<>(); + if (StringUtils.isNotBlank(relatedUser)) { + relatedUserList = Arrays.asList(relatedUser.split(";")); + } + return relatedUserList; + } + + public void deleteComment(String commentId) { + FunctionalCaseComment functionalCaseComment = functionalCaseCommentMapper.selectByPrimaryKey(commentId); + if (functionalCaseComment == null) { + return; + } + //删除选中的评论下的所有回复 + FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andParentIdEqualTo(commentId); + List functionalCaseComments = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + List commentIds = new ArrayList<>(functionalCaseComments.stream().map(FunctionalCaseComment::getId).toList()); + commentIds.add(commentId); + functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andIdIn(commentIds); + functionalCaseCommentMapper.deleteByExample(functionalCaseCommentExample); + } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseNoticeService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseNoticeService.java index 120a04ac62..4d88dab44e 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseNoticeService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseNoticeService.java @@ -1,29 +1,121 @@ package io.metersphere.functional.service; import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.domain.FunctionalCaseCustomField; +import io.metersphere.functional.domain.FunctionalCaseCustomFieldExample; import io.metersphere.functional.dto.FunctionalCaseDTO; +import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.system.domain.CustomField; +import io.metersphere.system.domain.CustomFieldExample; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.mapper.CustomFieldMapper; +import io.metersphere.system.notice.constants.NoticeConstants; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + @Service public class FunctionalCaseNoticeService { @Resource private FunctionalCaseMapper functionalCaseMapper; - public FunctionalCaseDTO getRelatedUsers(FunctionalCaseCommentRequest functionalCaseCommentRequest){ + @Resource + private FunctionalCaseCustomFieldMapper functionalCaseCustomFieldMapper; + + @Resource + private CustomFieldMapper customFieldMapper; + + public FunctionalCaseDTO getFunctionalCaseDTO(FunctionalCaseCommentRequest functionalCaseCommentRequest){ FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId()); FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO(); if (functionalCase!=null) { BeanUtils.copyBean(functionalCaseDTO,functionalCase); } - functionalCaseDTO.setRelatedUsers(functionalCaseCommentRequest.getNotifier()); + setNotifier(functionalCaseCommentRequest, functionalCaseDTO); + List customFields = getCustomFields(functionalCaseCommentRequest.getCaseId()); + functionalCaseDTO.setFields(customFields); return functionalCaseDTO; } + /** + * + * 如果是REPLAY事件,需要判断有无@的人,如果有@的人且当前被回复的人不是同一人,这里只要被回复的人,如果是同一人,这里通知人为空,走AT事件 + * 如果不是REPLAY事件,需要判断有无被回复的人,如果被回复的人不在被@人里,则用页面参数传递的通知人,如果在,则排除这个人,如果没有被回复的人,用页面数据 + * @param functionalCaseCommentRequest 页面参数 + * @param functionalCaseDTO 发通知需要解析字段集合 + */ + private void setNotifier(FunctionalCaseCommentRequest functionalCaseCommentRequest, FunctionalCaseDTO functionalCaseDTO) { + String notifier = functionalCaseCommentRequest.getNotifier(); + String replyUser = functionalCaseCommentRequest.getReplyUser(); + if (StringUtils.equals(functionalCaseCommentRequest.getEvent(), NoticeConstants.Event.REPLY)) { + if (StringUtils.isNotBlank(replyUser)) { + if (StringUtils.isNotBlank(notifier)) { + List notifierList = Arrays.asList(notifier.split(";")); + if (!notifierList.contains(replyUser)) { + functionalCaseDTO.setRelatedUsers(replyUser); + } + } + } + } else { + if (StringUtils.isNotBlank(replyUser)) { + StringBuilder notifierStr = new StringBuilder(); + if (StringUtils.isNotBlank(notifier)) { + List notifierList = Arrays.asList(notifier.split(";")); + if (notifierList.contains(replyUser)) { + for (String notifierId : notifierList) { + if (!StringUtils.equals(notifierId, replyUser)) { + notifierStr.append(notifierId).append(";"); + } + } + } + else { + notifierStr = new StringBuilder(notifier); + } + functionalCaseDTO.setRelatedUsers(notifierStr.toString()); + } + } else { + functionalCaseDTO.setRelatedUsers(notifier); + } + } + } + + /** + * 根据用例id获取当前用例在使用的自定义的字段及其值 + * + * @param caseId 用例Id + * @return 返回 字段以及字段值的组合 + */ + private List getCustomFields(String caseId) { + FunctionalCaseCustomFieldExample functionalCaseCustomFieldExample = new FunctionalCaseCustomFieldExample(); + functionalCaseCustomFieldExample.createCriteria().andCaseIdEqualTo(caseId); + List functionalCaseCustomFields = functionalCaseCustomFieldMapper.selectByExample(functionalCaseCustomFieldExample); + ListoptionDTOList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(functionalCaseCustomFields)) { + Map fieldValueMap = functionalCaseCustomFields.stream().collect(Collectors.toMap(FunctionalCaseCustomField::getFieldId, FunctionalCaseCustomField::getValue)); + List fieldIds = functionalCaseCustomFields.stream().map(FunctionalCaseCustomField::getFieldId).distinct().toList(); + CustomFieldExample customFieldExample = new CustomFieldExample(); + customFieldExample.createCriteria().andIdIn(fieldIds); + List customFields = customFieldMapper.selectByExample(customFieldExample); + customFields.forEach(t->{ + OptionDTO optionDTO = new OptionDTO(); + optionDTO.setId(t.getName()); + optionDTO.setName(fieldValueMap.get(t.getId())); + optionDTOList.add(optionDTO); + }); + } + return optionDTOList; + } } 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 03f9e1b818..45cec6fce4 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 @@ -2,13 +2,23 @@ package io.metersphere.functional.controller; 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.mapper.FunctionalCaseCommentMapper; +import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper; import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.project.domain.Notification; import io.metersphere.project.domain.NotificationExample; import io.metersphere.project.mapper.NotificationMapper; +import io.metersphere.sdk.constants.CustomFieldType; import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.constants.TemplateScene; +import io.metersphere.sdk.constants.TemplateScopeType; import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.Translator; import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.domain.CustomField; +import io.metersphere.system.mapper.CustomFieldMapper; import io.metersphere.system.notice.constants.NoticeConstants; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; @@ -33,17 +43,29 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @AutoConfigureMockMvc public class FunctionalCaseCommentControllerTests { + @Resource private MockMvc mockMvc; @Resource private NotificationMapper notificationMapper; + @Resource + private FunctionalCaseCommentMapper functionalCaseCommentMapper; + + @Resource + private CustomFieldMapper customFieldMapper; + + @Resource + private FunctionalCaseCustomFieldMapper functionalCaseCustomFieldMapper; + public static final String SAVE_URL = "/functional/case/comment/save"; + public static final String DELETE_URL = "/functional/case/comment/delete/"; + private static String sessionId; private static String csrfToken; - private static String projectId = "100001100001"; + private static final String projectId = "100001100001"; @Test @Order(0) @@ -61,25 +83,17 @@ public class FunctionalCaseCommentControllerTests { @Test @Order(1) - public void saveCommentSuccess() throws Exception { + public void saveCommentATSuccess() throws Exception { FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-1"); functionalCaseCommentRequest.setContent("评论你好"); - MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId) - .header(SessionConstants.CSRF_TOKEN, csrfToken) - .header(SessionConstants.CURRENT_PROJECT, projectId) - .content(JSON.toJSONString(functionalCaseCommentRequest)) - .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); - FunctionalCaseComment functionalCaseComment = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCaseComment.class); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); NotificationExample notificationExample = new NotificationExample(); notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); - Assertions.assertTrue(notifications.size() > 0); + Assertions.assertFalse(notifications.isEmpty()); Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "default-project-member-user-guo-1")); System.out.println(notifications.get(0).getContent()); Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest")); @@ -89,41 +103,42 @@ public class FunctionalCaseCommentControllerTests { @Test @Order(2) - public void saveCommentFalse() throws Exception { + public void saveCommentATFalse() throws Exception { FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); functionalCaseCommentRequest.setCaseId("xiaomeinvGTestNo"); functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-1"); functionalCaseCommentRequest.setContent("评论你好"); - mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId) - .header(SessionConstants.CSRF_TOKEN, csrfToken) - .header(SessionConstants.CURRENT_PROJECT, projectId) - .content(JSON.toJSONString(functionalCaseCommentRequest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().is5xxServerError()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + ResultHolder resultHolder = postFalse(functionalCaseCommentRequest); + String jsonString = JSON.toJSONString(resultHolder.getData()); + System.out.println(jsonString); NotificationExample notificationExample = new NotificationExample(); notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTestNo").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); List notifications = notificationMapper.selectByExample(notificationExample); Assertions.assertTrue(CollectionUtils.isEmpty(notifications)); } - @Test - @Order(3) - public void saveCommentExcludeSelfSuccess() throws Exception { - FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); - functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); - functionalCaseCommentRequest.setNotifier("default-project-member-user-guo"); - functionalCaseCommentRequest.setContent("这个好"); + private ResultHolder postFalse(FunctionalCaseCommentRequest functionalCaseCommentRequest) throws Exception { MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) .header(SessionConstants.CURRENT_PROJECT, projectId) .content(JSON.toJSONString(functionalCaseCommentRequest)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) + .andExpect(status().is5xxServerError()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); - ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class); - FunctionalCaseComment functionalCaseComment = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCaseComment.class); + return JSON.parseObject(contentAsString, ResultHolder.class); + } + + @Test + @Order(3) + public void saveCommentATExcludeSelfSuccess() throws Exception { + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); + functionalCaseCommentRequest.setNotifier("default-project-member-user-guo"); + functionalCaseCommentRequest.setContent("这个好"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); NotificationExample notificationExample = new NotificationExample(); notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK).andReceiverEqualTo("default-project-member-user-guo"); List notifications = notificationMapper.selectByExample(notificationExample); @@ -133,4 +148,260 @@ public class FunctionalCaseCommentControllerTests { Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "这个好")); } + @Test + @Order(4) + public void saveCommentATNoNotifierSuccess() throws Exception { + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("这个好"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTestOne").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExample(notificationExample); + Assertions.assertTrue(CollectionUtils.isEmpty(notifications)); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTestOne")); + Assertions.assertTrue(StringUtils.isBlank(functionalCaseComment.getNotifier())); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "这个好")); + } + + + @Test + @Order(5) + public void saveOnlyCommentSuccess() throws Exception { + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.COMMENT); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTestOne").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); + Assertions.assertFalse(notifications.isEmpty()); + System.out.println(JSON.toJSONString(notifications)); + Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "gyq")); + System.out.println(notifications.get(0).getContent()); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTestOne")); + Assertions.assertTrue(StringUtils.isBlank(functionalCaseComment.getNotifier())); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好")); + } + + @Test + @Order(6) + public void saveCommentReplySuccess() throws Exception { + FunctionalCaseComment functionalCaseComment1 = getFunctionalCaseComment(); + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setReplyUser("default-project-member-user-guo"); + functionalCaseCommentRequest.setParentId(functionalCaseComment1.getId()); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTestOne").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); + Assertions.assertFalse(notifications.isEmpty()); + System.out.println(JSON.toJSONString(notifications)); + Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "gyq")); + System.out.println(notifications.get(0).getContent()); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTestOne")); + Assertions.assertTrue(StringUtils.isBlank(functionalCaseComment.getNotifier())); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好")); + } + + @Test + @Order(7) + public void saveCommentReplyNoParentId() throws Exception { + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + ResultHolder resultHolder = postFalse(functionalCaseCommentRequest); + String message = resultHolder.getMessage(); + Assertions.assertTrue(StringUtils.equals(message, Translator.get("case_comment.parent_id_is_null"))); + + } + + @Test + @Order(8) + public void saveCommentReplyNoParent() throws Exception { + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setParentId("noComment"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + ResultHolder resultHolder = postFalse(functionalCaseCommentRequest); + String message = resultHolder.getMessage(); + Assertions.assertTrue(StringUtils.equals(message, Translator.get("case_comment.parent_case_is_null"))); + + + } + + @Test + @Order(9) + public void saveCommentReplyNotifierSuccess() throws Exception { + FunctionalCaseComment functionalCaseComment1 = getFunctionalCaseComment(); + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-2"); + functionalCaseCommentRequest.setReplyUser("default-project-member-user-guo"); + functionalCaseCommentRequest.setParentId(functionalCaseComment1.getId()); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); + Assertions.assertFalse(notifications.isEmpty()); + System.out.println(JSON.toJSONString(notifications)); + Assertions.assertTrue(StringUtils.equals(notifications.get(1).getReceiver(), "default-project-member-user-guo-2")); + System.out.println(notifications.get(0).getContent()); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest")); + Assertions.assertTrue(StringUtils.isNotBlank(functionalCaseComment.getNotifier())); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好")); + } + + @Test + @Order(10) + public void saveCommentReplyNoReply() throws Exception { + FunctionalCaseComment functionalCaseComment1 = getFunctionalCaseComment(); + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTestOne"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setParentId(functionalCaseComment1.getId()); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + ResultHolder resultHolder = postFalse(functionalCaseCommentRequest); + String message = resultHolder.getMessage(); + Assertions.assertTrue(StringUtils.equals(message, Translator.get("case_comment.reply_user_is_null"))); + } + + + @Test + @Order(11) + public void saveCommentWidthCustomFields() throws Exception { + CustomField customField = new CustomField(); + customField.setId("gyq_custom_field_one"); + customField.setName("testLevel"); + customField.setType(CustomFieldType.INPUT.toString()); + customField.setScene(TemplateScene.FUNCTIONAL.name()); + customField.setCreateUser("gyq"); + customField.setCreateTime(System.currentTimeMillis()); + customField.setUpdateTime(System.currentTimeMillis()); + customField.setRefId("gyq_custom_field_one"); + customField.setScopeId(projectId); + customField.setScopeType(TemplateScopeType.PROJECT.name()); + customField.setInternal(false); + customField.setEnableOptionKey(false); + customField.setRemark("1"); + customFieldMapper.insertSelective(customField); + FunctionalCaseCustomField functionalCaseCustomField = new FunctionalCaseCustomField(); + functionalCaseCustomField.setCaseId("xiaomeinvGTest"); + functionalCaseCustomField.setFieldId("gyq_custom_field_one"); + functionalCaseCustomField.setValue("1"); + functionalCaseCustomFieldMapper.insertSelective(functionalCaseCustomField); + + FunctionalCaseComment functionalCaseComment1 = getFunctionalCaseComment(); + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); + functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-3;default-project-member-user-guo-4;"); + functionalCaseCommentRequest.setContent("评论你好"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.REPLY); + functionalCaseCommentRequest.setReplyUser("default-project-member-user-guo"); + functionalCaseCommentRequest.setParentId(functionalCaseComment1.getId()); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); + Assertions.assertFalse(notifications.isEmpty()); + // Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "default-project-member-user-guo-1")); + System.out.println(JSON.toJSONString(notifications)); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest")); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getNotifier(), "default-project-member-user-guo-3;default-project-member-user-guo-4;")); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好")); + } + + @Test + @Order(12) + public void saveCommentATWidthReplyUser() throws Exception { + FunctionalCaseComment functionalCaseComment1 = getFunctionalCaseComment(); + FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest(); + functionalCaseCommentRequest.setCaseId("xiaomeinvGTest"); + functionalCaseCommentRequest.setNotifier("default-project-member-user-guo;default-project-member-user-guo-4;"); + functionalCaseCommentRequest.setContent("评论你好哇"); + functionalCaseCommentRequest.setEvent(NoticeConstants.Event.AT); + functionalCaseCommentRequest.setReplyUser("default-project-member-user-guo"); + functionalCaseCommentRequest.setParentId(functionalCaseComment1.getId()); + FunctionalCaseComment functionalCaseComment = getFunctionalCaseComment(functionalCaseCommentRequest); + NotificationExample notificationExample = new NotificationExample(); + notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK); + List notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample); + Assertions.assertFalse(notifications.isEmpty()); + // Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "default-project-member-user-guo-1")); + System.out.println(JSON.toJSONString(notifications)); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest")); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getNotifier(), "default-project-member-user-guo;default-project-member-user-guo-4;")); + Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好哇")); + } + + @Test + @Order(13) + 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;"); + List functionalCaseComments = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + System.out.println(JSON.toJSONString(functionalCaseComments)); + String id = functionalCaseComments.get(0).getId(); + Assertions.assertFalse(functionalCaseComments.isEmpty()); + delFunctionalCaseComment(id); + functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andParentIdEqualTo(id); + List functionalCaseComments1 = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + Assertions.assertTrue(functionalCaseComments1.isEmpty()); + FunctionalCaseComment functionalCaseComment = functionalCaseCommentMapper.selectByPrimaryKey(id); + Assertions.assertNull(functionalCaseComment); + + } + + @Test + @Order(14) + public void deleteNoCommentSuccess() throws Exception { + delFunctionalCaseComment("no_comment"); + FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andParentIdEqualTo("no_comment"); + List functionalCaseComments1 = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + Assertions.assertTrue(functionalCaseComments1.isEmpty()); + FunctionalCaseComment functionalCaseComment = functionalCaseCommentMapper.selectByPrimaryKey("no_comment"); + 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) + .header(SessionConstants.CURRENT_PROJECT, projectId) + .content(JSON.toJSONString(functionalCaseCommentRequest)) + .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); + return JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCaseComment.class); + } + + private void delFunctionalCaseComment(String commentId) throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(DELETE_URL+commentId).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(); + } + + private FunctionalCaseComment getFunctionalCaseComment() { + FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); + functionalCaseCommentExample.createCriteria().andCaseIdEqualTo("xiaomeinvGTest"); + List functionalCaseComments = functionalCaseCommentMapper.selectByExample(functionalCaseCommentExample); + return functionalCaseComments.get(0); + } + } 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 3cb2d35c1d..340935d4a3 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 @@ -1,11 +1,17 @@ INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) -VALUES ('xiaomeinvGTest', 1000001, 'test_guo', '100001100001', 'test_guo', '郭雨琦测试', 'UN_REVIEWED', null, 'text', +VALUES ('xiaomeinvGTest', 1000001, 'test_guo', '100001100001', 'test_guo', 'gyqTest', 'UN_REVIEWED', null, 'text', 10001, '111', 'xiaomeinvGTest', 'success', false, false, true, 'gyq', 'gyq', null, 1698058347559, 1698058347559, null); - +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, + version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, + update_user, delete_user, create_time, update_time, delete_time) +VALUES ('xiaomeinvGTestOne', 1000001, 'test_guo', '100001100001', 'test_guo', 'gyqTest1', 'UN_REVIEWED', null, 'text', + 10001, '111', 'xiaomeinvGTestOne', 'success', false, false, true, 'gyq', 'gyq', null, 1698058347559, + 1698058347559, + null); INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, last_project_id, create_user, update_user, deleted) @@ -15,15 +21,34 @@ VALUES ('default-project-member-user-guo', 'default-project-member-user1', 'proj ('default-project-member-user-guo-1', 'default-project-member-user2', 'project-member-guo2@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 0), + ('default-project-member-user-guo-2', 'default-project-member-user3', 'project-member-guo3@metersphere.io', + MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', + 'admin', 0), + ('default-project-member-user-guo-3', 'default-project-member-user4', 'project-member-guo4@metersphere.io', + MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', + 'admin', 0), + ('default-project-member-user-guo-4', 'default-project-member-user5', 'project-member-guo5@metersphere.io', + MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', + 'admin', 0), ('default-project-member-user-guo-del', 'default-project-member-userDel', 'project-member-guo-del@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, + NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 1), + ('gyq', 'gyq', 'gyq@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 1); INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES (UUID(), 'default-project-member-user-guo', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'), (UUID(), 'default-project-member-user-guo-1', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'), + (UUID(), 'default-project-member-user-guo-2', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), + (UUID(), 'default-project-member-user-guo-3', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), + (UUID(), 'default-project-member-user-guo-4', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), (UUID(), 'default-project-member-user-guo-del', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), + (UUID(), 'gyq', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'); INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) @@ -31,11 +56,20 @@ VALUES (UUID(), 'default-project-member-user-guo', 'project_admin', '10000110000 'admin'), (UUID(), 'default-project-member-user-guo-1', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'), + (UUID(), 'default-project-member-user-guo-2', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), + (UUID(), 'default-project-member-user-guo-3', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), + (UUID(), 'default-project-member-user-guo-4', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'), (UUID(), 'default-project-member-user-guo-del', 'project_admin', '100001100001', '100001', - UNIX_TIMESTAMP() * 1000, 'admin'); + UNIX_TIMESTAMP() * 1000, 'admin'), + (UUID(), 'gyq', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'); 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:READ+ADD'); + ('user_role_guo_permission2', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ+DELETE'), + ('user_role_guo_permission3', 'project_admin', 'FUNCTIONAL_CASE:READ+ADD'); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/CreateRobotResourceService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/CreateRobotResourceService.java index 18b5322f0b..4512f2c72b 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/CreateRobotResourceService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/CreateRobotResourceService.java @@ -29,7 +29,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Component public class CreateRobotResourceService implements CreateProjectResourceService { @@ -77,6 +79,7 @@ public class CreateRobotResourceService implements CreateProjectResourceService public void setMessageTask(String projectId, String defaultRobotId) { StringBuilder jsonStr = new StringBuilder(); InputStream inputStream = getClass().getResourceAsStream("/message_task.json"); + assert inputStream != null; BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; try { @@ -91,15 +94,21 @@ public class CreateRobotResourceService implements CreateProjectResourceService SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); MessageTaskMapper mapper = sqlSession.getMapper(MessageTaskMapper.class); MessageTaskBlobMapper blobMapper = sqlSession.getMapper(MessageTaskBlobMapper.class); + //生成消息管理默认显示数据 + setTemplateMessageTask(projectId, defaultRobotId, jsonStr, mapper, blobMapper); + //生成 内置at 的消息管理数据 + setAtMessageTask(projectId, defaultRobotId, mapper, blobMapper); + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + + private static void setTemplateMessageTask(String projectId, String defaultRobotId, StringBuilder jsonStr, MessageTaskMapper mapper, MessageTaskBlobMapper blobMapper) { List messageTaskDTOList = JSON.parseArray(jsonStr.toString(), MessageTaskDTO.class); for (MessageTaskDTO messageTaskDTO : messageTaskDTOList) { List messageTaskTypeDTOList = messageTaskDTO.getMessageTaskTypeDTOList(); for (MessageTaskTypeDTO messageTaskTypeDTO : messageTaskTypeDTOList) { String taskType = messageTaskTypeDTO.getTaskType(); - if (taskType.contains("AT")) { - continue; - } List messageTaskDetailDTOList = messageTaskTypeDTO.getMessageTaskDetailDTOList(); for (MessageTaskDetailDTO messageTaskDetailDTO : messageTaskDetailDTOList) { String event = messageTaskDetailDTO.getEvent(); @@ -134,8 +143,45 @@ public class CreateRobotResourceService implements CreateProjectResourceService } } } + } - sqlSession.flushStatements(); - SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + private static void setAtMessageTask(String projectId, String defaultRobotId, MessageTaskMapper mapper, MessageTaskBlobMapper blobMapper) { + Map> taskTypeEventMap = new HashMap<>(); + ListbugEventList = new ArrayList<>(); + bugEventList.add(NoticeConstants.Event.AT); + bugEventList.add(NoticeConstants.Event.REPLY); + taskTypeEventMap.put(NoticeConstants.TaskType.BUG_TASK,bugEventList); + ListfuncationalCaseEventList = new ArrayList<>(); + funcationalCaseEventList.add(NoticeConstants.Event.AT); + funcationalCaseEventList.add(NoticeConstants.Event.REPLY); + funcationalCaseEventList.add(NoticeConstants.Event.REVIEW_AT); + funcationalCaseEventList.add(NoticeConstants.Event.EXECUTE_AT); + taskTypeEventMap.put(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK,funcationalCaseEventList); + taskTypeEventMap.forEach((taskType, eventList)->{ + for (String event : eventList) { + String id = IDGenerator.nextStr(); + MessageTask messageTask = new MessageTask(); + messageTask.setId(id); + messageTask.setEvent(event); + messageTask.setTaskType(taskType); + messageTask.setReceiver("NONE"); + messageTask.setProjectId(projectId); + messageTask.setProjectRobotId(defaultRobotId); + messageTask.setEnable(true); + messageTask.setTestId("NONE"); + messageTask.setCreateUser("admin"); + messageTask.setCreateTime(System.currentTimeMillis()); + messageTask.setUpdateUser("admin"); + messageTask.setUpdateTime(System.currentTimeMillis()); + messageTask.setSubject(""); + messageTask.setUseDefaultSubject(true); + messageTask.setUseDefaultTemplate(true); + MessageTaskBlob messageTaskBlob = new MessageTaskBlob(); + messageTaskBlob.setId(id); + messageTaskBlob.setTemplate(""); + mapper.insert(messageTask); + blobMapper.insert(messageTaskBlob); + } + }); } } diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/CreateRobotResourceTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/CreateRobotResourceTests.java index f33ef9a7ee..1b2a82ce33 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/CreateRobotResourceTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/CreateRobotResourceTests.java @@ -1,8 +1,11 @@ package io.metersphere.project.controller; +import io.metersphere.project.domain.MessageTask; +import io.metersphere.project.domain.MessageTaskExample; import io.metersphere.project.domain.Project; import io.metersphere.project.domain.ProjectRobot; import io.metersphere.project.dto.MessageTaskDTO; +import io.metersphere.project.mapper.MessageTaskMapper; import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.SessionConstants; import io.metersphere.sdk.util.JSON; @@ -42,6 +45,9 @@ public class CreateRobotResourceTests extends BaseTest { @Resource private ProjectMapper projectMapper; + @Resource + private MessageTaskMapper messageTaskMapper; + @Test @Order(1) public void testCreateResource() throws Exception { @@ -60,6 +66,10 @@ public class CreateRobotResourceTests extends BaseTest { List projectRobotAfters = getList(id); Assertions.assertEquals(2, projectRobotAfters.size()); List messageList = getMessageList(id); + MessageTaskExample messageTaskExample = new MessageTaskExample(); + messageTaskExample.createCriteria().andProjectIdEqualTo(id).andEventLike("AT"); + List messageTasks = messageTaskMapper.selectByExample(messageTaskExample); + Assertions.assertTrue(messageTasks.size() > 0); Assertions.assertTrue(messageList.size() > 0); } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/constants/NoticeConstants.java b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/constants/NoticeConstants.java index ed63155fa2..660f47329e 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/constants/NoticeConstants.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/constants/NoticeConstants.java @@ -139,7 +139,7 @@ public interface NoticeConstants { String AT = "AT"; @Schema(description = "message.replay") - String REPLAY = "REPLAY"; + String REPLY = "REPLY"; @Schema(description = "message.review_passed") String REVIEW_PASSED = "REVIEW_PASSED"; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AbstractNoticeSender.java b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AbstractNoticeSender.java index c72d043b63..aaf87a1985 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AbstractNoticeSender.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AbstractNoticeSender.java @@ -21,7 +21,6 @@ import io.metersphere.plan.domain.TestPlanFollowerExample; import io.metersphere.plan.mapper.TestPlanFollowerMapper; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; -import io.metersphere.system.domain.CustomField; import io.metersphere.system.domain.User; import io.metersphere.system.domain.UserExample; import io.metersphere.system.mapper.CustomFieldMapper; @@ -33,7 +32,6 @@ import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.notice.constants.NotificationConstants; import io.metersphere.system.notice.utils.MessageTemplateUtils; import jakarta.annotation.Resource; -import org.apache.commons.beanutils.BeanMap; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -91,16 +89,13 @@ public abstract class AbstractNoticeSender implements NoticeSender { } if (CollectionUtils.isNotEmpty(fields)) { for (Object o : fields) { - Map jsonObject = new BeanMap(o); - String id = (String) jsonObject.get("id"); - CustomField customField = customFieldMapper.selectByPrimaryKey(id); - if (customField == null) { - continue; - } - Object value = jsonObject.get("value"); - if (value instanceof String && StringUtils.isNotEmpty((String) value)) { + String jsonFields = JSON.toJSONString(o); + Map jsonObject = JSON.parseObject(jsonFields, Map.class); + String customFieldName = (String) jsonObject.get("id"); + Object value = jsonObject.get("name"); + if (value instanceof String && StringUtils.isNotBlank((String) value)) { String v = StringUtils.unwrap((String) value, "\""); - noticeModel.getParamMap().put(customField.getName(), v); // 处理人 + noticeModel.getParamMap().put(customFieldName, v); // 处理人 } } } @@ -115,7 +110,7 @@ public abstract class AbstractNoticeSender implements NoticeSender { for (String userId : messageDetail.getReceiverIds()) { switch (userId) { case NoticeConstants.RelatedUser.CREATE_USER -> { - String createUser = (String) paramMap.get(NoticeConstants.RelatedUser.CREATE_USER); + String createUser = (String) paramMap.get("createUser"); if (StringUtils.isNotBlank(createUser)) { toUsers.add(new Receiver(createUser, NotificationConstants.Type.SYSTEM_NOTICE.name())); } @@ -140,7 +135,7 @@ public abstract class AbstractNoticeSender implements NoticeSender { } //处理评论人 - if (event.contains(NoticeConstants.Event.AT) || event.contains(NoticeConstants.Event.REPLAY)) { + if (event.contains(NoticeConstants.Event.AT) || event.contains(NoticeConstants.Event.REPLY)) { if (CollectionUtils.isNotEmpty(noticeModel.getRelatedUsers())) { for (String relatedUser : noticeModel.getRelatedUsers()) { toUsers.add(new Receiver(relatedUser, NotificationConstants.Type.MENTIONED_ME.name())); diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AfterReturningNoticeSendService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AfterReturningNoticeSendService.java index 7891508307..850991400c 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AfterReturningNoticeSendService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/AfterReturningNoticeSendService.java @@ -4,7 +4,6 @@ package io.metersphere.system.notice.sender; import io.metersphere.system.dto.sdk.BaseSystemConfigDTO; import io.metersphere.system.dto.sdk.SessionUser; import io.metersphere.system.notice.NoticeModel; -import io.metersphere.system.notice.annotation.SendNotice; import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.notice.utils.MessageTemplateUtils; import io.metersphere.system.service.NoticeSendService; @@ -24,7 +23,7 @@ public class AfterReturningNoticeSendService { private NoticeSendService noticeSendService; @Async - public void sendNotice(SendNotice sendNotice, List resources, SessionUser sessionUser, String currentProjectId) { + public void sendNotice(String taskType, String event, List resources, SessionUser sessionUser, String currentProjectId) { // 有批量操作发送多次 BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo(); @@ -39,9 +38,9 @@ public class AfterReturningNoticeSendService { // 占位符 handleDefaultValues(paramMap); - String context = getContext(sendNotice); + String context = getContext(taskType, event); - String subject = getSubject(sendNotice); + String subject = getSubject(taskType, event); List relatedUsers = getRelatedUsers(resource.get("relatedUsers")); @@ -50,12 +49,12 @@ public class AfterReturningNoticeSendService { .context(context) .subject(subject) .paramMap(paramMap) - .event(sendNotice.event()) + .event(event) .status((String) paramMap.get("status")) .excludeSelf(true) .relatedUsers(relatedUsers) .build(); - noticeSendService.send(sendNotice.taskType(), noticeModel); + noticeSendService.send(taskType, noticeModel); } } @@ -63,14 +62,14 @@ public class AfterReturningNoticeSendService { String relatedUser = (String) relatedUsers; List relatedUserList = new ArrayList<>(); if (StringUtils.isNotBlank(relatedUser)) { - relatedUserList = Arrays.asList(relatedUser.split(",")); + relatedUserList = Arrays.asList(relatedUser.split(";")); } return relatedUserList; } - private String getSubject(SendNotice sendNotice) { + private String getSubject(String taskType, String event) { Map defaultTemplateTitleMap = MessageTemplateUtils.getDefaultTemplateSubjectMap(); - return defaultTemplateTitleMap.get(sendNotice.taskType() + "_" + sendNotice.event()); + return defaultTemplateTitleMap.get(taskType + "_" + event); } @@ -81,8 +80,8 @@ public class AfterReturningNoticeSendService { paramMap.put("planShareUrl", StringUtils.EMPTY); // 占位符 } - private String getContext(SendNotice sendNotice) { + private String getContext(String taskType, String event) { Map defaultTemplateMap = MessageTemplateUtils.getDefaultTemplateMap(); - return defaultTemplateMap.get(sendNotice.taskType() + "_" + sendNotice.event()); + return defaultTemplateMap.get(taskType + "_" + event); } } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/SendNoticeAspect.java b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/SendNoticeAspect.java index 1b0b0f5cbe..6bd7930b79 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/SendNoticeAspect.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/notice/sender/SendNoticeAspect.java @@ -89,14 +89,14 @@ public class SendNoticeAspect { String[] params = discoverer.getParameterNames(method); //获取操作 SendNotice sendNotice = method.getAnnotation(SendNotice.class); + EvaluationContext context = new StandardEvaluationContext(); + for (int len = 0; len < params.length; len++) { + context.setVariable(params[len], args[len]); + } // 再次从数据库查询一次内容,方便获取最新参数 - if (StringUtils.isNotEmpty(sendNotice.target())) { //将参数纳入Spring管理 - EvaluationContext context = new StandardEvaluationContext(); - for (int len = 0; len < params.length; len++) { - context.setVariable(params[len], args[len]); - } + context.setVariable("targetClass", CommonBeanFactory.getBean(sendNotice.targetClass())); String target = sendNotice.target(); @@ -124,10 +124,32 @@ public class SendNoticeAspect { } else { resources.add(new BeanMap(retValue)); } + String taskType = sendNotice.taskType(); + // taskType + if (StringUtils.isNotEmpty(sendNotice.taskType())) { + try { + Expression titleExp = parser.parseExpression(taskType); + taskType = titleExp.getValue(resources, String.class); + + } catch (Exception e) { + LogUtils.info("使用原值"); + } + } + String event = sendNotice.event(); + // event + if (StringUtils.isNotEmpty(sendNotice.event())) { + try { + Expression titleExp = parser.parseExpression(event); + event = titleExp.getValue(context, String.class); + } catch (Exception e) { + LogUtils.info("使用原值"); + } + } SessionUser sessionUser = SessionUtils.getUser(); String currentProjectId = SessionUtils.getCurrentProjectId(); - afterReturningNoticeSendService.sendNotice(sendNotice, resources, sessionUser, currentProjectId); + LogUtils.info("event:"+event); + afterReturningNoticeSendService.sendNotice(taskType,event, resources, sessionUser, currentProjectId); } catch (Exception e) { LogUtils.error(e.getMessage(), e); } finally { diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/MessageDetailService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/MessageDetailService.java index 6c03fb81de..da9420e18c 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/MessageDetailService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/MessageDetailService.java @@ -112,10 +112,14 @@ public class MessageDetailService { if (!messageTask.getUseDefaultSubject() && StringUtils.isNotBlank(messageTask.getSubject())) { messageDetail.setSubject(messageTask.getSubject()); } else { - String subject = getSubject(messageTask.getTaskType(), messageTask.getEvent()); - messageDetail.setSubject(subject); + if (StringUtils.equals(projectRobot.getPlatform(),"MAIL")) { + String subject = getMailSubject(messageTask.getTaskType(), messageTask.getEvent()); + messageDetail.setSubject(subject); + } else { + String subject = getSubject(messageTask.getTaskType(), messageTask.getEvent()); + messageDetail.setSubject(subject); + } } - MessageTaskBlob messageTaskBlob = messageTaskBlobMap.get(messageTask.getId()); if (!messageTask.getUseDefaultTemplate() && StringUtils.isNotBlank(messageTaskBlob.getTemplate())) { messageDetail.setTemplate(messageTaskBlob.getTemplate()); @@ -132,12 +136,15 @@ public class MessageDetailService { return defaultTemplateMap.get(taskType + "_" + event); } - private String getSubject(String taskType, String event) { + private String getMailSubject(String taskType, String event) { Map defaultTemplateTitleMap = MessageTemplateUtils.getDefaultTemplateSubjectMap(); return "MeterSphere " + defaultTemplateTitleMap.get(taskType + "_" + event); } - + private String getSubject(String taskType, String event) { + Map defaultTemplateTitleMap = MessageTemplateUtils.getDefaultTemplateSubjectMap(); + return defaultTemplateTitleMap.get(taskType + "_" + event); + } /** * 根据用例ID获取所有该用例的定时任务的任务通知 * diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/AfterReturningNoticeSendServiceTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/AfterReturningNoticeSendServiceTests.java new file mode 100644 index 0000000000..34e68acfbc --- /dev/null +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/AfterReturningNoticeSendServiceTests.java @@ -0,0 +1,115 @@ +package io.metersphere.system.controller; + +import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.domain.FunctionalCaseCustomField; +import io.metersphere.functional.domain.FunctionalCaseCustomFieldExample; +import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper; +import io.metersphere.functional.mapper.FunctionalCaseMapper; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.base.BaseTest; +import io.metersphere.system.controller.dto.FunctionalCaseDTO; +import io.metersphere.system.domain.CustomField; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.dto.sdk.SessionUser; +import io.metersphere.system.dto.user.UserDTO; +import io.metersphere.system.mapper.CustomFieldMapper; +import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.notice.sender.AfterReturningNoticeSendService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class AfterReturningNoticeSendServiceTests extends BaseTest { + + + @Resource + private AfterReturningNoticeSendService afterReturningNoticeSendService; + @Resource + private FunctionalCaseMapper functionalCaseMapper; + @Resource + private CustomFieldMapper customFieldMapper; + @Resource + private FunctionalCaseCustomFieldMapper functionalCaseCustomFieldMapper; + + + private ThreadLocal source = new ThreadLocal<>(); + + @Test + @Order(1) + @Sql(scripts = {"/dml/init_aspect.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void noticeSuccess() { + String taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK; + ListeventList = new ArrayList<>(); + getTypeList(eventList); + FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey("aspect_gyq_one"); + FunctionalCaseCustomFieldExample functionalCaseCustomFieldExample = new FunctionalCaseCustomFieldExample(); + functionalCaseCustomFieldExample.createCriteria().andCaseIdEqualTo("aspect_gyq_one"); + List functionalCaseCustomFields = functionalCaseCustomFieldMapper.selectByExample(functionalCaseCustomFieldExample); + String fieldId = functionalCaseCustomFields.get(0).getFieldId(); + CustomField customFields = customFieldMapper.selectByPrimaryKey(fieldId); + + ListoptionDTOList = new ArrayList<>(); + OptionDTO optionDTO = new OptionDTO(); + optionDTO.setId(customFields.getName()); + optionDTO.setName(functionalCaseCustomFields.get(0).getValue()); + optionDTOList.add(optionDTO); + FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO(); + BeanUtils.copyBean(functionalCaseDTO,functionalCase); + functionalCaseDTO.setRelatedUsers("aspect-member-user-guo"); + functionalCaseDTO.setFields(optionDTOList); + + String jsonObject = JSON.toJSONString(functionalCaseDTO); + if (!StringUtils.equals("{}", jsonObject) && !StringUtils.equals("[]", jsonObject)) { + source.set(JSON.toJSONString(functionalCaseDTO)); + } + + List resources = new ArrayList<>(); + String v = source.get(); + if (StringUtils.isNotBlank(v)) { + // array + if (StringUtils.startsWith(v, "[")) { + resources.addAll(JSON.parseArray(v, Map.class)); + } + // map + else { + Map value = JSON.parseObject(v, Map.class); + resources.add(value); + } + } + + UserDTO userDTO = new UserDTO(); + userDTO.setId(sessionId); + userDTO.setName("admin"); + SessionUser user = SessionUser.fromUser(userDTO, sessionId); + + for (String event : eventList) { + afterReturningNoticeSendService.sendNotice(taskType, event,resources, user, "100001100001"); + } + + } + + private static void getTypeList(ListeventList ) { + eventList.add(NoticeConstants.Event.CREATE); + eventList.add(NoticeConstants.Event.UPDATE); + eventList.add(NoticeConstants.Event.AT); + eventList.add(NoticeConstants.Event.REPLY); + } + + +} diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/dto/FunctionalCaseDTO.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/dto/FunctionalCaseDTO.java new file mode 100644 index 0000000000..b0ea835e1c --- /dev/null +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/dto/FunctionalCaseDTO.java @@ -0,0 +1,19 @@ +package io.metersphere.system.controller.dto; + +import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class FunctionalCaseDTO extends FunctionalCase { + + @Schema(description = "评论@的人, 多个以';'隔开") + private String relatedUsers; + + @Schema(description = "自定义字段的值") + private List fields; + +} diff --git a/backend/services/system-setting/src/test/resources/dml/init_aspect.sql b/backend/services/system-setting/src/test/resources/dml/init_aspect.sql new file mode 100644 index 0000000000..4c79d121c4 --- /dev/null +++ b/backend/services/system-setting/src/test/resources/dml/init_aspect.sql @@ -0,0 +1,35 @@ +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, + version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, + update_user, delete_user, create_time, update_time, delete_time) +VALUES ('aspect_gyq_one', 1000001, 'test_guo', '100001100001', 'test_guo', 'gyq_test_one', 'UN_REVIEWED', null, 'text', + 10001, '111', 'aspect_gyq_one', 'success', false, false, true, 'gyq', 'gyq', null, 1698058347559, 1698058347559, + null); + +INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, + version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, + update_user, delete_user, create_time, update_time, delete_time) +VALUES ('aspect_gyq_two', 1000001, 'test_guo', '100001100001', 'test_guo', 'gyq_test_two', 'UN_REVIEWED', null, 'text', + 10001, '111', 'aspect_gyq_two', 'success', false, false, true, 'gyq', 'gyq', null, 1698058347559, 1698058347559, + null); + +INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, + last_project_id, create_user, update_user, deleted) +VALUES ('aspect-member-user-guo', 'aspect-member-user-guo', 'aspect-member-user-guo@metersphere.io', + MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', + 'admin', 0); + +INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) +VALUES (UUID(), 'aspect-member-user-guo', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'); + +INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) +VALUES (UUID(), 'aspect-member-user-guo', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000, + 'admin'); + +INSERT INTO user_role_permission(id, role_id, permission_id) +VALUES ('aspect_user_role_guo_permission1', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ+ADD'), + ('aspect_user_role_guo_permission2', 'project_admin', 'FUNCTIONAL_CASE:READ+ADD'); + +INSERT INTO custom_field(id, name, scene, type, remark, create_time, update_time, create_user, ref_id, scope_id, internal, enable_option_key, scope_type) +values ('aspect_test_one','aspect_test','FUNCTIONAL', 'INPUT','aspect_test', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin' , 'aspect_test_one', '100001100001',false, false, 'PROJECT'); + +INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('aspect_gyq_one', 'aspect_test_one', 'hello');