diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApplicationNumScope.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApplicationNumScope.java new file mode 100644 index 0000000000..33847b4dd0 --- /dev/null +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ApplicationNumScope.java @@ -0,0 +1,19 @@ +package io.metersphere.sdk.constants; + +public enum ApplicationNumScope { + API_DEFINITION, + API_TEST_CASE, + API_SCENARIO, + + UI_SCENARIO, + UI_ELEMENT, + UI_CUSTOM_COMMAND, + + LOAD_TEST, + + TEST_PLAN, + + BUG_MANAGEMENT, + + CASE_MANAGEMENT +} diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ProjectApplicationType.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ProjectApplicationType.java index 56081eff6c..4bf424cb8d 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ProjectApplicationType.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/ProjectApplicationType.java @@ -20,27 +20,25 @@ public class ProjectApplicationType { } - //UI测试 - public enum UI{ + public enum UI { UI_CLEAN_REPORT, UI_SHARE_REPORT, UI_RESOURCE_POOL_ID, } - //性能测试 - public enum PERFORMANCE_TEST{ - PERFORMANCE_TEST_CLEAN_REPORT, - PERFORMANCE_TEST_SHARE_REPORT, - PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE, - PERFORMANCE_TEST_SCRIPT_REVIEWER_ID, + public enum LOAD_TEST { + LOAD_TEST_CLEAN_REPORT, + LOAD_TEST_SHARE_REPORT, + LOAD_TEST_SCRIPT_REVIEWER_ENABLE, + LOAD_TEST_SCRIPT_REVIEWER_ID, } //接口测试 - public enum API{ + public enum API { API_URL_REPEATABLE, API_CLEAN_REPORT, API_SHARE_REPORT, @@ -53,40 +51,40 @@ public class ProjectApplicationType { //用例管理 - public enum CASE{ + public enum CASE { CASE_PUBLIC, CASE_RE_REVIEW, } //用例管理-关联需求 - public enum CASE_RELATED_CONFIG{ + public enum CASE_RELATED_CONFIG { CASE_RELATED, CASE_ENABLE, } //缺陷管理 - public enum BUG{ + public enum BUG { BUG_SYNC } //缺陷管理-同步配置项 - public enum BUG_SYNC_CONFIG{ + public enum BUG_SYNC_CONFIG { CRON_EXPRESSION, SYNC_ENABLE, MECHANISM, } // 版本管理-配置项 - public enum VERSION{ + public enum VERSION { VERSION_ENABLE } /** * 记录项目中配置的默认模板 */ - public enum DEFAULT_TEMPLATE{ + public enum DEFAULT_TEMPLATE { FUNCTIONAL_DEFAULT_TEMPLATE, BUG_DEFAULT_TEMPLATE, API_DEFAULT_TEMPLATE, diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectApplicationController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectApplicationController.java index 5a290b92f7..cc0b486594 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectApplicationController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectApplicationController.java @@ -105,7 +105,7 @@ public class ProjectApplicationController { @Operation(summary = "性能测试-获取配置") @RequiresPermissions(PermissionConstants.PROJECT_APPLICATION_PERFORMANCE_TEST_READ) public Map getPerformanceTest(@Validated @RequestBody ProjectApplicationRequest request) { - List types = Arrays.asList(ProjectApplicationType.PERFORMANCE_TEST.values()).stream().map(ProjectApplicationType.PERFORMANCE_TEST::name).collect(Collectors.toList()); + List types = Arrays.asList(ProjectApplicationType.LOAD_TEST.values()).stream().map(ProjectApplicationType.LOAD_TEST::name).collect(Collectors.toList()); return projectApplicationService.get(request, types); } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectApplicationService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectApplicationService.java index 44089e7c9b..8737455e9b 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectApplicationService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectApplicationService.java @@ -108,7 +108,7 @@ public class ProjectApplicationService { String type = application.getType(); if (StringUtils.equals(type, ProjectApplicationType.TEST_PLAN.TEST_PLAN_CLEAN_REPORT.name()) || StringUtils.equals(type, ProjectApplicationType.UI.UI_CLEAN_REPORT.name()) - || StringUtils.equals(type, ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_CLEAN_REPORT.name()) + || StringUtils.equals(type, ProjectApplicationType.LOAD_TEST.LOAD_TEST_CLEAN_REPORT.name()) || StringUtils.equals(type, ProjectApplicationType.API.API_CLEAN_REPORT.name())) { //清除 测试计划/UI测试/性能测试/接口测试 报告 定时任务 this.doHandleSchedule(application, currentUser); diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java index 0ad46aa737..0b7d187f0b 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectApplicationControllerTests.java @@ -177,7 +177,7 @@ public class ProjectApplicationControllerTests extends BaseTest { @Order(7) public void testPerformanceClean() throws Exception { //新增 - ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_CLEAN_REPORT.name(), TIME_TYPE_VALUE); + ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_CLEAN_REPORT.name(), TIME_TYPE_VALUE); this.requestPost(PERFORMANCE_UPDATE_URL, request); //更新 request.setTypeValue("4M"); @@ -192,7 +192,7 @@ public class ProjectApplicationControllerTests extends BaseTest { @Order(8) public void testPerformanceShare() throws Exception { //新增 - ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_SHARE_REPORT.name(), TIME_TYPE_VALUE); + ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_SHARE_REPORT.name(), TIME_TYPE_VALUE); this.requestPost(PERFORMANCE_UPDATE_URL, request); //更新 request.setTypeValue("5M"); @@ -204,7 +204,7 @@ public class ProjectApplicationControllerTests extends BaseTest { @Order(9) public void testPerformanceReviewer() throws Exception { //新增 - ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE.name(), "admin"); + ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_SCRIPT_REVIEWER_ENABLE.name(), "admin"); this.requestPost(PERFORMANCE_UPDATE_URL, request); } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/uid/NumGenerator.java b/backend/services/system-setting/src/main/java/io/metersphere/system/uid/NumGenerator.java new file mode 100644 index 0000000000..4cea56a3d0 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/uid/NumGenerator.java @@ -0,0 +1,76 @@ +package io.metersphere.system.uid; + +import com.fit2cloud.quartz.anno.QuartzScheduled; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.util.LogUtils; +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RIdGenerator; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class NumGenerator { + private static final long INIT = 100001L; // 代表从100001开始,各种domain的 num + private static final long LIMIT = 1; + + private static Redisson redisson; + private static StringRedisTemplate stringRedisTemplate; + private static ProjectMapper projectMapper; + + public NumGenerator(Redisson redisson, StringRedisTemplate stringRedisTemplate, ProjectMapper projectMapper) { + NumGenerator.redisson = redisson; + NumGenerator.stringRedisTemplate = stringRedisTemplate; + NumGenerator.projectMapper = projectMapper; + } + + /** + * @param prefix 前缀: PROJECT_ID, 或者 PROJECT_ID + "_" + DOMAIN 例如接口用例的前缀为: 100001_12345 + * @param scope 用例类型 + */ + public static long nextNum(String prefix, ApplicationNumScope scope) { + RIdGenerator idGenerator = redisson.getIdGenerator(prefix + "_" + scope.name()); + // 每次都尝试初始化,容量为1,只有一个线程可以初始化成功 + if (scope.equals(ApplicationNumScope.API_TEST_CASE)) { + // 二级的用例 + idGenerator.tryInit(Long.parseLong(prefix.split("_")[1] + INIT), LIMIT); + } else { + idGenerator.tryInit(INIT, LIMIT); + } + return idGenerator.nextId(); + } + + @QuartzScheduled(cron = "0 1 0 * * ?") + public void cleanDeletedProjectResource() { + for (ApplicationNumScope value : ApplicationNumScope.values()) { + cleanDeletedProjectResource(value); + } + } + + private void cleanDeletedProjectResource(ApplicationNumScope value) { + String suffix = "}:allocation"; + + ScanOptions options = ScanOptions.scanOptions().match("*_" + value.name()).count(1000).build(); + try ( + Cursor scan = stringRedisTemplate.scan(options) + ) { + while (scan.hasNext()) { + String key = scan.next(); + if (StringUtils.contains(key, suffix)) { + continue; + } + Project project = projectMapper.selectByPrimaryKey(key.split("_")[0]); + if (project == null) { + LogUtils.info("清理已经删除项目的num数据: " + key); + stringRedisTemplate.delete(key); + stringRedisTemplate.delete("{" + key + suffix); + } + } + } + } + +} diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/TestRIdGenerator.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/TestRIdGenerator.java deleted file mode 100644 index ecc640e072..0000000000 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/TestRIdGenerator.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.metersphere.system.controller; - -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.redisson.Redisson; -import org.redisson.api.RIdGenerator; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class TestRIdGenerator { - @Resource - private Redisson redisson; - - @Test - public void testId1() throws Exception { - String projectId = "projectId"; - RIdGenerator idGenerator = redisson.getIdGenerator(projectId); - long capacity = 1000000; // 容量,代表每个项目最多可以生成多少个id - long init = 1000000_1000001L; // 代表从100_0000_100_0001开始,项目的num - idGenerator.tryInit(init, capacity); - long nextId = idGenerator.nextId(); - Assertions.assertEquals(nextId, init); -// for (int i = 0; i < capacity; i++) { -// long nextId = idGenerator.nextId(); -// } - } -} diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/rid/RIdGeneratorTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/rid/RIdGeneratorTests.java new file mode 100644 index 0000000000..358fe6e771 --- /dev/null +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/rid/RIdGeneratorTests.java @@ -0,0 +1,77 @@ +package io.metersphere.system.rid; + +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.system.uid.NumGenerator; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RIdGeneratorTests { + + @Test + @Order(1) + public void testId1() throws Exception { + String projectId = "100001"; + + long capacity = 10; // 容量,代表每个项目最多可以生成多少个id + long init = 100001L; // 代表从1000001开始,项目的 num + long start = System.currentTimeMillis(); + AtomicLong atomicLong = new AtomicLong(init); + // 使用多线程执行 + ExecutorService executorService = Executors.newFixedThreadPool(5); + for (int i = 0; i < capacity; i++) { + executorService.submit(() -> { + long nextId = NumGenerator.nextNum(projectId, ApplicationNumScope.API_DEFINITION); + System.out.println(nextId); + if (atomicLong.get() < nextId) { + atomicLong.set(nextId); + } + }); + } + executorService.close(); + System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms"); + Assertions.assertEquals(capacity + init, atomicLong.get() + 1); + } + + @Test + @Order(2) + public void testId2() throws Exception { + String projectId = "100001"; + + long capacity = 10; // 容量,代表每个项目最多可以生成多少个id + long init = 100001L; // 代表从1000001开始,项目的 num + long apiNum = 100005; + long start = System.currentTimeMillis(); + AtomicLong atomicLong = new AtomicLong(init); + // 使用多线程执行 + ExecutorService executorService = Executors.newFixedThreadPool(5); + for (int i = 0; i < capacity; i++) { + executorService.submit(() -> { + // 接口用例的前缀为: PROJECT_ID_API_DEFINITION 比较特殊 + long nextId = NumGenerator.nextNum(projectId + "_" + apiNum, ApplicationNumScope.API_TEST_CASE); + System.out.println(nextId); + if (atomicLong.get() < nextId) { + atomicLong.set(nextId); + } + }); + } + executorService.close(); + System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms"); + Assertions.assertEquals(capacity + Long.parseLong(apiNum + "" + init), atomicLong.get() + 1); + } + + @Test + @Order(3) + public void testClean() { + NumGenerator numGenerator = CommonBeanFactory.getBean(NumGenerator.class); + numGenerator.cleanDeletedProjectResource(); + } +}