feat(功能用例): 用例详情添加关联/取消关联前后置接口&部分逻辑和接口通用

This commit is contained in:
WangXu10 2024-01-02 17:00:34 +08:00 committed by Craftsman
parent 29fb6dd00e
commit 4bc05648fc
7 changed files with 223 additions and 60 deletions

View File

@ -21,10 +21,7 @@ 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;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -76,4 +73,10 @@ public class FunctionalCaseRelationshipController {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize());
return PageUtils.setPageInfo(page, functionalCaseRelationshipEdgeService.getFunctionalCasePage(request));
}
@GetMapping("/delete/{id}")
public void delete(@PathVariable("id") String id) {
functionalCaseRelationshipEdgeService.delete(id);
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.functional.mapper;
import io.metersphere.functional.dto.FunctionalCaseRelationshipDTO;
import io.metersphere.functional.request.RelationshipRequest;
import io.metersphere.system.dto.RelationshipEdgeDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -12,4 +13,10 @@ public interface ExtFunctionalCaseRelationshipEdgeMapper {
List<String> getGraphIds(@Param("ids") List<String> ids);
List<FunctionalCaseRelationshipDTO> list(@Param("request") RelationshipRequest request, @Param("sort") String sort);
RelationshipEdgeDTO getGraphId(@Param("id") String id);
List<RelationshipEdgeDTO> getEdgeByGraphId(@Param("graphId") String graphId);
void update(@Param("ids") List<String> ids, @Param("graphId") String graphId);
}

View File

@ -57,4 +57,23 @@
fcre.create_time desc
</if>
</select>
<select id="getGraphId" resultType="io.metersphere.system.dto.RelationshipEdgeDTO" parameterType="java.lang.String">
select source_id sourceId, target_id targetId, graph_id graphId from functional_case_relationship_edge where id = #{id}
</select>
<select id="getEdgeByGraphId" parameterType="java.lang.String" resultType="io.metersphere.system.dto.RelationshipEdgeDTO">
select source_id sourceId, target_id targetId from functional_case_relationship_edge where graph_id = #{graphId}
</select>
<update id="update">
update functional_case_relationship_edge set graph_id = #{graphId} 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>)
</update>
</mapper>

View File

@ -14,9 +14,9 @@ import io.metersphere.functional.mapper.ExtFunctionalCaseRelationshipEdgeMapper;
import io.metersphere.functional.mapper.FunctionalCaseRelationshipEdgeMapper;
import io.metersphere.functional.request.RelationshipAddRequest;
import io.metersphere.functional.request.RelationshipRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.RelationshipEdgeDTO;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.RelationshipEdgeUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.ExecutorType;
@ -83,27 +83,14 @@ public class FunctionalCaseRelationshipEdgeService {
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"));
}
;
});
List<RelationshipEdgeDTO> edgeDTOS = edgeList.stream().map(column -> new RelationshipEdgeDTO(column.getSourceId(), column.getTargetId())).collect(Collectors.toList());
RelationshipEdgeUtils.checkEdge(edgeDTOS);
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
@ -134,45 +121,6 @@ public class FunctionalCaseRelationshipEdgeService {
}
/**
* 给定一点深度搜索该连通图中是否存在环
*
* @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;
}
/**
* 获取已经存在的依赖关系图
*
@ -229,4 +177,11 @@ public class FunctionalCaseRelationshipEdgeService {
}
return list;
}
public void delete(String id) {
RelationshipEdgeUtils.updateGraphId(id, extFunctionalCaseRelationshipEdgeMapper::getGraphId, extFunctionalCaseRelationshipEdgeMapper::getEdgeByGraphId, extFunctionalCaseRelationshipEdgeMapper::update);
}
}

View File

@ -26,6 +26,7 @@ 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";
public static final String PAGE = "/functional/case/relationship/page";
public static final String DELETE = "/functional/case/relationship/delete/";
@Test
@ -86,6 +87,12 @@ public class FunctionalCaseRelationshipControllerTests extends BaseTest {
// 返回请求正常
Assertions.assertNotNull(postResultHolder);
//增加覆盖率
request.setId("wx_relationship_1");
request.setType("POST");
request.setSelectIds(List.of("wx_relationship_6"));
this.requestPostWithOkAndReturn(ADD, request);
}
@ -122,5 +129,23 @@ public class FunctionalCaseRelationshipControllerTests extends BaseTest {
request.setId("wx_relationship_1");
request.setType("POST");
this.requestPostWithOkAndReturn(PAGE, request);
request.setId("test1111");
request.setType("POST");
this.requestPostWithOkAndReturn(PAGE, request);
}
@Test
@Order(4)
public void testDelete() throws Exception {
MvcResult postResult = this.requestGetWithOkAndReturn(DELETE + "relationship_1");
// 获取返回值
String postReturnData = postResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder postResultHolder = JSON.parseObject(postReturnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(postResultHolder);
//异常覆盖
assertErrorCode(this.requestGet(DELETE + "test"), MsHttpResultCode.FAILED);
}
}

View File

@ -0,0 +1,27 @@
package io.metersphere.system.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author wx
*/
@Data
public class RelationshipEdgeDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String sourceId;
private String targetId;
private String graphId;
public RelationshipEdgeDTO(String sourceId, String targetId) {
this.sourceId = sourceId;
this.targetId = targetId;
}
}

