fix(用例管理): 修复xmind导入问题以及新增返回评审状态的评审接口

--bug=1045294 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001045294
This commit is contained in:
guoyuqi 2024-08-16 19:59:48 +08:00 committed by 刘瑞斌
parent cd3526d0e8
commit e52b3cc378
10 changed files with 257 additions and 19 deletions

View File

@ -108,6 +108,8 @@ minder_extra_node.text_expected_result=预期结果
minder_extra_node.description=备注信息
minder_extra_node.text_node_empty=文本节点名称不能为空
minder_extra_node.case_node_empty=用例名称不能为空
mind_import_case_name_empty=部分用例名称为空,校验失败;
mind_import_module_name_empty=模块名称为空
#moduleCaseReview
case_review.id.not_blank=ID不能为空
case_review.name.length_range=名称长度必须在1-200之间

View File

@ -143,6 +143,7 @@ minder_extra_node.text_expected_result=ExpectedResult
minder_extra_node.description=Description
minder_extra_node.text_node_empty=Text node name cannot be empty
minder_extra_node.case_node_empty=Case name cannot be empty
mind_import_case_name_empty=Some use case names are empty and verification failed;
#moduleCaseReview
case_review.id.not_blank=ID cannot be empty

View File

@ -142,6 +142,8 @@ minder_extra_node.text_expected_result=预期结果
minder_extra_node.description=备注信息
minder_extra_node.text_node_empty=文本节点名称不能为空
minder_extra_node.case_node_empty=用例名称不能为空
mind_import_case_name_empty=部分用例名称为空,校验失败;
#moduleCaseReview
case_review.id.not_blank=ID不能为空
case_review.name.length_range=名称长度必须在1-200之间

View File

@ -143,6 +143,7 @@ minder_extra_node.text_expected_result=預期結果
minder_extra_node.description=備註資訊
minder_extra_node.text_node_empty=文字節點名稱不能為空
minder_extra_node.case_node_empty=用例名稱不能為空
mind_import_case_name_empty=部分用例名稱為空,校驗失敗;
#moduleCaseReview
case_review.id.not_blank=ID不能為空
case_review.name.length_range=名稱長度必須在1-200之間

View File

