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 0c5b911a5c..e4db6fc719 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 @@ -141,4 +141,6 @@ 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 +case_comment.id_is_null=The current comment id is empty +un_follow_functional_case=unfollow functional case +follow_functional_case=followed functional case \ 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 3d9a66e05b..1730cc9413 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 @@ -141,4 +141,6 @@ 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 +case_comment.id_is_null=当前评论id为空 +un_follow_functional_case=取消关注用例 +follow_functional_case=关注用例 \ 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 dd68a88f3a..a9df2b04a7 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 @@ -141,4 +141,6 @@ 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 +case_comment.id_is_null=目前評論id為空 +un_follow_functional_case=取消關注用例 +follow_functional_case=關注用例 \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java index 8013269c81..1f13d666c1 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java @@ -4,6 +4,8 @@ import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.dto.FunctionalCaseDetailDTO; import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; +import io.metersphere.functional.request.FunctionalCaseFollowerRequest; +import io.metersphere.functional.service.FunctionalCaseLogService; import io.metersphere.functional.service.FunctionalCaseService; import io.metersphere.project.service.ProjectTemplateService; import io.metersphere.sdk.constants.PermissionConstants; @@ -15,6 +17,7 @@ import io.metersphere.system.utils.SessionUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotBlank; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -50,7 +53,7 @@ public class FunctionalCaseController { @PostMapping("/add") @Operation(summary = "功能用例-新增用例") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD) - @Log(type = OperationLogType.ADD, expression = "#msClass.addFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseService.class) + @Log(type = OperationLogType.ADD, expression = "#msClass.addFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseLogService.class) public FunctionalCase addFunctionalCase(@Validated @RequestPart("request") FunctionalCaseAddRequest request, @RequestPart(value = "files", required = false) List files) { String userId = SessionUtils.getUserId(); return functionalCaseService.addFunctionalCase(request, files, userId); @@ -68,9 +71,27 @@ public class FunctionalCaseController { @PostMapping("/update") @Operation(summary = "功能用例-更新用例") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) - @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseService.class) + @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseLogService.class) public FunctionalCase updateFunctionalCase(@Validated @RequestPart("request") FunctionalCaseEditRequest request, @RequestPart(value = "files", required = false) List files) { String userId = SessionUtils.getUserId(); return functionalCaseService.updateFunctionalCase(request, files, userId); } + + + @PostMapping("/edit/follower") + @Operation(summary = "功能用例-关注/取消关注用例") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) + @Log(type = OperationLogType.UPDATE, expression = "#msClass.editFollower(#request)", msClass = FunctionalCaseLogService.class) + public void editFollower(@Validated @RequestBody FunctionalCaseFollowerRequest request) { + functionalCaseService.editFollower(request.getFunctionalCaseId(), request.getUserId()); + } + + + @GetMapping("/follower/{functionalCaseId}") + @Operation(summary = "功能用例-获取用例关注人") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) + public List getFollower(@PathVariable @NotBlank(message = "{functional_case.id.not_blank}") String functionalCaseId) { + return functionalCaseService.getFollower(functionalCaseId); + } + } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseFollowerRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseFollowerRequest.java new file mode 100644 index 0000000000..0f2a75a03f --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseFollowerRequest.java @@ -0,0 +1,26 @@ +package io.metersphere.functional.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * @author wx + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class FunctionalCaseFollowerRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "用户id") + @NotBlank(message = "{user_id.not_blank}") + private String userId; + + @Schema(description = "用例id") + @NotBlank(message = "{functional_case.id.not_blank}") + private String functionalCaseId; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java new file mode 100644 index 0000000000..fe3b36bf9b --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java @@ -0,0 +1,113 @@ +package io.metersphere.functional.service; + +import io.metersphere.functional.domain.FunctionalCaseFollower; +import io.metersphere.functional.domain.FunctionalCaseFollowerExample; +import io.metersphere.functional.mapper.FunctionalCaseFollowerMapper; +import io.metersphere.functional.request.FunctionalCaseAddRequest; +import io.metersphere.functional.request.FunctionalCaseEditRequest; +import io.metersphere.functional.request.FunctionalCaseFollowerRequest; +import io.metersphere.sdk.constants.HttpMethodConstants; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.log.constants.OperationLogModule; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.log.dto.LogDTO; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @author wx + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class FunctionalCaseLogService { + + @Resource + private FunctionalCaseFollowerMapper functionalCaseFollowerMapper; + + + //TODO 日志(需要修改) + + /** + * 新增用例 日志 + * + * @param requests + * @param files + * @return + */ + public LogDTO addFunctionalCaseLog(FunctionalCaseAddRequest requests, List files) { + LogDTO dto = new LogDTO( + requests.getProjectId(), + null, + null, + null, + OperationLogType.ADD.name(), + OperationLogModule.FUNCTIONAL_CASE, + requests.getName()); + + dto.setPath("/functional/case/add"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(requests)); + return dto; + } + + + /** + * 更新用例 日志 + * + * @param requests + * @param files + * @return + */ + public LogDTO updateFunctionalCaseLog(FunctionalCaseEditRequest requests, List files) { + //TODO 获取原值 + LogDTO dto = new LogDTO( + requests.getProjectId(), + null, + requests.getId(), + null, + OperationLogType.UPDATE.name(), + OperationLogModule.FUNCTIONAL_CASE, + requests.getName()); + + dto.setPath("/functional/case/update"); + dto.setMethod(HttpMethodConstants.POST.name()); + dto.setModifiedValue(JSON.toJSONBytes(requests)); + return dto; + } + + + /** + * 关注取消关注日志 + * + * @param request + * @return + */ + public LogDTO editFollower(FunctionalCaseFollowerRequest request) { + FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); + example.createCriteria().andCaseIdEqualTo(request.getFunctionalCaseId()).andUserIdEqualTo(request.getUserId()); + List caseFollowers = functionalCaseFollowerMapper.selectByExample(example); + String content = ""; + if (CollectionUtils.isNotEmpty(caseFollowers)) { + content = Translator.get("un_follow_functional_case"); + } else { + content = Translator.get("follow_functional_case"); + } + LogDTO dto = new LogDTO( + null, + null, + request.getFunctionalCaseId(), + request.getUserId(), + OperationLogType.UPDATE.name(), + OperationLogModule.FUNCTIONAL_CASE, + content); + dto.setPath("/functional/case/follower/" + request.getFunctionalCaseId()); + dto.setMethod(HttpMethodConstants.POST.name()); + return dto; + } +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java index e1acb85e5f..41337f6187 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java @@ -1,13 +1,11 @@ package io.metersphere.functional.service; -import io.metersphere.functional.domain.FunctionalCase; -import io.metersphere.functional.domain.FunctionalCaseAttachment; -import io.metersphere.functional.domain.FunctionalCaseBlob; -import io.metersphere.functional.domain.FunctionalCaseCustomField; +import io.metersphere.functional.domain.*; import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.dto.FunctionalCaseDetailDTO; import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; import io.metersphere.functional.mapper.FunctionalCaseBlobMapper; +import io.metersphere.functional.mapper.FunctionalCaseFollowerMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; @@ -16,16 +14,13 @@ import io.metersphere.project.service.ProjectTemplateService; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.MsFileUtils; import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO; import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.file.FileRequest; import io.metersphere.system.file.MinioRepository; -import io.metersphere.system.log.constants.OperationLogModule; -import io.metersphere.system.log.constants.OperationLogType; -import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.uid.NumGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -33,8 +28,11 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; /** * @author wx @@ -66,6 +64,8 @@ public class FunctionalCaseService { @Resource private ProjectTemplateService projectTemplateService; + @Resource + private FunctionalCaseFollowerMapper functionalCaseFollowerMapper; public FunctionalCase addFunctionalCase(FunctionalCaseAddRequest request, List files, String userId) { String caseId = IDGenerator.nextStr(); @@ -119,13 +119,9 @@ public class FunctionalCaseService { } public int getNextNum(String projectId) { - //TODO 获取下一个num方法(暂时直接查询数据库) - FunctionalCase testCase = extFunctionalCaseMapper.getMaxNumByProjectId(projectId); - if (testCase == null || testCase.getNum() == null) { - return 100001; - } else { - return Optional.ofNullable(testCase.getNum() + 1).orElse(100001); - } + long nextNum = NumGenerator.nextNum(projectId, ApplicationNumScope.CASE_MANAGEMENT); + BigDecimal bigDecimal = new BigDecimal(nextNum); + return bigDecimal.intValue(); } /** @@ -267,53 +263,42 @@ public class FunctionalCaseService { } - - //TODO 日志 /** - * 新增用例 日志 + * 关注/取消关注用例 * - * @param requests - * @param files - * @return + * @param functionalCaseId + * @param userId */ - public LogDTO addFunctionalCaseLog(FunctionalCaseAddRequest requests, List files) { - LogDTO dto = new LogDTO( - requests.getProjectId(), - null, - null, - null, - OperationLogType.ADD.name(), - OperationLogModule.FUNCTIONAL_CASE, - requests.getName()); - - dto.setPath("/functional/case/add"); - dto.setMethod(HttpMethodConstants.POST.name()); - dto.setOriginalValue(JSON.toJSONBytes(requests)); - return dto; + public void editFollower(String functionalCaseId, String userId) { + FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); + example.createCriteria().andCaseIdEqualTo(functionalCaseId).andUserIdEqualTo(userId); + if (functionalCaseFollowerMapper.countByExample(example) > 0) { + functionalCaseFollowerMapper.deleteByPrimaryKey(functionalCaseId, userId); + } else { + FunctionalCaseFollower functionalCaseFollower = new FunctionalCaseFollower(); + functionalCaseFollower.setCaseId(functionalCaseId); + functionalCaseFollower.setUserId(userId); + functionalCaseFollowerMapper.insert(functionalCaseFollower); + } } /** - * 更新用例 日志 + * 获取用例关注人 * - * @param requests - * @param files + * @param functionalCaseId * @return */ - public LogDTO updateFunctionalCaseLog(FunctionalCaseAddRequest requests, List files) { - //TODO 获取原值 - LogDTO dto = new LogDTO( - requests.getProjectId(), - null, - null, - null, - OperationLogType.UPDATE.name(), - OperationLogModule.FUNCTIONAL_CASE, - requests.getName()); - - dto.setPath("/functional/case/update"); - dto.setMethod(HttpMethodConstants.POST.name()); - dto.setModifiedValue(JSON.toJSONBytes(requests)); - return dto; + public List getFollower(String functionalCaseId) { + FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); + example.createCriteria().andCaseIdEqualTo(functionalCaseId); + List caseFollowers = functionalCaseFollowerMapper.selectByExample(example); + List followers = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(caseFollowers)) { + followers = caseFollowers.stream().map(FunctionalCaseFollower::getUserId).distinct().collect(Collectors.toList()); + } + return followers; } + + } diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java index 335f319e94..fca7dd4ca8 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java @@ -3,6 +3,7 @@ package io.metersphere.functional.controller; import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; +import io.metersphere.functional.request.FunctionalCaseFollowerRequest; import io.metersphere.functional.result.FunctionalCaseResultCode; import io.metersphere.functional.utils.FileBaseUtils; import io.metersphere.sdk.util.JSON; @@ -33,6 +34,8 @@ public class FunctionalCaseControllerTests extends BaseTest { public static final String DEFAULT_TEMPLATE_FIELD_URL = "/functional/case/default/template/field/"; public static final String FUNCTIONAL_CASE_DETAIL_URL = "/functional/case/detail/"; public static final String FUNCTIONAL_CASE_UPDATE_URL = "/functional/case/update"; + public static final String FUNCTIONAL_CASE_EDIT_FOLLOWER_URL = "/functional/case/edit/follower"; + public static final String FUNCTIONAL_CASE_FOLLOWER_URL = "/functional/case/follower/"; @Test @Order(1) @@ -167,4 +170,34 @@ public class FunctionalCaseControllerTests extends BaseTest { editRequest.setSteps(""); return editRequest; } + + + @Test + @Order(4) + public void testEditFollower() throws Exception { + FunctionalCaseFollowerRequest functionalCaseFollowerRequest = new FunctionalCaseFollowerRequest(); + functionalCaseFollowerRequest.setFunctionalCaseId("TEST_FUNCTIONAL_CASE_ID"); + functionalCaseFollowerRequest.setUserId("admin"); + //关注 + MvcResult mvcResult = this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_EDIT_FOLLOWER_URL, functionalCaseFollowerRequest); + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + Assertions.assertNotNull(resultHolder); + //获取关注人 + MvcResult followerMvcResult = this.requestGetWithOkAndReturn(FUNCTIONAL_CASE_FOLLOWER_URL + "TEST_FUNCTIONAL_CASE_ID"); + String followerReturnData = followerMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder followerResultHolder = JSON.parseObject(followerReturnData, ResultHolder.class); + Assertions.assertNotNull(followerResultHolder); + + //取消关注 + MvcResult editMvcResult = this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_EDIT_FOLLOWER_URL, functionalCaseFollowerRequest); + String editReturnData = editMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder editResultHolder = JSON.parseObject(editReturnData, ResultHolder.class); + Assertions.assertNotNull(editResultHolder); + //获取关注人 + MvcResult editFollowerMvcResult = this.requestGetWithOkAndReturn(FUNCTIONAL_CASE_FOLLOWER_URL + "TEST_FUNCTIONAL_CASE_ID"); + String editFollowerReturnData = editFollowerMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder editFollowerResultHolder = JSON.parseObject(editFollowerReturnData, ResultHolder.class); + Assertions.assertNotNull(editFollowerResultHolder); + } }