View File

@ -0,0 +1,127 @@
package io.metersphere.system.utils;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.RelationshipEdgeDTO;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
public class RelationshipEdgeUtils {
public static void checkEdge(List<RelationshipEdgeDTO> edgeDTOS) {
HashSet<String> nodeIds = new HashSet<>();
nodeIds.addAll(edgeDTOS.stream().map(RelationshipEdgeDTO::getSourceId).collect(Collectors.toSet()));
nodeIds.addAll(edgeDTOS.stream().map(RelationshipEdgeDTO::getTargetId).collect(Collectors.toSet()));
// 判断是否有环
HashSet<String> visitedSet = new HashSet<>();
nodeIds.forEach(nodeId -> {
if (!visitedSet.contains(nodeId) && directedCycle(nodeId, edgeDTOS, new HashSet<>(), visitedSet)) {
throw new MSException(Translator.get("cycle_relationship"));
}
});
}
/**
* 给定一点深度搜索该连通图中是否存在环
*
* @param id
* @param edges
* @param markSet 标记该路径上经过的节点
* @param visitedSet 标记访问过的节点
* @return
*/
public static boolean directedCycle(String id, List<RelationshipEdgeDTO> 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 (RelationshipEdgeDTO 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;
}
public static void updateGraphId(String id, Function<String, RelationshipEdgeDTO> getGraphIdFunc, Function<String, List<RelationshipEdgeDTO>> getEdgeByGraphIdFunc, BiConsumer<List, String> updateFunc) {
RelationshipEdgeDTO edge = getGraphIdFunc.apply(id);
if (edge == null) {
throw new MSException(Translator.get("relationship_not_exist"));
}
List<RelationshipEdgeDTO> edges = getEdgeByGraphIdFunc.apply(edge.getGraphId());
// 去掉要删除的边
edges = edges.stream()
.filter(i -> !i.getSourceId().equals(edge.getSourceId()) && !i.getTargetId().equals(edge.getTargetId()))
.collect(Collectors.toList());
Set<String> nodes = new HashSet<>();
Set<String> markSet = new HashSet<>();
nodes.addAll(edges.stream().map(RelationshipEdgeDTO::getSourceId).collect(Collectors.toSet()));
nodes.addAll(edges.stream().map(RelationshipEdgeDTO::getTargetId).collect(Collectors.toSet()));
dfsForMark(edge.getSourceId(), edges, markSet, true);
dfsForMark(edge.getSourceId(), edges, markSet, false);
// 如果连通的点减少说明形成了两个不连通子图重新设置graphId
if (markSet.size() != nodes.size()) {
List<String> updateIds = new ArrayList<>(markSet);
updateFunc.accept(updateIds, UUID.randomUUID().toString());
}
}
/**
* 遍历标记经过的节点
*
* @param node
* @param edges
* @param markSet
* @param isForwardDirection
*/
public static void dfsForMark(String node, List<RelationshipEdgeDTO> edges, Set<String> markSet, boolean isForwardDirection) {
markSet.add(node);
Set<String> nextLevelNodes = new HashSet<>();
for (RelationshipEdgeDTO edge : edges) {
if (isForwardDirection) {
if (node.equals(edge.getSourceId())) {
nextLevelNodes.add(edge.getTargetId());
}
} else {
if (node.equals(edge.getTargetId())) {
nextLevelNodes.add(edge.getSourceId());
}
}
}
nextLevelNodes.forEach(nextNode -> {
if (!markSet.contains(nextNode)) {
dfsForMark(nextNode, edges, markSet, true);
dfsForMark(nextNode, edges, markSet, false);
}
});
}
}