fix(测试跟踪): 功能用例默认模块多次创建的问题

This commit is contained in:
song-cc-rock 2023-07-17 14:45:13 +08:00 committed by fit2-zhao
parent 2b2069581f
commit d424ef28bc
3 changed files with 71 additions and 49 deletions

View File

@ -4,21 +4,21 @@ import io.metersphere.base.domain.TestCaseNode;
import io.metersphere.commons.constants.OperLogConstants; import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.OperLogModule; import io.metersphere.commons.constants.OperLogModule;
import io.metersphere.commons.constants.PermissionConstants; import io.metersphere.commons.constants.PermissionConstants;
import io.metersphere.dto.TestCaseNodeDTO;
import io.metersphere.log.annotation.MsAuditLog; import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.log.annotation.MsRequestLog; import io.metersphere.log.annotation.MsRequestLog;
import io.metersphere.plan.request.function.QueryTestPlanCaseRequest;
import io.metersphere.request.testcase.DragNodeRequest; import io.metersphere.request.testcase.DragNodeRequest;
import io.metersphere.request.testcase.QueryNodeRequest; import io.metersphere.request.testcase.QueryNodeRequest;
import io.metersphere.request.testcase.QueryTestCaseRequest; import io.metersphere.request.testcase.QueryTestCaseRequest;
import io.metersphere.plan.request.function.QueryTestPlanCaseRequest;
import io.metersphere.request.testreview.QueryCaseReviewRequest; import io.metersphere.request.testreview.QueryCaseReviewRequest;
import io.metersphere.dto.TestCaseNodeDTO;
import io.metersphere.service.BaseCheckPermissionService; import io.metersphere.service.BaseCheckPermissionService;
import io.metersphere.service.TestCaseNodeService; import io.metersphere.service.TestCaseNodeService;
import io.metersphere.service.wapper.CheckPermissionService; import io.metersphere.service.wapper.CheckPermissionService;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;

View File

@ -1,21 +1,16 @@
package io.metersphere.listener; package io.metersphere.listener;
import io.metersphere.base.domain.ModuleNode;
import io.metersphere.base.domain.Project; import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.TestCaseNodeExample;
import io.metersphere.base.mapper.ProjectMapper; import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.ext.ExtModuleNodeMapper; import io.metersphere.base.mapper.ext.ExtModuleNodeMapper;
import io.metersphere.commons.constants.KafkaTopicConstants; import io.metersphere.commons.constants.KafkaTopicConstants;
import io.metersphere.commons.constants.ProjectModuleDefaultNodeEnum;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.service.TestCaseNodeService;
import jakarta.annotation.Resource;
import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.UUID;
@Component @Component
public class ProjectCreatedListener { public class ProjectCreatedListener {
public static final String CONSUME_ID = "test_track_project-created"; public static final String CONSUME_ID = "test_track_project-created";
@ -24,6 +19,8 @@ public class ProjectCreatedListener {
private ExtModuleNodeMapper extModuleNodeMapper; private ExtModuleNodeMapper extModuleNodeMapper;
@Resource @Resource
private ProjectMapper projectMapper; private ProjectMapper projectMapper;
@Resource
private TestCaseNodeService testCaseNodeService;
@KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.PROJECT_CREATED_TOPIC, groupId = "${spring.application.name}") @KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.PROJECT_CREATED_TOPIC, groupId = "${spring.application.name}")
public void consume(ConsumerRecord<?, String> record) { public void consume(ConsumerRecord<?, String> record) {
@ -37,23 +34,7 @@ public class ProjectCreatedListener {
if (project == null) { if (project == null) {
return; return;
} }
// 创建功能用例默认模块
// 防止重复创建功能用例默认节点 testCaseNodeService.createDefaultNode(projectId);
TestCaseNodeExample example = new TestCaseNodeExample();
example.createCriteria()
.andProjectIdEqualTo(projectId).andNameEqualTo(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getNodeName());
List<ModuleNode> moduleNodes = extModuleNodeMapper.selectByExample(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getTableName(), example);
if (moduleNodes.size() == 0) {
ModuleNode record = new ModuleNode();
record.setId(UUID.randomUUID().toString());
record.setCreateUser(project.getCreateUser());
record.setPos(1.0);
record.setLevel(1);
record.setCreateTime(System.currentTimeMillis());
record.setUpdateTime(System.currentTimeMillis());
record.setProjectId(projectId);
record.setName(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getNodeName());
extModuleNodeMapper.insert(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getTableName(), record);
}
} }
} }

View File

