feat(用例管理): 增加脑图点击模块获取用例接口

This commit is contained in:
guoyuqi 2024-04-29 09:19:00 +08:00 committed by Yuki Guo
parent 4a7cef668a
commit 46afc6c124
9 changed files with 188 additions and 291 deletions

View File

@ -5,6 +5,9 @@ ALTER TABLE user_key MODIFY COLUMN description VARCHAR(1000);
ALTER TABLE api_definition_mock ADD COLUMN status_code INT(50) ; ALTER TABLE api_definition_mock ADD COLUMN status_code INT(50) ;
CREATE INDEX idx_scene ON custom_field (scene);
CREATE INDEX idx_internal ON custom_field (internal);
-- set innodb lock wait timeout to default -- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT; SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -1,13 +1,10 @@
package io.metersphere.functional.controller; package io.metersphere.functional.controller;
import com.alibaba.excel.util.StringUtils;
import io.metersphere.functional.dto.FunctionalMinderTreeDTO; import io.metersphere.functional.dto.FunctionalMinderTreeDTO;
import io.metersphere.functional.request.FunctionalCasePageRequest; import io.metersphere.functional.request.FunctionalCaseMindRequest;
import io.metersphere.functional.request.MinderReviewFunctionalCasePageRequest;
import io.metersphere.functional.service.FunctionalCaseMinderService; import io.metersphere.functional.service.FunctionalCaseMinderService;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.security.CheckOwner; import io.metersphere.system.security.CheckOwner;
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;
@ -18,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/** /**
* @author guoyuqi * @author guoyuqi
*/ */
@ -30,23 +29,11 @@ public class FunctionalCaseMinderController {
private FunctionalCaseMinderService functionalCaseMinderService; private FunctionalCaseMinderService functionalCaseMinderService;
@PostMapping("/list") @PostMapping("/list")
@Operation(summary = "用例管理-功能用例-脑图用例列表查询") @Operation(summary = "用例管理-功能用例-脑图用例跟根据模块ID查询列表")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_MINDER) @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_MINDER)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#projectId()", resourceType = "project")
public FunctionalMinderTreeDTO getFunctionalCaseMinderTree(@Validated @RequestBody FunctionalCasePageRequest request) { public List<FunctionalMinderTreeDTO> getFunctionalCaseMinderTree(@Validated @RequestBody FunctionalCaseMindRequest request) {
return functionalCaseMinderService.getFunctionalCasePage(request, false); return functionalCaseMinderService.getMindFunctionalCase(request, false);
}
@PostMapping("/review/list")
@Operation(summary = "用例管理-用例评审-脑图用例列表查询")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_MINDER)
@CheckOwner(resourceId = "#request.getReviewId()", resourceType = "case_review")
public FunctionalMinderTreeDTO getFunctionalReviewCaseMinderTree(@Validated @RequestBody MinderReviewFunctionalCasePageRequest request) {
String userId = StringUtils.EMPTY;
if (request.isViewFlag()) {
userId = SessionUtils.getUserId();
}
return functionalCaseMinderService.getReviewFunctionalCasePage(request, false, userId);
} }
} }

View File

