feat(功能用例): 用例详情添加前置用例&后置用例

This commit is contained in:
WangXu10 2023-12-28 13:59:55 +08:00 committed by f2c-ci-robot[bot]
parent d632b5e6d4
commit 4871c48661
9 changed files with 270 additions and 5 deletions

View File

@ -165,4 +165,5 @@ case_review.not.exist=Case review does not exist
case_review_content.not.exist = Review comments cannot be empty
case_review_history.system=System trigger
case_review.viewFlag.not_blank=View flag cannot be empty
functional_case_relationship_edge.type.not_blank=Relationship type cannot be empty
functional_case_relationship_edge.type.not_blank=Relationship type cannot be empty
cycle_relationship=There is a circular dependency after association, please check the dependency relationship

View File

@ -165,3 +165,4 @@ case_review_content.not.exist = 评审意见不能为空
case_review_history.system=系统触发
case_review.viewFlag.not_blank=查看标识不能为空
functional_case_relationship_edge.type.not_blank=类型不能为空
cycle_relationship=关联后存在循环依赖,请检查依赖关系

View File

@ -165,4 +165,5 @@ case_review.not.exist=用例評審不存在
case_review_content.not.exist = 評審意見不能為空
case_review_history.system=系統觸發
case_review.viewFlag.not_blank=查看標誌不能為空
functional_case_relationship_edge.type.not_blank=類型不能為空
functional_case_relationship_edge.type.not_blank=類型不能為空
cycle_relationship=關聯后存在循環依賴,請檢查依賴關係

View File

@ -4,6 +4,7 @@ import com.alibaba.excel.util.StringUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.functional.dto.FunctionalCasePageDTO;
import io.metersphere.functional.request.RelationshipAddRequest;
import io.metersphere.functional.request.RelationshipPageRequest;
import io.metersphere.functional.service.FunctionalCaseRelationshipEdgeService;
import io.metersphere.functional.service.FunctionalCaseService;
@ -11,9 +12,11 @@ import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
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 org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@ -49,5 +52,16 @@ public class FunctionalCaseRelationshipController {
return PageUtils.setPageInfo(page, functionalCaseService.getFunctionalCasePage(request, false));
}
@PostMapping("/add")
@Operation(summary = "用例管理-功能用例-用例详情-前后置关系-添加前后置关系")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
@CheckOwner(resourceId = "#request.getCaseId()", resourceType = "project")
public void add(@Validated @RequestBody RelationshipAddRequest request) {
List<String> excludeIds = functionalCaseRelationshipEdgeService.getExcludeIds(request.getId());
request.setExcludeIds(excludeIds);
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
functionalCaseRelationshipEdgeService.add(request, ids, SessionUtils.getUserId());
}
}
}

View File