@ -12,6 +12,7 @@ import io.metersphere.base.mapper.ext.ExtTestCaseNodeMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestReviewCaseMapper; import io.metersphere.base.mapper.ext.ExtTestReviewCaseMapper;
import io.metersphere.commons.constants.MicroServiceName; import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.commons.constants.ProjectModuleDefaultNodeEnum;
import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
@ -36,6 +37,8 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -69,6 +72,10 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
TestPlanProjectService testPlanProjectService; TestPlanProjectService testPlanProjectService;
@Resource @Resource
TestPlanService testPlanService; TestPlanService testPlanService;
@Resource
private RedissonClient redissonClient;
private static final String TEST_CASE_DEFAULT_NODE_CREATE_KEY = "TEST_CASE:DEFAULT_NODE:CREATE";
public TestCaseNodeService() { public TestCaseNodeService() {
super(TestCaseNodeDTO.class); super(TestCaseNodeDTO.class);
@ -122,25 +129,12 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
} }
} }
public TestCaseNode getDefaultNode(String projectId) { public TestCaseNode checkDefaultNode(String projectId) {
TestCaseNodeExample example = new TestCaseNodeExample(); TestCaseNode defaultNode = getDefaultNode(projectId);
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo("未规划用例").andParentIdIsNull(); if (defaultNode == null) {
List<TestCaseNode> list = testCaseNodeMapper.selectByExample(example); return this.createDefaultNode(projectId);
if (CollectionUtils.isEmpty(list)) {
NodeNumDTO record = new NodeNumDTO();
record.setId(UUID.randomUUID().toString());
record.setCreateUser(SessionUtils.getUserId());
record.setName("未规划用例");
record.setPos(1.0);
record.setLevel(1);
record.setCreateTime(System.currentTimeMillis());
record.setUpdateTime(System.currentTimeMillis());
record.setProjectId(projectId);
testCaseNodeMapper.insert(record);
record.setCaseNum(0);
return record;
} else { } else {
return list.get(0); return defaultNode;
} }
} }
@ -222,7 +216,7 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
request.setQueryUi(queryUi); request.setQueryUi(queryUi);
this.setRequestWeekParam(request); this.setRequestWeekParam(request);
// 判断当前项目下是否有默认模块没有添加默认模块 // 判断当前项目下是否有默认模块没有添加默认模块
this.getDefaultNode(projectId); this.checkDefaultNode(projectId);
request.setProjectId(projectId); request.setProjectId(projectId);
request.setUserId(SessionUtils.getUserId()); request.setUserId(SessionUtils.getUserId());
request.setNodeIds(null); request.setNodeIds(null);
@ -291,8 +285,10 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
public List<TestCaseNodeDTO> getTrashCaseNode(String projectId, QueryTestCaseRequest request) { public List<TestCaseNodeDTO> getTrashCaseNode(String projectId, QueryTestCaseRequest request) {
// 初始化回收站中模块被删除的用例, 挂在默认未规划模块, 获取回收站模块节点数据 // 初始化回收站中模块被删除的用例, 挂在默认未规划模块, 获取回收站模块节点数据
TestCaseNode defaultNode = this.getDefaultNode(projectId); TestCaseNode defaultNode = this.checkDefaultNode(projectId);
if (defaultNode != null) {
extTestCaseMapper.updateNoModuleTrashNodeToDefault(projectId, defaultNode.getId(), defaultNode.getName()); extTestCaseMapper.updateNoModuleTrashNodeToDefault(projectId, defaultNode.getId(), defaultNode.getName());
}
request.setProjectId(projectId); request.setProjectId(projectId);
request.setNodeIds(null); request.setNodeIds(null);
ServiceUtils.setBaseQueryRequestCustomMultipleFields(request); ServiceUtils.setBaseQueryRequestCustomMultipleFields(request);
@ -761,4 +757,49 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
} }
} }
} }
public NodeNumDTO createDefaultNode(String projectId) {
// 加锁, 防止并发创建
RLock lock = redissonClient.getLock(TEST_CASE_DEFAULT_NODE_CREATE_KEY + ":" + projectId);
if (lock.tryLock()) {
try {
// 双重检查, 判断是否已经存在默认节点
if (getDefaultNode(projectId) != null) {
return null;
}
// 创建默认节点, 只执行一次
NodeNumDTO record = new NodeNumDTO();
record.setId(UUID.randomUUID().toString());
record.setCreateUser(SessionUtils.getUserId());
record.setName(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getNodeName());
record.setPos(1.0);
record.setLevel(1);
record.setCreateTime(System.currentTimeMillis());
record.setUpdateTime(System.currentTimeMillis());
record.setProjectId(projectId);
testCaseNodeMapper.insert(record);
record.setCaseNum(0);
return record;
}catch (Exception e){
LogUtil.error(e);
return null;
} finally {
lock.unlock();
}
} else {
return null;
}
}
public TestCaseNode getDefaultNode(String projectId) {
TestCaseNodeExample example = new TestCaseNodeExample();
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(ProjectModuleDefaultNodeEnum.TEST_CASE_DEFAULT_NODE.getNodeName()).andParentIdIsNull();
List<TestCaseNode> defaultNodes = testCaseNodeMapper.selectByExample(example);
if (CollectionUtils.isEmpty(defaultNodes)) {
return null;
} else {
return defaultNodes.get(0);
}
}
} }