@ -0,0 +1,61 @@
package io.metersphere.functional.dto;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class FunctionalCaseMindDTO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{functional_case.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.module_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{functional_case.module_id.length_range}", groups = {Created.class, Updated.class})
private String moduleId;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{functional_case.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.template_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{functional_case.template_id.length_range}", groups = {Created.class, Updated.class})
private String templateId;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{functional_case.name.length_range}", groups = {Created.class, Updated.class})
private String name;
@Schema(description = "评审状态:未评审/评审中/通过/不通过/重新提审", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.review_status.not_blank}", groups = {Created.class})
@Size(min = 1, max = 64, message = "{functional_case.review_status.length_range}", groups = {Created.class, Updated.class})
private String reviewStatus;
@Schema(description = "用例等级")
private String priority;
@Schema(description = "用例步骤JSON)step_model 为 Step 时启用")
private byte[] steps;
@Schema(description = "预期结果step_model 为 Text 时启用")
private byte[] expectedResult;
@Schema(description = "前置条件")
private byte[] prerequisite;
@Schema(description = "备注")
private byte[] description;
}

View File

@ -3,9 +3,11 @@ package io.metersphere.functional.mapper;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.dto.BaseFunctionalCaseBatchDTO; import io.metersphere.functional.dto.BaseFunctionalCaseBatchDTO;
import io.metersphere.functional.dto.FunctionalCaseMindDTO;
import io.metersphere.functional.dto.FunctionalCasePageDTO; import io.metersphere.functional.dto.FunctionalCasePageDTO;
import io.metersphere.functional.dto.FunctionalCaseVersionDTO; import io.metersphere.functional.dto.FunctionalCaseVersionDTO;
import io.metersphere.functional.request.FunctionalCaseBatchMoveRequest; import io.metersphere.functional.request.FunctionalCaseBatchMoveRequest;
import io.metersphere.functional.request.FunctionalCaseMindRequest;
import io.metersphere.functional.request.FunctionalCasePageRequest; import io.metersphere.functional.request.FunctionalCasePageRequest;
import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
@ -63,18 +65,25 @@ public interface ExtFunctionalCaseMapper {
/** /**
* 获取缺陷未关联的功能用例列表 * 获取缺陷未关联的功能用例列表
*
* @param request provider参数 * @param request provider参数
* @param deleted 是否删除状态 * @param deleted 是否删除状态
* @param sort 排序 * @param sort 排序
* @return 通用的列表Case集合 * @return 通用的列表Case集合
*/ */
List<TestCaseProviderDTO> listUnRelatedCaseWithBug(@Param("request") TestCasePageProviderRequest request, @Param("deleted") boolean deleted, @Param("sort") String sort); List<TestCaseProviderDTO> listUnRelatedCaseWithBug(@Param("request") TestCasePageProviderRequest request, @Param("deleted") boolean deleted, @Param("sort") String sort);
/** /**
* 根据关联条件获取关联的用例ID * 根据关联条件获取关联的用例ID
*
* @param request 关联参数 * @param request 关联参数
* @param deleted 是否删除状态 * @param deleted 是否删除状态
* @return 关联的用例ID集合 * @return 关联的用例ID集合
*/ */
List<String> getSelectIdsByAssociateParam(@Param("request")AssociateOtherCaseRequest request, @Param("deleted") boolean deleted); List<String> getSelectIdsByAssociateParam(@Param("request") AssociateOtherCaseRequest request, @Param("deleted") boolean deleted);
/**
* 根据模块ID获取脑图展示数据
*/
List<FunctionalCaseMindDTO> getMinderCaseList(@Param("request") FunctionalCaseMindRequest request, @Param("deleted") boolean deleted);
} }

View File

@ -779,4 +779,26 @@
</foreach> </foreach>
</if> </if>
</sql> </sql>
<select id="getMinderCaseList" resultType="io.metersphere.functional.dto.FunctionalCaseMindDTO">
SELECT
fc.id, fc.name, fc.project_id, fc.module_id, fc.template_id, fc.review_status,
fcb.steps, fcb.expected_result, fcb.prerequisite, fcb.description, fccf.value as priority
FROM
functional_case fc
LEFT JOIN functional_case_blob fcb ON fcb.id = fc.id
LEFT JOIN functional_case_custom_field fccf ON fccf.case_id = fc.id
LEFT JOIN custom_field cf ON cf.id = fccf.field_id
WHERE
fc.deleted = false
AND
fc.project_Id = #{request.projectId}
AND
fc.module_id = #{request.moduleId}
AND
cf.name = 'functional_priority'
AND cf.scene = 'FUNCTIONAL'
AND cf.internal= true
</select>
</mapper> </mapper>

View File

@ -0,0 +1,16 @@
package io.metersphere.functional.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class FunctionalCaseMindRequest {
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.project_id.not_blank}")
private String projectId;
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module.id.not_blank}")
private String moduleId;
}

View File