@ -0,0 +1,11 @@
package io.metersphere.functional.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtFunctionalCaseRelationshipEdgeMapper {
List<String> getGraphIds(@Param("ids") List<String> ids);
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.functional.mapper.ExtFunctionalCaseRelationshipEdgeMapper">
<select id="getGraphIds" resultType="java.lang.String">
select distinct graph_id
from functional_case_relationship_edge
where source_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
or target_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,7 +1,7 @@
package io.metersphere.functional.request;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
import io.metersphere.functional.dto.BaseFunctionalCaseBatchDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@ -10,7 +10,7 @@ import lombok.Data;
* @author wx
*/
@Data
public class RelationshipAddRequest extends TableBatchProcessDTO {
public class RelationshipAddRequest extends BaseFunctionalCaseBatchDTO {
@Schema(description = "用例id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case.id.not_blank}")

View File

@ -9,11 +9,24 @@ package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCaseRelationshipEdge;
import io.metersphere.functional.domain.FunctionalCaseRelationshipEdgeExample;
import io.metersphere.functional.mapper.ExtFunctionalCaseRelationshipEdgeMapper;
import io.metersphere.functional.mapper.FunctionalCaseRelationshipEdgeMapper;
import io.metersphere.functional.request.RelationshipAddRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -22,6 +35,10 @@ public class FunctionalCaseRelationshipEdgeService {
@Resource
private FunctionalCaseRelationshipEdgeMapper functionalCaseRelationshipEdgeMapper;
@Resource
private ExtFunctionalCaseRelationshipEdgeMapper extFunctionalCaseRelationshipEdgeMapper;
@Resource
SqlSessionFactory sqlSessionFactory;
public List<String> getExcludeIds(String id) {
@ -43,4 +60,163 @@ public class FunctionalCaseRelationshipEdgeService {
return functionalCaseRelationshipEdgeMapper.selectByExample(example);
}
/**
* 添加前后置用例
*
* @param request request
* @param ids ids
*/
public void add(RelationshipAddRequest request, List<String> ids, String userId) {
List<FunctionalCaseRelationshipEdge> edgeList = getExistRelationshipEdges(ids, request.getId());
Set<String> addEdgesIds = new HashSet<>();
String graphId = IDGenerator.nextStr();
switch (request.getType()) {
case "PRE":
addPre(request, ids, userId, edgeList, addEdgesIds, graphId);
break;
case "POST":
addPost(request, ids, userId, edgeList, addEdgesIds, graphId);
break;
default:
break;
}
HashSet<String> nodeIds = new HashSet<>();
nodeIds.addAll(edgeList.stream().map(FunctionalCaseRelationshipEdge::getSourceId).collect(Collectors.toSet()));
nodeIds.addAll(edgeList.stream().map(FunctionalCaseRelationshipEdge::getTargetId).collect(Collectors.toSet()));
// 判断是否有环
HashSet<String> visitedSet = new HashSet<>();
nodeIds.forEach(nodeId -> {
if (!visitedSet.contains(nodeId) && directedCycle(nodeId, edgeList, new HashSet<>(), visitedSet)) {
throw new MSException(Translator.get("cycle_relationship"));
}
;
});
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
FunctionalCaseRelationshipEdgeMapper batchMapper = sqlSession.getMapper(FunctionalCaseRelationshipEdgeMapper.class);
edgeList.forEach(item -> {
if (addEdgesIds.contains(item.getSourceId() + item.getTargetId())) {
if (batchMapper.selectByPrimaryKey(item.getId()) == null) {
batchMapper.insert(item);
} else {
item.setGraphId(graphId); // 把原来图的id设置成合并后新的图的id
batchMapper.updateByPrimaryKey(item);
}
} else {
item.setGraphId(graphId); // 把原来图的id设置成合并后新的图的id
batchMapper.updateByPrimaryKey(item);
}
});
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
/**
* 添加后置用例
*
* @param request
* @param ids
* @param userId
* @param edgeList
* @param addEdgesIds
* @param graphId
*/
private void addPost(RelationshipAddRequest request, List<String> ids, String userId, List<FunctionalCaseRelationshipEdge> edgeList, Set<String> addEdgesIds, String graphId) {
ids.forEach(sourceId -> {
FunctionalCaseRelationshipEdge edge = createRelationshipEdge(sourceId, request.getId(), graphId, userId);
edgeList.add(edge);
addEdgesIds.add(edge.getSourceId() + edge.getTargetId());
});
}
/**
* 给定一点深度搜索该连通图中是否存在环
*
* @param id
* @param edges
* @param markSet 标记该路径上经过的节点
* @param visitedSet 标记访问过的节点
* @return
*/
public boolean directedCycle(String id, List<FunctionalCaseRelationshipEdge> edges, Set<String> markSet, Set<String> visitedSet) {
if (markSet.contains(id)) {
// 如果已经访问过该节点则说明存在环
return true;
}
markSet.add(id);
visitedSet.add(id);
ArrayList<String> nextLevelNodes = new ArrayList();
for (FunctionalCaseRelationshipEdge relationshipEdge : edges) {
if (id.equals(relationshipEdge.getSourceId())) {
nextLevelNodes.add(relationshipEdge.getTargetId());
}
}
for (String nextNode : nextLevelNodes) {
if (directedCycle(nextNode, edges, markSet, visitedSet)) {
return true;
}
}
// 关键递归完这一条路径要把这个标记去掉否则会误判为有环
// 比如 1->3, 1->2->3 , 3 经过多次但是无环
markSet.remove(id);
return false;
}
/**
* 获取已经存在的依赖关系图
*
* @param ids
* @param id
* @return
*/
private List<FunctionalCaseRelationshipEdge> getExistRelationshipEdges(List<String> ids, String id) {
List<String> graphNodes = new ArrayList<>();
graphNodes.add(id);
graphNodes.addAll(ids);
List<String> graphIds = extFunctionalCaseRelationshipEdgeMapper.getGraphIds(graphNodes);
if (CollectionUtils.isEmpty(graphIds)) {
return new ArrayList<>();
}
FunctionalCaseRelationshipEdgeExample example = new FunctionalCaseRelationshipEdgeExample();
example.createCriteria()
.andGraphIdIn(graphIds);
return functionalCaseRelationshipEdgeMapper.selectByExample(example);
}
/**
* 添加前置用例
*
* @param request request
* @param ids ids
*/
private void addPre(RelationshipAddRequest request, List<String> ids, String userId, List<FunctionalCaseRelationshipEdge> edgeList, Set<String> addEdgesIds, String graphId) {
ids.forEach(targetId -> {
FunctionalCaseRelationshipEdge edge = createRelationshipEdge(request.getId(), targetId, graphId, userId);
edgeList.add(edge);
addEdgesIds.add(edge.getSourceId() + edge.getTargetId());
});
}
private FunctionalCaseRelationshipEdge createRelationshipEdge(String sourceId, String targetId, String graphId, String userId) {
FunctionalCaseRelationshipEdge edge = new FunctionalCaseRelationshipEdge();
edge.setId(IDGenerator.nextStr());
edge.setSourceId(sourceId);
edge.setTargetId(targetId);
edge.setGraphId(graphId);
edge.setCreateUser(userId);
edge.setCreateTime(System.currentTimeMillis());
edge.setUpdateTime(System.currentTimeMillis());
return edge;
}
}

View File

@ -1,9 +1,11 @@
package io.metersphere.functional.controller;
import io.metersphere.functional.request.RelationshipAddRequest;
import io.metersphere.functional.request.RelationshipPageRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.controller.handler.result.MsHttpResultCode;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@ -13,6 +15,7 @@ import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -20,6 +23,7 @@ import java.util.HashMap;
public class FunctionalCaseRelationshipControllerTests extends BaseTest {
public static final String RELATE_PAGE = "/functional/case/relationship/relate/page";
public static final String ADD = "/functional/case/relationship/add";
@Test
@ -44,4 +48,41 @@ public class FunctionalCaseRelationshipControllerTests extends BaseTest {
}});
this.requestPostWithOkAndReturn(RELATE_PAGE, request);
}
@Test
@Order(2)
public void testAddRelationship() throws Exception {
//前置用例
RelationshipAddRequest request = new RelationshipAddRequest();
request.setId("wx_relationship_1");
request.setProjectId("wx_relationship");
request.setType("PRE");
request.setSelectIds(List.of("wx_relationship_3"));
MvcResult mvcResult = this.requestPostWithOkAndReturn(ADD, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
//测试依赖关系 形成环
request.setId("wx_relationship_3");
request.setProjectId("wx_relationship");
request.setType("PRE");
request.setSelectIds(List.of("wx_relationship_1"));
assertErrorCode(this.requestPost(ADD, request), MsHttpResultCode.FAILED);
//后置用例
request.setId("wx_relationship_4");
request.setType("POST");
request.setSelectIds(List.of("wx_relationship_5"));
MvcResult postResult = this.requestPostWithOkAndReturn(ADD, request);
// 获取返回值
String postReturnData = postResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder postResultHolder = JSON.parseObject(postReturnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(postResultHolder);
}
}