feat(功能用例): 关注/取消关注用例

This commit is contained in:
WangXu10 2023-10-30 16:57:06 +08:00 committed by Craftsman
parent 7eabb3fe89
commit 46abc85a9f
8 changed files with 242 additions and 58 deletions

View File

@ -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_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.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.reply_user_is_null=The user who replied is empty
case_comment.id_is_null=The current comment id is empty case_comment.id_is_null=The current comment id is empty
un_follow_functional_case=unfollow functional case
follow_functional_case=followed functional case

View File

@ -141,4 +141,6 @@ case_comment.case_is_null=功能用例不存在
case_comment.parent_id_is_null=当前回复的评论id为空 case_comment.parent_id_is_null=当前回复的评论id为空
case_comment.parent_case_is_null=当前回复的评论不存在 case_comment.parent_case_is_null=当前回复的评论不存在
case_comment.reply_user_is_null=回复的用户为空 case_comment.reply_user_is_null=回复的用户为空
case_comment.id_is_null=当前评论id为空 case_comment.id_is_null=当前评论id为空
un_follow_functional_case=取消关注用例
follow_functional_case=关注用例

View File

@ -141,4 +141,6 @@ case_comment.case_is_null=功能用例不存在
case_comment.parent_id_is_null=目前回覆的評論id為空 case_comment.parent_id_is_null=目前回覆的評論id為空
case_comment.parent_case_is_null=目前回應的評論不存在 case_comment.parent_case_is_null=目前回應的評論不存在
case_comment.reply_user_is_null=回覆的用戶為空 case_comment.reply_user_is_null=回覆的用戶為空
case_comment.id_is_null=目前評論id為空 case_comment.id_is_null=目前評論id為空
un_follow_functional_case=取消關注用例
follow_functional_case=關注用例

View File

@ -4,6 +4,8 @@ import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.dto.FunctionalCaseDetailDTO; import io.metersphere.functional.dto.FunctionalCaseDetailDTO;
import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest; 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.functional.service.FunctionalCaseService;
import io.metersphere.project.service.ProjectTemplateService; import io.metersphere.project.service.ProjectTemplateService;
import io.metersphere.sdk.constants.PermissionConstants; 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.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -50,7 +53,7 @@ public class FunctionalCaseController {
@PostMapping("/add") @PostMapping("/add")
@Operation(summary = "功能用例-新增用例") @Operation(summary = "功能用例-新增用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD) @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<MultipartFile> files) { public FunctionalCase addFunctionalCase(@Validated @RequestPart("request") FunctionalCaseAddRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
String userId = SessionUtils.getUserId(); String userId = SessionUtils.getUserId();
return functionalCaseService.addFunctionalCase(request, files, userId); return functionalCaseService.addFunctionalCase(request, files, userId);
@ -68,9 +71,27 @@ public class FunctionalCaseController {
@PostMapping("/update") @PostMapping("/update")
@Operation(summary = "功能用例-更新用例") @Operation(summary = "功能用例-更新用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) @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<MultipartFile> files) { public FunctionalCase updateFunctionalCase(@Validated @RequestPart("request") FunctionalCaseEditRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
String userId = SessionUtils.getUserId(); String userId = SessionUtils.getUserId();
return functionalCaseService.updateFunctionalCase(request, files, userId); 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<String> getFollower(@PathVariable @NotBlank(message = "{functional_case.id.not_blank}") String functionalCaseId) {
return functionalCaseService.getFollower(functionalCaseId);
}
} }

View File

@ -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;
}

View File

@ -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<MultipartFile> 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<MultipartFile> 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<FunctionalCaseFollower> 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;
}
}

View File