@ -1,26 +1,19 @@
package io.metersphere.functional.service; package io.metersphere.functional.service;
import io.metersphere.functional.constants.CaseReviewPassRule;
import io.metersphere.functional.constants.MinderLabel; import io.metersphere.functional.constants.MinderLabel;
import io.metersphere.functional.domain.CaseReview; import io.metersphere.functional.dto.FunctionalCaseMindDTO;
import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.dto.FunctionalMinderTreeDTO;
import io.metersphere.functional.dto.*; import io.metersphere.functional.dto.FunctionalMinderTreeNodeDTO;
import io.metersphere.functional.mapper.CaseReviewMapper; import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCasePageRequest; import io.metersphere.functional.request.FunctionalCaseMindRequest;
import io.metersphere.functional.request.MinderReviewFunctionalCasePageRequest;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.jetbrains.annotations.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 功能用例脑图 * 功能用例脑图
@ -32,217 +25,70 @@ import java.util.stream.Collectors;
public class FunctionalCaseMinderService { public class FunctionalCaseMinderService {
@Resource @Resource
private FunctionalCaseService functionalCaseService; private ExtFunctionalCaseMapper extFunctionalCaseMapper;
@Resource
private FunctionalCaseModuleService functionalCaseModuleService;
@Resource
private CaseReviewFunctionalCaseService caseReviewFunctionalCaseService;
@Resource
private CaseReviewMapper caseReviewMapper;
/** /**
* 功能用例-脑图用例列表查询 * 功能用例-脑图用例列表查询
* *
* @param request FunctionalCasePageRequest
* @param deleted 用例是否删除 * @param deleted 用例是否删除
* @return FunctionalMinderTreeDTO * @return FunctionalMinderTreeDTO
*/ */
public FunctionalMinderTreeDTO getFunctionalCasePage(FunctionalCasePageRequest request, boolean deleted) { public List<FunctionalMinderTreeDTO> getMindFunctionalCase(FunctionalCaseMindRequest request, boolean deleted) {
//根据查询条件查询出所有符合的功能用例 List<FunctionalMinderTreeDTO> list = new ArrayList<>();
List<FunctionalCasePageDTO> functionalCasePage = functionalCaseService.getFunctionalCasePage(request, deleted); //查出当前模块下的所有用例
//根据功能用例的模块ID查出每个模块id父级直到根节点 List<FunctionalCaseMindDTO> functionalCaseMindDTOList = extFunctionalCaseMapper.getMinderCaseList(request, deleted);
List<String> moduleIds = functionalCasePage.stream().map(FunctionalCase::getModuleId).distinct().toList(); //构造父子级数据
Map<String, List<FunctionalCasePageDTO>> moduleCaseMap = functionalCasePage.stream().collect(Collectors.groupingBy(FunctionalCasePageDTO::getModuleId)); for (FunctionalCaseMindDTO functionalCaseMindDTO : functionalCaseMindDTOList) {
List<BaseTreeNode> baseTreeNodes = getBaseTreeNodes(moduleIds); FunctionalMinderTreeDTO root = new FunctionalMinderTreeDTO();
//判断虚拟节点有无数据 FunctionalMinderTreeNodeDTO rootData = new FunctionalMinderTreeNodeDTO();
if (moduleCaseMap.get(ModuleConstants.DEFAULT_NODE_ID) == null && CollectionUtils.isEmpty(baseTreeNodes.get(0).getChildren())) { rootData.setId(functionalCaseMindDTO.getId());
baseTreeNodes.remove(0); rootData.setText(functionalCaseMindDTO.getName());
rootData.setPriority(functionalCaseMindDTO.getPriority());
rootData.setStatus(functionalCaseMindDTO.getReviewStatus());
rootData.setResource(List.of(MinderLabel.CASE.toString(), functionalCaseMindDTO.getPriority()));
List<FunctionalMinderTreeDTO> children = buildChildren(functionalCaseMindDTO);
root.setChildren(children);
root.setData(rootData);
list.add(root);
} }
//构建返回数据主层级应与模块树层级相同 return list;
List<FunctionalMinderTreeDTO> functionalMinderTreeNodeDTOs = new ArrayList<>();
buildCaseTree(baseTreeNodes, moduleCaseMap, functionalMinderTreeNodeDTOs);
return getRoot(functionalMinderTreeNodeDTOs);
} }
private static FunctionalMinderTreeDTO getRoot(List<FunctionalMinderTreeDTO> functionalMinderTreeDTOs) { private List<FunctionalMinderTreeDTO> buildChildren(FunctionalCaseMindDTO functionalCaseMindDTO) {
FunctionalMinderTreeDTO root = new FunctionalMinderTreeDTO(); List<FunctionalMinderTreeDTO> children = new ArrayList<>();
if (functionalCaseMindDTO.getSteps() != null) {
String stepText = new String(functionalCaseMindDTO.getSteps(), StandardCharsets.UTF_8);
FunctionalMinderTreeDTO stepFunctionalMinderTreeDTO = getFunctionalMinderTreeDTO(stepText, MinderLabel.TEXT_DESCRIPTION.toString());
children.add(stepFunctionalMinderTreeDTO);
}
if (functionalCaseMindDTO.getExpectedResult() != null) {
String expectedResultText = new String(functionalCaseMindDTO.getExpectedResult(), StandardCharsets.UTF_8);
FunctionalMinderTreeDTO expectedResultFunctionalMinderTreeDTO = getFunctionalMinderTreeDTO(expectedResultText, MinderLabel.EXPECTED_RESULT.toString());
children.add(expectedResultFunctionalMinderTreeDTO);
}
if (functionalCaseMindDTO.getPrerequisite() != null) {
String prerequisiteText = new String(functionalCaseMindDTO.getPrerequisite(), StandardCharsets.UTF_8);
FunctionalMinderTreeDTO prerequisiteFunctionalMinderTreeDTO = getFunctionalMinderTreeDTO(prerequisiteText, MinderLabel.PREREQUISITE.toString());
children.add(prerequisiteFunctionalMinderTreeDTO);
}
if (functionalCaseMindDTO.getDescription() != null) {
String descriptionText = new String(functionalCaseMindDTO.getDescription(), StandardCharsets.UTF_8);
FunctionalMinderTreeDTO descriptionFunctionalMinderTreeDTO = getFunctionalMinderTreeDTO(descriptionText, MinderLabel.DESCRIPTION.toString());
children.add(descriptionFunctionalMinderTreeDTO);
}
return children;
}
@NotNull
private static FunctionalMinderTreeDTO getFunctionalMinderTreeDTO(String text, String resource) {
FunctionalMinderTreeDTO functionalMinderTreeDTO = new FunctionalMinderTreeDTO();
FunctionalMinderTreeNodeDTO rootData = new FunctionalMinderTreeNodeDTO(); FunctionalMinderTreeNodeDTO rootData = new FunctionalMinderTreeNodeDTO();
rootData.setResource(List.of(MinderLabel.MODULE.toString())); rootData.setText(text);
rootData.setText(Translator.get("case.minder.all.case")); rootData.setResource(List.of(resource));
root.setChildren(functionalMinderTreeDTOs); functionalMinderTreeDTO.setChildren(new ArrayList<>());
root.setData(rootData); functionalMinderTreeDTO.setData(rootData);
return root; return functionalMinderTreeDTO;
} }
private List<BaseTreeNode> getBaseTreeNodes(List<String> moduleIds) {
List<BaseTreeNode> nodeByNodeIds = functionalCaseModuleService.getNodeByNodeIds(moduleIds);
//根据模块节点构造模块树
return functionalCaseModuleService.buildTreeAndCountResource(nodeByNodeIds, true, Translator.get("functional_case.module.default.name"));
}
private static void buildCaseTree(List<BaseTreeNode> baseTreeNodes, Map<String, List<FunctionalCasePageDTO>> moduleCaseMap, List<FunctionalMinderTreeDTO> functionalMinderTreeNodeDTOs) {
baseTreeNodes.forEach(t -> {
//构建根节点
FunctionalMinderTreeDTO functionalMinderTreeDTO = new FunctionalMinderTreeDTO();
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeDTO = getFunctionalMinderTreeNodeDTO(t);
functionalMinderTreeDTO.setData(functionalMinderTreeNodeDTO);
List<FunctionalMinderTreeDTO> children = new ArrayList<>();
//如果当前节点有用例则用例是他的子节点
buildCaseChild(moduleCaseMap, t, children);
//查询当前节点下的子模块节点
buildModuleChild(moduleCaseMap, t, children);
functionalMinderTreeDTO.setChildren(children);
functionalMinderTreeNodeDTOs.add(functionalMinderTreeDTO);
});
}
private static FunctionalMinderTreeNodeDTO getFunctionalMinderTreeNodeDTO(BaseTreeNode t) {
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeDTO = new FunctionalMinderTreeNodeDTO();
functionalMinderTreeNodeDTO.setId(t.getId());
functionalMinderTreeNodeDTO.setText(t.getName());
functionalMinderTreeNodeDTO.setResource(List.of(MinderLabel.MODULE.toString()));
return functionalMinderTreeNodeDTO;
}
private static void buildCaseChild(Map<String, List<FunctionalCasePageDTO>> moduleCaseMap, BaseTreeNode t, List<FunctionalMinderTreeDTO> children) {
if (moduleCaseMap.get(t.getId()) != null && CollectionUtils.isNotEmpty(moduleCaseMap.get(t.getId()))) {
List<FunctionalCasePageDTO> functionalCasePageDTOS = moduleCaseMap.get(t.getId());
functionalCasePageDTOS.forEach(functionalCasePageDTO -> {
FunctionalMinderTreeDTO functionalMinderTreeChild = new FunctionalMinderTreeDTO();
//加载用例为children
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeChild = new FunctionalMinderTreeNodeDTO();
functionalMinderTreeNodeChild.setId(functionalCasePageDTO.getId());
functionalMinderTreeNodeChild.setText(functionalCasePageDTO.getName());
if (CollectionUtils.isNotEmpty(functionalCasePageDTO.getCustomFields())) {
List<FunctionalCaseCustomFieldDTO> list = functionalCasePageDTO.getCustomFields().stream().filter(customField -> customField.getFieldName().equals(Translator.get("custom_field.functional_priority"))).toList();
if (CollectionUtils.isNotEmpty(list)) {
functionalMinderTreeNodeChild.setPriority(list.get(0).getDefaultValue());
}
}
functionalMinderTreeNodeChild.setResource(List.of(MinderLabel.CASE.toString()));
functionalMinderTreeChild.setData(functionalMinderTreeNodeChild);
//将用例的blob记为Case的children
children.add(functionalMinderTreeChild);
});
}
}
private static void buildModuleChild(Map<String, List<FunctionalCasePageDTO>> moduleCaseMap, BaseTreeNode t, List<FunctionalMinderTreeDTO> children) {
if (CollectionUtils.isNotEmpty(t.getChildren())) {
t.getChildren().forEach(child -> {
FunctionalMinderTreeDTO functionalMinderTreeDTOChild = new FunctionalMinderTreeDTO();
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeDTOChild = getFunctionalMinderTreeNodeDTO(child);
functionalMinderTreeDTOChild.setData(functionalMinderTreeNodeDTOChild);
List<FunctionalMinderTreeDTO> childChildren = new ArrayList<>();
buildCaseChild(moduleCaseMap, child, childChildren);
functionalMinderTreeDTOChild.setChildren(childChildren);
children.add(functionalMinderTreeDTOChild);
buildModuleChild(moduleCaseMap, child, childChildren);
});
}
}
/**
* 用例评审-脑图用例列表查询
*
* @param request MinderReviewFunctionalCasePageRequest
* @param deleted 用例是否删除
* @param userId userId 只看我的
* @return FunctionalMinderTreeDTO
*/
public FunctionalMinderTreeDTO getReviewFunctionalCasePage(MinderReviewFunctionalCasePageRequest request, boolean deleted, String userId) {
CaseReview caseReview = caseReviewMapper.selectByPrimaryKey(request.getReviewId());
List<ReviewFunctionalCaseDTO> page = caseReviewFunctionalCaseService.page(request, deleted, userId, null);
List<String> caseIds = page.stream().map(ReviewFunctionalCaseDTO::getCaseId).toList();
List<String> moduleIds = page.stream().map(ReviewFunctionalCaseDTO::getModuleId).toList();
List<BaseTreeNode> baseTreeNodes = getBaseTreeNodes(moduleIds);
//判断虚拟节点有无数据
Map<String, List<ReviewFunctionalCaseDTO>> moduleCaseMap = page.stream().collect(Collectors.groupingBy(ReviewFunctionalCaseDTO::getModuleId));
if (moduleCaseMap.get(ModuleConstants.DEFAULT_NODE_ID) == null && CollectionUtils.isEmpty(baseTreeNodes.get(0).getChildren())) {
baseTreeNodes.remove(0);
}
//自定义字段
Map<String, List<FunctionalCaseCustomFieldDTO>> caseCustomFiledMap = functionalCaseService.getCaseCustomFiledMap(caseIds,request.getProjectId());
//构建返回数据主层级应与模块树层级相同
MinderSearchDTO minderSearchDTO = new MinderSearchDTO();
minderSearchDTO.setBaseTreeNodes(baseTreeNodes);
minderSearchDTO.setModuleCaseMap(moduleCaseMap);
minderSearchDTO.setCustomFieldMap(caseCustomFiledMap);
minderSearchDTO.setReviewPassRule(caseReview.getReviewPassRule());
minderSearchDTO.setViewFlag(request.isViewFlag());
minderSearchDTO.setViewResult(request.isViewResult());
List<FunctionalMinderTreeDTO> functionalMinderTreeDTOs = buildReviewCaseTree(minderSearchDTO);
return getRoot(functionalMinderTreeDTOs);
}
private List<FunctionalMinderTreeDTO> buildReviewCaseTree(MinderSearchDTO minderSearchDTO) {
List<FunctionalMinderTreeDTO> functionalMinderTreeNodeDTOs = new ArrayList<>();
minderSearchDTO.getBaseTreeNodes().forEach(t -> {
//构建根节点
FunctionalMinderTreeDTO functionalMinderTreeDTO = new FunctionalMinderTreeDTO();
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeDTO = getFunctionalMinderTreeNodeDTO(t);
functionalMinderTreeDTO.setData(functionalMinderTreeNodeDTO);
List<FunctionalMinderTreeDTO> children = new ArrayList<>();
//如果当前节点有用例则用例是他的子节点
minderSearchDTO.setBaseTreeNode(t);
buildReviewCaseChild(minderSearchDTO, children);
//查询当前节点下的子模块节点
buildReviewModuleChild(minderSearchDTO, children);
functionalMinderTreeDTO.setChildren(children);
functionalMinderTreeNodeDTOs.add(functionalMinderTreeDTO);
});
return functionalMinderTreeNodeDTOs;
}
private void buildReviewModuleChild(MinderSearchDTO minderSearchDTO, List<FunctionalMinderTreeDTO> children) {
List<BaseTreeNode> baseTreeNodes = minderSearchDTO.getBaseTreeNode().getChildren();
if (CollectionUtils.isNotEmpty(baseTreeNodes)) {
baseTreeNodes.forEach(child -> {
FunctionalMinderTreeDTO functionalMinderTreeDTOChild = new FunctionalMinderTreeDTO();
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeDTOChild = getFunctionalMinderTreeNodeDTO(child);
functionalMinderTreeDTOChild.setData(functionalMinderTreeNodeDTOChild);
minderSearchDTO.setBaseTreeNode(child);
List<FunctionalMinderTreeDTO> childChildren = new ArrayList<>();
buildReviewCaseChild(minderSearchDTO, childChildren);
functionalMinderTreeDTOChild.setChildren(childChildren);
children.add(functionalMinderTreeDTOChild);
buildReviewModuleChild(minderSearchDTO, childChildren);
});
}
}
private void buildReviewCaseChild(MinderSearchDTO minderSearchDTO, List<FunctionalMinderTreeDTO> children) {
Map<String, List<ReviewFunctionalCaseDTO>> moduleCaseMap = minderSearchDTO.getModuleCaseMap();
BaseTreeNode baseTreeNode = minderSearchDTO.getBaseTreeNode();
String baseTreeNodeId = baseTreeNode.getId();
if (moduleCaseMap.get(baseTreeNodeId) != null && CollectionUtils.isNotEmpty(moduleCaseMap.get(baseTreeNodeId))) {
List<ReviewFunctionalCaseDTO> reviewFunctionalCaseDTOS = moduleCaseMap.get(baseTreeNodeId);
reviewFunctionalCaseDTOS.forEach(reviewFunctionalCaseDTO -> {
FunctionalMinderTreeDTO functionalMinderTreeChild = new FunctionalMinderTreeDTO();
//加载用例为children
FunctionalMinderTreeNodeDTO functionalMinderTreeNodeChild = new FunctionalMinderTreeNodeDTO();
functionalMinderTreeNodeChild.setId(reviewFunctionalCaseDTO.getCaseId());
functionalMinderTreeNodeChild.setText(reviewFunctionalCaseDTO.getName());
List<FunctionalCaseCustomFieldDTO> functionalCaseCustomFieldDTOS = minderSearchDTO.getCustomFieldMap().get(reviewFunctionalCaseDTO.getCaseId());
if (CollectionUtils.isNotEmpty(functionalCaseCustomFieldDTOS)) {
List<FunctionalCaseCustomFieldDTO> list = functionalCaseCustomFieldDTOS.stream().filter(customField -> customField.getFieldName().equals(Translator.get("custom_field.functional_priority"))).toList();
if (CollectionUtils.isNotEmpty(list)) {
functionalMinderTreeNodeChild.setPriority(list.get(0).getDefaultValue());
}
}
if (minderSearchDTO.isViewFlag() && minderSearchDTO.isViewResult() && StringUtils.equalsIgnoreCase(minderSearchDTO.getReviewPassRule(), CaseReviewPassRule.MULTIPLE.toString())) {
functionalMinderTreeNodeChild.setStatus(reviewFunctionalCaseDTO.getStatus());
}
functionalMinderTreeNodeChild.setResource(List.of(MinderLabel.CASE.toString()));
functionalMinderTreeChild.setData(functionalMinderTreeNodeChild);
children.add(functionalMinderTreeChild);
});
}
}
} }