@ -120,6 +120,15 @@ public class CaseReviewFunctionalCaseController {
}
@PostMapping("/mind/multiple/review")
@Operation(summary = "用例管理-用例评审-评审列表-评审详情-列表-批量评审")
@RequiresPermissions(value = {PermissionConstants.CASE_REVIEW_REVIEW, PermissionConstants.CASE_REVIEW_READ_UPDATE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getReviewId()", resourceType = "case_review")
public String mindReview(@Validated @RequestBody MindReviewFunctionalCaseRequest request) {
return caseReviewFunctionalCaseService.mindReview(request, SessionUtils.getUserId());
}
@PostMapping("/batch/edit/reviewers")
@Operation(summary = "用例管理-用例评审-评审列表-评审详情-列表-批量修改评审人")
@CheckOwner(resourceId = "#request.getReviewId()", resourceType = "case_review")

View File

@ -0,0 +1,39 @@
package io.metersphere.functional.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@Data
public class MindReviewFunctionalCaseRequest{
@Schema(description = "评审id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{case_review_user.review_id.not_blank}")
private String reviewId;
@Schema(description = "用例id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{case_review_user.review_id.not_blank}")
private String caseId;
@Schema(description = "评审结果:未评审(UN_REVIEWED)/评审中(UNDER_REVIEWED)/PASS(通过)/UN_PASS(未通过)/RE_REVIEWED(重新提审)")
@NotBlank(message = "{case_review.status.not_blank}")
private String status;
@Schema(description = "评论内容")
private String content;
@Schema(description = "评论@的人的Id, 多个以';'隔开")
private String notifier;
@Schema(description = "用例评审评论富文本的文件id集合")
private List<String> reviewCommentFileIds;
@Schema(description = "userId用来判断是否只看我的")
private String userId;
@Schema(description = "模块id")
private List<String> moduleIds;
}

View File

@ -415,7 +415,7 @@ public class CaseReviewFunctionalCaseService {
request.setReviewPassRule(caseReview.getReviewPassRule());
//检查权限
if (!permissionCheckService.userHasProjectPermission(userId, caseReview.getProjectId(), PermissionConstants.CASE_REVIEW_READ_UPDATE) && StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.RE_REVIEWED.toString())) {
throw new MSException("http_result_forbidden");
throw new MSException(Translator.get("http_result_forbidden"));
}
List<CaseReviewFunctionalCase> caseReviewFunctionalCaseList = doCaseReviewFunctionalCases(request);
if (CollectionUtils.isEmpty(caseReviewFunctionalCaseList)) {
@ -507,6 +507,101 @@ public class CaseReviewFunctionalCaseService {
provider.updateCaseReview(param);
}
public String mindReview(MindReviewFunctionalCaseRequest request, String userId) {
String reviewId = request.getReviewId();
String caseId = request.getCaseId();
CaseReview caseReview = caseReviewMapper.selectByPrimaryKey(reviewId);
//检查权限
if (!permissionCheckService.userHasProjectPermission(userId, caseReview.getProjectId(), PermissionConstants.CASE_REVIEW_READ_UPDATE) && StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.RE_REVIEWED.toString())) {
throw new MSException(Translator.get("http_result_forbidden"));
}
CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample();
caseReviewFunctionalCaseExample.createCriteria().andReviewIdEqualTo(reviewId).andCaseIdEqualTo(caseId);
List<CaseReviewFunctionalCase> caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample);
if (CollectionUtils.isEmpty(caseReviewFunctionalCases)) {
throw new MSException(Translator.get("case_comment.case_is_null"));
}
CaseReviewHistoryExample caseReviewHistoryExample = new CaseReviewHistoryExample();
caseReviewHistoryExample.createCriteria().andCaseIdEqualTo(caseId).andReviewIdEqualTo(reviewId).andDeletedEqualTo(false).andAbandonedEqualTo(false);
List<CaseReviewHistory> caseReviewHistories = caseReviewHistoryMapper.selectByExample(caseReviewHistoryExample);
CaseReviewFunctionalCaseUserExample caseReviewFunctionalCaseUserExample = new CaseReviewFunctionalCaseUserExample();
caseReviewFunctionalCaseUserExample.createCriteria().andReviewIdEqualTo(reviewId).andCaseIdEqualTo(caseId);
List<CaseReviewFunctionalCaseUser> userList = caseReviewFunctionalCaseUserMapper.selectByExample(caseReviewFunctionalCaseUserExample);
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(caseId);
UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
userRoleRelationExample.createCriteria().andRoleIdEqualTo(InternalUserRole.ADMIN.getValue()).andSourceIdEqualTo(UserRoleScope.SYSTEM).andOrganizationIdEqualTo(UserRoleScope.SYSTEM);
List<UserRoleRelation> userRoleRelations = userRoleRelationMapper.selectByExample(userRoleRelationExample);
List<String> systemUsers = userRoleRelations.stream().map(UserRoleRelation::getUserId).distinct().toList();
Map<String, String> statusMap = new HashMap<>();
//重新提审作废之前的记录
if (StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.RE_REVIEWED.toString())) {
extCaseReviewHistoryMapper.batchUpdateAbandoned(reviewId, List.of(caseId));
}
//校验当前操作人是否是该用例的评审人或者是系统管理员是增加评审历史不是过滤掉
if (!systemUsers.contains(userId) && (CollectionUtils.isEmpty(userList) || CollectionUtils.isEmpty(userList.stream().filter(t -> StringUtils.equalsIgnoreCase(t.getUserId(), userId)).toList()))) {
LogUtils.error(caseId + ": no review user, please check");
throw new MSException(caseId + ": no review user, please check");
}
boolean isAdmin = systemUsers.contains(userId) && (CollectionUtils.isEmpty(userList) || CollectionUtils.isEmpty(userList.stream().filter(t -> StringUtils.equalsIgnoreCase(t.getUserId(), userId)).toList()));
BatchReviewFunctionalCaseRequest batchReviewFunctionalCaseRequest = new BatchReviewFunctionalCaseRequest();
BeanUtils.copyBean(batchReviewFunctionalCaseRequest, request);
batchReviewFunctionalCaseRequest.setReviewPassRule(caseReview.getReviewPassRule());
CaseReviewHistory caseReviewHistory = buildCaseReviewHistory(batchReviewFunctionalCaseRequest, userId, caseId);
caseReviewHistoryMapper.insert(caseReviewHistory);
if (CollectionUtils.isEmpty(caseReviewHistories)) {
caseReviewHistories = new ArrayList<>();
}
caseReviewHistories.add(caseReviewHistory);
Map<String, List<CaseReviewHistory>>caseHistoryMap = new HashMap<>();
caseHistoryMap.put(caseId,caseReviewHistories);
Map<String, List<CaseReviewFunctionalCaseUser>>reviewerMap = new HashMap<>();
reviewerMap.put(caseId,userList);
CaseReviewFunctionalCase caseReviewFunctionalCase = caseReviewFunctionalCases.get(0);
//根据评审规则更新用例评审和功能用例关系表中的状态 1.单人评审直接更新评审结果 2.多人评审需要计算 3.如果是重新评审直接全部变成重新评审
setStatus(batchReviewFunctionalCaseRequest, caseReviewFunctionalCase, caseHistoryMap, reviewerMap, isAdmin);
statusMap.put(caseId, caseReviewFunctionalCase.getStatus());
caseReviewFunctionalCaseMapper.updateByPrimaryKeySelective(caseReviewFunctionalCase);
//检查是否有@发送@通知
if (StringUtils.isNotBlank(request.getNotifier())) {
List<String> relatedUsers = Arrays.asList(request.getNotifier().split(";"));
reviewSendNoticeService.sendNoticeCase(relatedUsers, userId, caseId, NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, NoticeConstants.Event.REVIEW_AT, reviewId);
}
//发送评审通过不通过通知评审中不发
if (StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.UN_PASS.toString())) {
reviewSendNoticeService.sendNoticeCase(new ArrayList<>(), userId, caseId, NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, NoticeConstants.Event.REVIEW_FAIL, reviewId);
}
if (StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.PASS.toString())) {
reviewSendNoticeService.sendNoticeCase(new ArrayList<>(), userId, caseId, NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, NoticeConstants.Event.REVIEW_PASSED, reviewId);
}
functionalCaseAttachmentService.uploadMinioFile(caseId, functionalCase.getProjectId(), request.getReviewCommentFileIds(), userId, CaseFileSourceType.REVIEW_COMMENT.toString());
Map<String, Object> param = new HashMap<>();
Map<String, Integer> countMap = new HashMap<>();
countMap.put(caseReviewFunctionalCase.getStatus(), 1);
param.put(CaseEvent.Param.CASE_IDS, List.of(caseId));
param.put(CaseEvent.Param.REVIEW_ID, reviewId);
param.put(CaseEvent.Param.STATUS_MAP, statusMap);
param.put(CaseEvent.Param.USER_ID, userId);
param.put(CaseEvent.Param.EVENT_NAME, CaseEvent.Event.REVIEW_FUNCTIONAL_CASE);
param.put(CaseEvent.Param.COUNT_MAP, countMap);
provider.updateCaseReview(param);
return caseReviewFunctionalCase.getStatus();
}
private static void setStatus(BatchReviewFunctionalCaseRequest request, CaseReviewFunctionalCase caseReviewFunctionalCase, Map<String, List<CaseReviewHistory>> caseHistoryMap, Map<String, List<CaseReviewFunctionalCaseUser>> reviewerMap, boolean isAdmin) {
if (StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.RE_REVIEWED.toString())) {

View File

@ -78,8 +78,6 @@ public class XMindCaseParser {
process = new DetailUtil();
}
private final List<String> priorityList = Arrays.asList("P0", "P1", "P2", "P3");
private static final String ID = "(?:id:|id|Id:|Id|iD:|iD)";
private static final String CASE = "((?i)case)";
private static final String PREREQUISITE = "(?:" + Translator.get("xmind_prerequisite") + ":|" + Translator.get("xmind_prerequisite") + ")";
@ -130,7 +128,7 @@ public class XMindCaseParser {
this.formatTestCase(item.getTitle(), parent.getPath(), item.getChildren() != null ? item.getChildren().getAttached() : null);
} else {
if (StringUtils.equalsIgnoreCase(parent.getPath().trim(), Translator.get("functional_case.module.default.name"))) {
process.parse(replace(item.getTitle(), CASE) + "" + Translator.get("functional_case.module.default.name.add_error"));
process.add(Translator.get("incorrect_format"), Translator.get("functional_case.module.default.name.add_error"));
return;
}
String nodePath = parent.getPath().trim() + "/" + item.getTitle().trim();
@ -254,11 +252,7 @@ public class XMindCaseParser {
private boolean validateCustomField(FunctionalCaseExcelData data) {
boolean validate = true;
Map<String, Object> customData = data.getCustomData();
boolean hasPriority = false;
for (String fieldName : customData.keySet()) {
if (StringUtils.equalsIgnoreCase(fieldName, Translator.get("custom_field.functional_priority"))) {
hasPriority = true;
}
Object value = customData.get(fieldName);
TemplateCustomFieldDTO templateCustomFieldDTO = customFieldsMap.get(fieldName);
if (templateCustomFieldDTO == null) {
@ -276,10 +270,6 @@ public class XMindCaseParser {
process.add(data.getName(), e.getMessage());
}
}
if (!hasPriority) {
validate = false;
process.add(data.getName(), Translator.get("priority_is_null"));
}
return validate;
}
@ -492,7 +482,7 @@ public class XMindCaseParser {
} else {
String modulePath = item.getTitle();
if (StringUtils.isBlank(modulePath)) {
return process.parse(replace(item.getTitle(), CASE) + "" + Translator.get("module_not_null"));
return process.parse(Translator.get("module_not_null"));
}
item.setPath(modulePath);
if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty()) {

View File

@ -62,7 +62,7 @@ public class DetailUtil implements Serializable {
}
}
if (emptyName>0) {
ExcelErrData excelErrData = new ExcelErrData(result.size(), "部分用例名称为空,校验失败;");
ExcelErrData excelErrData = new ExcelErrData(result.size(), Translator.get("mind_import_case_name_empty"));
errList.add(excelErrData);
}
return errList;

View File

@ -11,6 +11,7 @@ import io.metersphere.functional.request.*;
import io.metersphere.functional.service.CaseReviewFunctionalCaseService;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.dto.sdk.BaseCondition;
@ -54,6 +55,10 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
public static final String BATCH_EDIT_REVIEWERS = "/case/review/detail/batch/edit/reviewers";
public static final String REVIEW_FUNCTIONAL_CASE_BATCH_REVIEW = "/case/review/detail/batch/review";
public static final String REVIEW_FUNCTIONAL_CASE_MIND_REVIEW = "/case/review/detail/mind/multiple/review";
public static final String URL_MODULE_TREE = "/case/review/detail/tree/";
public static final String REVIEW_FUNCTIONAL_CASE_MODULE_COUNT = "/case/review/detail/module/count";
@ -456,6 +461,100 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
@Test
@Order(9)
public void testMindReview() throws Exception {
MindReviewFunctionalCaseRequest request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_3");
request.setCaseId("wx_test_5");
request.setStatus(FunctionalCaseReviewStatus.PASS.toString());
request.setContent("测试批量评审通过");
this.requestPost(REVIEW_FUNCTIONAL_CASE_MIND_REVIEW, request).andExpect(status().is5xxServerError());
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_2");
request.setCaseId("gyq_case_id_9");
request.setStatus(FunctionalCaseReviewStatus.UN_PASS.toString());
request.setContent("测试批量评审失败");
this.requestPostWithOk(REVIEW_FUNCTIONAL_CASE_MIND_REVIEW, request);
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setCaseId("wx_case_id_4");
request.setStatus(FunctionalCaseReviewStatus.UNDER_REVIEWED.toString());
request.setContent("测试批量评审人");
try {
caseReviewFunctionalCaseService.mindReview(request, "666");
} catch (Exception e){
LogUtils.error(e.getMessage());
}
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setCaseId("wx_case_id_2");
request.setStatus(FunctionalCaseReviewStatus.UNDER_REVIEWED.toString());
request.setContent("测试批量评审人");
caseReviewFunctionalCaseService.mindReview(request, "123");
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setCaseId("wx_case_id_2");
request.setStatus(FunctionalCaseReviewStatus.UN_PASS.toString());
request.setContent("测试批量评审人");
caseReviewFunctionalCaseService.mindReview(request, "admin");
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_1");
request.setCaseId("gyq_case_id_5");
request.setStatus(FunctionalCaseReviewStatus.PASS.toString());
request.setNotifier("gyq;admin");
request.setContent("测试批量评审通过");
this.requestPostWithOk(REVIEW_FUNCTIONAL_CASE_MIND_REVIEW, request);
CaseReviewFunctionalCase caseReviewFunctionalCase = caseReviewFunctionalCaseMapper.selectByPrimaryKey("gyq_test_5");
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_1");
request.setStatus(FunctionalCaseReviewStatus.UN_PASS.toString());
request.setContent("hhh");
request.setCaseId("gyq_case_id_5");
request.setContent("测试批量评审通过");
caseReviewFunctionalCaseService.mindReview(request, "multiple_review_admin");
CaseReviewFunctionalCase caseReviewFunctionalCase1 = caseReviewFunctionalCaseMapper.selectByPrimaryKey("gyq_test_5");
Assertions.assertFalse(StringUtils.equalsIgnoreCase(caseReviewFunctionalCase.getStatus(), caseReviewFunctionalCase1.getStatus()));
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setStatus(FunctionalCaseReviewStatus.PASS.toString());
request.setCaseId("wx_case_id_2");
caseReviewFunctionalCaseService.mindReview(request, "admin");
CaseReviewHistoryExample caseReviewHistoryExample = new CaseReviewHistoryExample();
caseReviewHistoryExample.createCriteria().andCaseIdEqualTo("wx_case_id_2").andReviewIdEqualTo("wx_review_id_4").andAbandonedEqualTo(false).andDeletedEqualTo(false);
List<CaseReviewHistory> caseReviewHistories = caseReviewHistoryMapper.selectByExample(caseReviewHistoryExample);
Assertions.assertEquals(8,caseReviewHistories.size());
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setStatus(FunctionalCaseReviewStatus.PASS.toString());
request.setCaseId("wx_case_id_2");
caseReviewFunctionalCaseService.mindReview(request, "123");
caseReviewHistories = caseReviewHistoryMapper.selectByExample(caseReviewHistoryExample);
Assertions.assertEquals(9,caseReviewHistories.size());
request = new MindReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_4");
request.setStatus(FunctionalCaseReviewStatus.PASS.toString());
request.setCaseId("wx_case_id_2");
caseReviewFunctionalCaseService.mindReview(request, "123");
caseReviewHistories = caseReviewHistoryMapper.selectByExample(caseReviewHistoryExample);
Assertions.assertEquals(10,caseReviewHistories.size());
}
@Test
@Order(10)
public void testBatchReviewFalse() throws Exception {
BatchReviewFunctionalCaseRequest request = new BatchReviewFunctionalCaseRequest();
request.setReviewId("wx_review_id_1");
@ -487,7 +586,7 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
@Test
@Order(10)
@Order(11)
public void testBatchEditReviewers() throws Exception {
BatchEditReviewerRequest request = new BatchEditReviewerRequest();
//更新评审人
@ -539,7 +638,7 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
}
@Test
@Order(11)
@Order(12)
public void getUserStatus() throws Exception {
List<OptionDTO> optionDTOS = getOptionDTOS("wx_review_id_1", "gyq_case_id_5");
Assertions.assertTrue(CollectionUtils.isNotEmpty(optionDTOS));
@ -548,7 +647,7 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
}
@Test
@Order(12)
@Order(13)
public void getModuleCount() throws Exception {
ReviewFunctionalCasePageRequest request = new ReviewFunctionalCasePageRequest();
request.setReviewId("wx_review_id_1");
@ -566,7 +665,7 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
}
@Test
@Order(13)
@Order(14)
public void getReviewerList() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(GET_CASE_REVIEWER_LIST + "/wx_review_id_1/gyq_case_id_5").header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
@ -580,7 +679,7 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
}
@Test
@Order(14)
@Order(15)
public void getReviewerWidthTotalList() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(GET_CASE_REVIEWER_AND_STATUS + "/wx_review_id_1/gyq_case_id_5").header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)