@ -1,13 +1,11 @@
package io.metersphere.functional.service; package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.domain.*;
import io.metersphere.functional.domain.FunctionalCaseAttachment;
import io.metersphere.functional.domain.FunctionalCaseBlob;
import io.metersphere.functional.domain.FunctionalCaseCustomField;
import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.dto.CaseCustomsFieldDTO;
import io.metersphere.functional.dto.FunctionalCaseDetailDTO; import io.metersphere.functional.dto.FunctionalCaseDetailDTO;
import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
import io.metersphere.functional.mapper.FunctionalCaseBlobMapper; import io.metersphere.functional.mapper.FunctionalCaseBlobMapper;
import io.metersphere.functional.mapper.FunctionalCaseFollowerMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest; 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.constants.*;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.MsFileUtils; import io.metersphere.sdk.util.MsFileUtils;
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO; import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.file.FileRequest; import io.metersphere.system.file.FileRequest;
import io.metersphere.system.file.MinioRepository; 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.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -33,8 +28,11 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
/** /**
* @author wx * @author wx
@ -66,6 +64,8 @@ public class FunctionalCaseService {
@Resource @Resource
private ProjectTemplateService projectTemplateService; private ProjectTemplateService projectTemplateService;
@Resource
private FunctionalCaseFollowerMapper functionalCaseFollowerMapper;
public FunctionalCase addFunctionalCase(FunctionalCaseAddRequest request, List<MultipartFile> files, String userId) { public FunctionalCase addFunctionalCase(FunctionalCaseAddRequest request, List<MultipartFile> files, String userId) {
String caseId = IDGenerator.nextStr(); String caseId = IDGenerator.nextStr();
@ -119,13 +119,9 @@ public class FunctionalCaseService {
} }
public int getNextNum(String projectId) { public int getNextNum(String projectId) {
//TODO 获取下一个num方法(暂时直接查询数据库) long nextNum = NumGenerator.nextNum(projectId, ApplicationNumScope.CASE_MANAGEMENT);
FunctionalCase testCase = extFunctionalCaseMapper.getMaxNumByProjectId(projectId); BigDecimal bigDecimal = new BigDecimal(nextNum);
if (testCase == null || testCase.getNum() == null) { return bigDecimal.intValue();
return 100001;
} else {
return Optional.ofNullable(testCase.getNum() + 1).orElse(100001);
}
} }
/** /**
@ -267,53 +263,42 @@ public class FunctionalCaseService {
} }
//TODO 日志
/** /**
* 新增用例 日志 * 关注/取消关注用例
* *
* @param requests * @param functionalCaseId
* @param files * @param userId
* @return
*/ */
public LogDTO addFunctionalCaseLog(FunctionalCaseAddRequest requests, List<MultipartFile> files) { public void editFollower(String functionalCaseId, String userId) {
LogDTO dto = new LogDTO( FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample();
requests.getProjectId(), example.createCriteria().andCaseIdEqualTo(functionalCaseId).andUserIdEqualTo(userId);
null, if (functionalCaseFollowerMapper.countByExample(example) > 0) {
null, functionalCaseFollowerMapper.deleteByPrimaryKey(functionalCaseId, userId);
null, } else {
OperationLogType.ADD.name(), FunctionalCaseFollower functionalCaseFollower = new FunctionalCaseFollower();
OperationLogModule.FUNCTIONAL_CASE, functionalCaseFollower.setCaseId(functionalCaseId);
requests.getName()); functionalCaseFollower.setUserId(userId);
functionalCaseFollowerMapper.insert(functionalCaseFollower);
dto.setPath("/functional/case/add"); }
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(requests));
return dto;
} }
/** /**
* 更新用例 日志 * 获取用例关注人
* *
* @param requests * @param functionalCaseId
* @param files
* @return * @return
*/ */
public LogDTO updateFunctionalCaseLog(FunctionalCaseAddRequest requests, List<MultipartFile> files) { public List<String> getFollower(String functionalCaseId) {
//TODO 获取原值 FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample();
LogDTO dto = new LogDTO( example.createCriteria().andCaseIdEqualTo(functionalCaseId);
requests.getProjectId(), List<FunctionalCaseFollower> caseFollowers = functionalCaseFollowerMapper.selectByExample(example);
null, List<String> followers = new ArrayList<>();
null, if (CollectionUtils.isNotEmpty(caseFollowers)) {
null, followers = caseFollowers.stream().map(FunctionalCaseFollower::getUserId).distinct().collect(Collectors.toList());
OperationLogType.UPDATE.name(), }
OperationLogModule.FUNCTIONAL_CASE, return followers;
requests.getName());
dto.setPath("/functional/case/update");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setModifiedValue(JSON.toJSONBytes(requests));
return dto;
} }
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.functional.controller;
import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.dto.CaseCustomsFieldDTO;
import io.metersphere.functional.request.FunctionalCaseAddRequest; import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest;
import io.metersphere.functional.request.FunctionalCaseFollowerRequest;
import io.metersphere.functional.result.FunctionalCaseResultCode; import io.metersphere.functional.result.FunctionalCaseResultCode;
import io.metersphere.functional.utils.FileBaseUtils; import io.metersphere.functional.utils.FileBaseUtils;
import io.metersphere.sdk.util.JSON; 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 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_DETAIL_URL = "/functional/case/detail/";
public static final String FUNCTIONAL_CASE_UPDATE_URL = "/functional/case/update"; 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 @Test
@Order(1) @Order(1)
@ -167,4 +170,34 @@ public class FunctionalCaseControllerTests extends BaseTest {
editRequest.setSteps(""); editRequest.setSteps("");
return editRequest; 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);
}
} }