View File

@ -1,8 +1,7 @@
package io.metersphere.functional.controller; package io.metersphere.functional.controller;
import io.metersphere.functional.dto.FunctionalMinderTreeDTO; import io.metersphere.functional.dto.FunctionalMinderTreeDTO;
import io.metersphere.functional.request.FunctionalCasePageRequest; import io.metersphere.functional.request.FunctionalCaseMindRequest;
import io.metersphere.functional.request.MinderReviewFunctionalCasePageRequest;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.ResultHolder;
@ -14,76 +13,30 @@ import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.List;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc @AutoConfigureMockMvc
public class FunctionalCaseMinderControllerTest extends BaseTest { public class FunctionalCaseMinderControllerTest extends BaseTest {
public static final String FUNCTIONAL_CASE_LIST_URL = "/functional/mind/case/list"; public static final String FUNCTIONAL_CASE_LIST_URL = "/functional/mind/case/list";
public static final String REViEW_FUNCTIONAL_CASE_LIST_URL = "/functional/mind/case/review/list";
@Test @Test
@Order(1) @Order(1)
@Sql(scripts = {"/dml/init_file_minder_test.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED)) @Sql(scripts = {"/dml/init_file_minder_test.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testGetPageList() throws Exception { public void testGetPageList() throws Exception {
FunctionalCasePageRequest request = new FunctionalCasePageRequest(); FunctionalCaseMindRequest request = new FunctionalCaseMindRequest();
request.setProjectId("project-case-minder-test"); request.setProjectId("project-case-minder-test");
request.setCurrent(1); request.setModuleId("TEST_MINDER_MODULE_ID_GYQ");
request.setPageSize(10);
request.setSort(new HashMap<>() {{
put("createTime", "desc");
}});
MvcResult mvcResultPage = this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_LIST_URL, request); MvcResult mvcResultPage = this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_LIST_URL, request);
String contentAsString = mvcResultPage.getResponse().getContentAsString(StandardCharsets.UTF_8); String contentAsString = mvcResultPage.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class); ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
FunctionalMinderTreeDTO baseTreeNodes = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalMinderTreeDTO.class); List<FunctionalMinderTreeDTO> baseTreeNodes = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), FunctionalMinderTreeDTO.class);
Assertions.assertNotNull(baseTreeNodes); Assertions.assertNotNull(baseTreeNodes);
String jsonString = JSON.toJSONString(baseTreeNodes); String jsonString = JSON.toJSONString(baseTreeNodes);
System.out.println(jsonString); System.out.println(jsonString);
Assertions.assertEquals(2,baseTreeNodes.size());
} }
@Test
@Order(2)
public void testGetReviewPageList() throws Exception {
/* Map<String, Object> map = new HashMap<>();
map.put("customs", Arrays.asList(new LinkedHashMap() {{
put("id", "TEST_FIELD_ID");
put("operator", "in");
put("value", "222");
put("type", "List");
}}));
request.setCombine(map);*/
MinderReviewFunctionalCasePageRequest request = new MinderReviewFunctionalCasePageRequest();
request.setProjectId("project-case-minder-test");
request.setCurrent(1);
request.setPageSize(10);
request.setSort(new HashMap<>() {{
put("createTime", "desc");
}});
request.setReviewId("TEST_MINDER_REVIEW_ID_GYQ");
request.setViewFlag(true);
request.setViewResult(true);
MvcResult mvcResultPage = this.requestPostWithOkAndReturn(REViEW_FUNCTIONAL_CASE_LIST_URL, request);
String contentAsString = mvcResultPage.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
FunctionalMinderTreeDTO baseTreeNodes = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalMinderTreeDTO.class);
Assertions.assertNotNull(baseTreeNodes);
//String jsonString = JSON.toJSONString(baseTreeNodes);
//System.out.println(jsonString);
request.setReviewId("TEST_MINDER_REVIEW_ID_GYQ2");
mvcResultPage = this.requestPostWithOkAndReturn(REViEW_FUNCTIONAL_CASE_LIST_URL, request);
contentAsString = mvcResultPage.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
baseTreeNodes = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalMinderTreeDTO.class);
Assertions.assertNotNull(baseTreeNodes);
//jsonString = JSON.toJSONString(baseTreeNodes);
//System.out.println(jsonString);
}
} }

View File

@ -11,12 +11,12 @@ VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_1', 1, 'TEST_MINDER_MODULE_ID_GYQ', 'pro
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_1', NULL, NULL, NULL, NULL, NULL); INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_1', NULL, NULL, NULL, NULL, NULL);
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_2', NULL, NULL, NULL, NULL, NULL); INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_2', 0x504B03041400080808002B826D58000000000000000000000000030000007A69708BAE56CA2BCD55B232D0514A492D4E56B2527A366DC3D3DDBB9E4F59F1645FB7B1A1311AF7E9A495682826CF100A62F262F29E4DD909444A3A4A45A9C5A5392584CD5BDFF67C412384CCCD05998030CF10661CD4D05A1DA8630DE18E353236343246B60DC83532040A2AD5C60200504B0708159DEE336E000000DB000000504B010214001400080808002B826D58159DEE336E000000DB0000000300000000000000000000000000000000007A6970504B05060000000001000100310000009F0000000000, 0x504B03041400080808002B826D58000000000000000000000000030000007A69700300504B0708000000000200000000000000504B010214001400080808002B826D580000000002000000000000000300000000000000000000000000000000007A6970504B0506000000000100010031000000330000000000, 0x504B03041400080808002B826D58000000000000000000000000030000007A69700300504B0708000000000200000000000000504B010214001400080808002B826D580000000002000000000000000300000000000000000000000000000000007A6970504B0506000000000100010031000000330000000000, 0x504B03041400080808002B826D58000000000000000000000000030000007A69700300504B0708000000000200000000000000504B010214001400080808002B826D580000000002000000000000000300000000000000000000000000000000007A6970504B0506000000000100010031000000330000000000, 0x504B03041400080808002B826D58000000000000000000000000030000007A69700300504B0708000000000200000000000000504B010214001400080808002B826D580000000002000000000000000300000000000000000000000000000000007A6970504B0506000000000100010031000000330000000000);
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_3', NULL, NULL, NULL, NULL, NULL); INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_3', NULL, NULL, NULL, NULL, NULL);
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_1', 'custom_field_minder_gyq_id_3', 'P0'); INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_1', 'custom_field_minder_gyq_id_3', 'P0');
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_MINDER_CASE_ID_2', 'custom_field_minder_gyq_id_3', 'P3');
INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_time, update_time, create_user, update_user) INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_time, update_time, create_user, update_user)
@ -29,7 +29,7 @@ VALUES
INSERT INTO custom_field (id, name, scene, type, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUE INSERT INTO custom_field (id, name, scene, type, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUE
('custom_field_minder_gyq_id_3', '用例等级', 'FUNCTIONAL', 'SELECT', '', 0, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001'); ('custom_field_minder_gyq_id_3', 'functional_priority', 'FUNCTIONAL', 'SELECT', '', true, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001');
INSERT INTO case_review(id, num, name, module_id, project_id, status, review_pass_rule, pos, start_time, end_time, case_count, pass_rate, tags, description, create_time, create_user, update_time, update_user) INSERT INTO case_review(id, num, name, module_id, project_id, status, review_pass_rule, pos, start_time, end_time, case_count, pass_rate, tags, description, create_time, create_user, update_time, update_user)