diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlan.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlan.java index 846aa6bf4c..589ab79d84 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlan.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlan.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlan implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiCase.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiCase.java index b4a6e07f22..8ec8ca00a3 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiCase.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiCase.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanApiCase implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_api_case.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_api_case.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_api_case.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiScenario.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiScenario.java index f6621d1a60..db81ffd6d9 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiScenario.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanApiScenario.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanApiScenario implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_api_scenario.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_api_scenario.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_api_scenario.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanConfig.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanConfig.java index d6ff9a74a2..25bf408f8c 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanConfig.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanConfig.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanConfig implements Serializable { @Schema(title = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_config.test_plan_id.not_blank}", groups = {Created.class}) + @NotBlank(message = "{test_plan_config.test_plan_id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_config.test_plan_id.length_range}", groups = {Created.class, Updated.class}) private String testPlanId; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecuteRecord.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecuteRecord.java index b8dffb4d41..bbbf104b6b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecuteRecord.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecuteRecord.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanExecuteRecord implements Serializable { @Schema(title = "测试计划执行记录ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_execute_record.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_execute_record.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_execute_record.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecutionQueue.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecutionQueue.java index cdefb52b8f..7bdec53df4 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecutionQueue.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanExecutionQueue.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanExecutionQueue implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_execution_queue.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_execution_queue.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_execution_queue.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCase.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCase.java index 7918dccb4e..db1f36a2f9 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCase.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCase.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanFunctionCase implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_function_case.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_function_case.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_function_case.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCaseResult.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCaseResult.java index 41a4bb7c2c..2a17a3a351 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCaseResult.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanFunctionCaseResult.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanFunctionCaseResult implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_function_case_result.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_function_case_result.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_function_case_result.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanLoadCase.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanLoadCase.java index 92d6d5118e..75ed3a9e3a 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanLoadCase.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanLoadCase.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanLoadCase implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_load_case.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_load_case.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_load_case.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiCaseInfo.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiCaseInfo.java index 566363b2c5..b5511303c7 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiCaseInfo.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiCaseInfo.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanRecordApiCaseInfo implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_record_api_case_info.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_record_api_case_info.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_api_case_info.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiScenarioInfo.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiScenarioInfo.java index c5f73c73d9..e73ae137c0 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiScenarioInfo.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordApiScenarioInfo.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanRecordApiScenarioInfo implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_record_api_scenario_info.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_record_api_scenario_info.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_api_scenario_info.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordLoadCaseInfo.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordLoadCaseInfo.java index 69875fd1da..6f3811beae 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordLoadCaseInfo.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordLoadCaseInfo.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanRecordLoadCaseInfo implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_record_load_case_info.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_record_load_case_info.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_load_case_info.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordUiScenarioInfo.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordUiScenarioInfo.java index 60bb21c40c..91d518bfef 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordUiScenarioInfo.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanRecordUiScenarioInfo.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanRecordUiScenarioInfo implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_record_ui_scenario_info.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_record_ui_scenario_info.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_record_ui_scenario_info.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java index 1e4ba8b37d..9769b4725f 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReport.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanReport implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_report.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_report.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_report.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportContent.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportContent.java index 66a942e094..7056dfdbc7 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportContent.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanReportContent.java @@ -5,13 +5,14 @@ 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 java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanReportContent implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_report_content.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_report_content.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_report_content.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanUiScenario.java b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanUiScenario.java index 8e8860f1c5..ef7afc256d 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanUiScenario.java +++ b/backend/framework/domain/src/main/java/io/metersphere/plan/domain/TestPlanUiScenario.java @@ -6,13 +6,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class TestPlanUiScenario implements Serializable { @Schema(title = "ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan_ui_scenario.id.not_blank}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{test_plan_ui_scenario.id.not_blank}", groups = {Updated.class}) @Size(min = 1, max = 50, message = "{test_plan_ui_scenario.id.length_range}", groups = {Created.class, Updated.class}) private String id; diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_3__plan_ddl.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_3__plan_ddl.sql index f39829b162..c7aef4eed4 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_3__plan_ddl.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/ddl/V3.0.0_3__plan_ddl.sql @@ -41,12 +41,105 @@ CREATE TABLE IF NOT EXISTS test_plan_principal( CREATE TABLE IF NOT EXISTS test_plan_config( `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , - `run_mode_config` TEXT NOT NULL COMMENT '运行模式' , + `run_mode_config` TEXT COMMENT '运行模式' , `automatic_status_update` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否自定更新功能用例状态' , `repeat_case` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否允许重复添加用例' , `pass_threshold` INT(3) NOT NULL DEFAULT 100 COMMENT '测试计划通过阈值;0-100' , PRIMARY KEY (test_plan_id) ) COMMENT = '测试计划配置'; +DROP TABLE IF EXISTS test_plan_api_case; +CREATE TABLE test_plan_api_case( + `id` VARCHAR(50) NOT NULL COMMENT 'ID' , + `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , + `api_case_id` VARCHAR(50) NOT NULL COMMENT '接口用例ID' , + `environment_type` VARCHAR(20) COMMENT '环境类型' , + `environment` LONGTEXT COMMENT '所属环境' , + `environment_group_id` VARCHAR(50) COMMENT '环境组ID' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(40) NOT NULL COMMENT '创建人' , + `pos` BIGINT NOT NULL COMMENT '自定义排序,间隔5000' , + PRIMARY KEY (id) +) COMMENT = '测试计划关联接口用例'; + + +CREATE INDEX idx_api_case_id ON test_plan_api_case(api_case_id); +CREATE INDEX idx_test_plan_id ON test_plan_api_case(test_plan_id); +CREATE INDEX idx_create_user ON test_plan_api_case(create_user); + +DROP TABLE IF EXISTS test_plan_api_scenario; +CREATE TABLE test_plan_api_scenario( + `id` VARCHAR(50) NOT NULL COMMENT 'ID' , + `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , + `api_scenario_id` VARCHAR(255) COMMENT '场景ID' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(100) NOT NULL COMMENT '创建人' , + `pos` BIGINT NOT NULL COMMENT '自定义排序,间隔5000' , + `environment_type` VARCHAR(20) COMMENT '环境类型' , + `environment` LONGTEXT COMMENT '所属环境' , + `environment_group_id` VARCHAR(50) COMMENT '环境组ID' , + PRIMARY KEY (id) +) COMMENT = '测试计划关联场景用例'; + + +CREATE INDEX idx_api_scenario_id ON test_plan_api_scenario(api_scenario_id); +CREATE INDEX idx_test_plan_id ON test_plan_api_scenario(test_plan_id); +CREATE INDEX idx_create_user ON test_plan_api_scenario(create_user); + +DROP TABLE IF EXISTS test_plan_load_case; +CREATE TABLE test_plan_load_case( + `id` VARCHAR(50) NOT NULL COMMENT 'ID' , + `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , + `load_case_id` VARCHAR(50) NOT NULL COMMENT '性能用例ID' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , + `test_resource_pool_id` VARCHAR(50) COMMENT '所用测试资源池ID' , + `pos` BIGINT NOT NULL COMMENT '自定义排序,间隔5000' , + `load_configuration` LONGTEXT COMMENT '压力配置' , + `advanced_configuration` TEXT COMMENT '高级配置' , + PRIMARY KEY (id) +) COMMENT = '测试计划关联性能测试用例'; + + +CREATE INDEX idx_load_case_id ON test_plan_load_case(load_case_id); +CREATE INDEX idx_test_plan_id ON test_plan_load_case(test_plan_id); +CREATE INDEX idx_create_user ON test_plan_load_case(create_user); + +DROP TABLE IF EXISTS test_plan_function_case; +CREATE TABLE test_plan_function_case( + `id` VARCHAR(50) NOT NULL COMMENT 'ID' , + `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , + `function_case_id` VARCHAR(50) NOT NULL COMMENT '功能用例ID' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , + `pos` BIGINT NOT NULL COMMENT '自定义排序,间隔5000' , + PRIMARY KEY (id) +) COMMENT = '测试计划关联功能用例'; + + +CREATE INDEX idx_function_case_id ON test_plan_function_case(function_case_id); +CREATE INDEX idx_test_plan_id ON test_plan_function_case(test_plan_id); +CREATE INDEX idx_create_user ON test_plan_function_case(create_user); + +DROP TABLE IF EXISTS test_plan_ui_scenario; +CREATE TABLE test_plan_ui_scenario( + `id` VARCHAR(50) NOT NULL COMMENT 'ID' , + `test_plan_id` VARCHAR(50) NOT NULL COMMENT '测试计划ID' , + `ui_scenario_id` VARCHAR(50) NOT NULL COMMENT 'UI场景ID' , + `create_user` VARCHAR(50) NOT NULL COMMENT '创建人' , + `create_time` BIGINT NOT NULL COMMENT '创建时间' , + `pos` BIGINT NOT NULL COMMENT '排序,默认值5000' , + `environment_type` VARCHAR(20) COMMENT '环境类型' , + `environment` LONGTEXT COMMENT '所属环境' , + `environment_group_id` VARCHAR(50) COMMENT '环境组ID' , + PRIMARY KEY (id) +) COMMENT = '测试计划关联UI场景'; + + +CREATE INDEX idx_ui_scenario_id ON test_plan_ui_scenario(ui_scenario_id); +CREATE INDEX idx_test_plan_id ON test_plan_ui_scenario(test_plan_id); +CREATE INDEX idx_create_user ON test_plan_ui_scenario(create_user); + + -- set innodb lock wait timeout to default SET SESSION innodb_lock_wait_timeout = DEFAULT; \ No newline at end of file diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java index a0aa0572af..3fe5d93581 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/controller/handler/RestControllerExceptionHandler.java @@ -55,7 +55,7 @@ public class RestControllerExceptionHandler { IResultCode errorCode = e.getErrorCode(); if (errorCode == null) { // 如果抛出异常没有设置状态码,则返回错误 message - return ResponseEntity.ok() + return ResponseEntity.internalServerError() .body(new ResultHolder(MsHttpResultCode.FAILED.getCode(), MsHttpResultCode.FAILED.getMessage(), e.getMessage())); } @@ -71,6 +71,13 @@ public class RestControllerExceptionHandler { } } + @ExceptionHandler({Exception.class}) + public ResponseEntity handlerMSException(Exception e) { + return ResponseEntity.internalServerError() + .body(new ResultHolder(MsHttpResultCode.FAILED.getCode(), + MsHttpResultCode.FAILED.getMessage(), e.getMessage())); + } + /*=========== Shiro 异常拦截==============*/ @ExceptionHandler(ShiroException.class) public ResultHolder exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java index b91bb5113c..fa8eab22bd 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java @@ -2,13 +2,15 @@ package io.metersphere.plan.controller; import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.plan.service.TestPlanService; +import io.metersphere.sdk.exception.MSException; import io.metersphere.validation.groups.Created; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotBlank; +import org.apache.commons.collections4.CollectionUtils; 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; @RestController @RequestMapping("/test-plan") @@ -20,4 +22,17 @@ public class TestPlanController { public TestPlanDTO addUser(@Validated({Created.class}) @RequestBody TestPlanDTO testPlan) { return testPlanService.add(testPlan); } + + @PostMapping("/delete/batch") + public void deleteBatch(@RequestBody List idList) { + if (CollectionUtils.isEmpty(idList)) { + MSException.throwException("The ids cannot be empty!"); + } + testPlanService.batchDelete(idList); + } + + @GetMapping("/delete/{id}") + public void delete(@NotBlank @PathVariable String id) { + testPlanService.delete(id); + } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanDTO.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanDTO.java index 5effad11cd..dd9e466c9a 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanDTO.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/TestPlanDTO.java @@ -3,9 +3,11 @@ package io.metersphere.plan.dto; import io.metersphere.plan.domain.TestPlan; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.List; +@EqualsAndHashCode(callSuper = true) @Data public class TestPlanDTO extends TestPlan { @Schema(title = "测试计划责任人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @@ -13,4 +15,13 @@ public class TestPlanDTO extends TestPlan { @Schema(title = "测试计划关注人") private List followers; + + @Schema(title = "是否自定更新功能用例状态", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean automaticStatusUpdate; + + @Schema(title = "是否允许重复添加用例", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean repeatCase; + + @Schema(title = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED) + private int passThreshold = 100; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java new file mode 100644 index 0000000000..f8156ea485 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java @@ -0,0 +1,11 @@ +package io.metersphere.plan.mapper; + +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ExtTestPlanMapper { + List selectByParentId(String parentId); + + List selectByParentIdList(@Param("list") List parentTestPlanId); +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.xml new file mode 100644 index 0000000000..2e25d30d1d --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java index 7557ac735f..00f3ef7241 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java @@ -9,6 +9,8 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Transactional(rollbackFor = Exception.class) public class TestPlanApiCaseService { @@ -45,4 +47,16 @@ public class TestPlanApiCaseService { BeanUtils.copyBean(testPlanApiCaseDTO, testPlanApiCase); return testPlanApiCaseDTO; } + + public int deleteByTestPlanId(String testPlanId) { + TestPlanApiCaseExample example = new TestPlanApiCaseExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + return testPlanApiCaseMapper.deleteByExample(example); + } + + public int deleteBatchByTestPlanId(List testPlanIdList) { + TestPlanApiCaseExample example = new TestPlanApiCaseExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + return testPlanApiCaseMapper.deleteByExample(example); + } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java new file mode 100644 index 0000000000..bc88237fc6 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java @@ -0,0 +1,28 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlanApiScenarioExample; +import io.metersphere.plan.mapper.TestPlanApiScenarioMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanApiScenarioService { + @Resource + private TestPlanApiScenarioMapper testPlanApiScenarioMapper; + + public int deleteByTestPlanId(String testPlanId) { + TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + return testPlanApiScenarioMapper.deleteByExample(example); + } + + public int deleteBatchByTestPlanId(List testPlanIdList) { + TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + return testPlanApiScenarioMapper.deleteByExample(example); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanConfigService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanConfigService.java new file mode 100644 index 0000000000..a4158991a3 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanConfigService.java @@ -0,0 +1,28 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlanConfigExample; +import io.metersphere.plan.mapper.TestPlanConfigMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanConfigService { + @Resource + private TestPlanConfigMapper testPlanConfigMapper; + + public void delete(String testPlanId) { + TestPlanConfigExample example = new TestPlanConfigExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + testPlanConfigMapper.deleteByExample(example); + } + + public void deleteBatch(List testPlanIdList) { + TestPlanConfigExample example = new TestPlanConfigExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + testPlanConfigMapper.deleteByExample(example); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFollowerService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFollowerService.java index 28968d7e88..1ea71477f9 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFollowerService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFollowerService.java @@ -3,6 +3,10 @@ package io.metersphere.plan.service; import io.metersphere.plan.domain.TestPlanFollower; import io.metersphere.plan.mapper.TestPlanFollowerMapper; import jakarta.annotation.Resource; +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 org.springframework.transaction.annotation.Transactional; @@ -13,11 +17,23 @@ import java.util.List; public class TestPlanFollowerService { @Resource - TestPlanFollowerMapper testPlanFollowerMapper; + private SqlSessionFactory sqlSessionFactory; public void batchSave(List testPlanFollowerList) { - for (TestPlanFollower testPlanFollower : testPlanFollowerList) { - testPlanFollowerMapper.insert(testPlanFollower); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanFollowerMapper testPlanFollowerMapper = sqlSession.getMapper(TestPlanFollowerMapper.class); + try { + int insertIndex = 0; + for (TestPlanFollower testPlanFollower : testPlanFollowerList) { + testPlanFollowerMapper.insert(testPlanFollower); + insertIndex++; + if (insertIndex % 50 == 0) { + sqlSession.flushStatements(); + } + } + sqlSession.flushStatements(); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionCaseService.java new file mode 100644 index 0000000000..8bff43ed2c --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionCaseService.java @@ -0,0 +1,28 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlanFunctionCaseExample; +import io.metersphere.plan.mapper.TestPlanFunctionCaseMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanFunctionCaseService { + @Resource + private TestPlanFunctionCaseMapper testPlanFunctionCaseMapper; + + public int deleteByTestPlanId(String testPlanId) { + TestPlanFunctionCaseExample example = new TestPlanFunctionCaseExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + return testPlanFunctionCaseMapper.deleteByExample(example); + } + + public int deleteBatchByTestPlanId(List testPlanIdList) { + TestPlanFunctionCaseExample example = new TestPlanFunctionCaseExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + return testPlanFunctionCaseMapper.deleteByExample(example); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java new file mode 100644 index 0000000000..c03fd878c1 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java @@ -0,0 +1,28 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlanLoadCaseExample; +import io.metersphere.plan.mapper.TestPlanLoadCaseMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanLoadCaseService { + @Resource + private TestPlanLoadCaseMapper testPlanLoadCaseMapper; + + public void deleteByTestPlanId(String testPlanId) { + TestPlanLoadCaseExample example = new TestPlanLoadCaseExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + testPlanLoadCaseMapper.deleteByExample(example); + } + + public void deleteBatchByTestPlanId(List testPlanIdList) { + TestPlanLoadCaseExample example = new TestPlanLoadCaseExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + testPlanLoadCaseMapper.deleteByExample(example); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanPrincipalService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanPrincipalService.java index d6e352bb47..c56f1d6fea 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanPrincipalService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanPrincipalService.java @@ -3,7 +3,10 @@ package io.metersphere.plan.service; import io.metersphere.plan.domain.TestPlanPrincipal; import io.metersphere.plan.mapper.TestPlanPrincipalMapper; import jakarta.annotation.Resource; -import jakarta.validation.constraints.NotEmpty; +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 org.springframework.transaction.annotation.Transactional; @@ -14,11 +17,23 @@ import java.util.List; public class TestPlanPrincipalService { @Resource - TestPlanPrincipalMapper testPlanPrincipalMapper; + private SqlSessionFactory sqlSessionFactory; - public void batchSave(@NotEmpty List testPlanPrincipalList) { - for (TestPlanPrincipal testPlanPrincipal : testPlanPrincipalList) { - testPlanPrincipalMapper.insert(testPlanPrincipal); + public void batchSave(List testPlanPrincipalList) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanPrincipalMapper testPlanPrincipalMapper = sqlSession.getMapper(TestPlanPrincipalMapper.class); + try { + int insertIndex = 0; + for (TestPlanPrincipal testPlanPrincipal : testPlanPrincipalList) { + testPlanPrincipalMapper.insert(testPlanPrincipal); + insertIndex++; + if (insertIndex % 50 == 0) { + sqlSession.flushStatements(); + } + } + sqlSession.flushStatements(); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java index a662c0cc0a..74ca5d012c 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java @@ -1,15 +1,21 @@ package io.metersphere.plan.service; -import io.metersphere.plan.domain.TestPlan; -import io.metersphere.plan.domain.TestPlanFollower; -import io.metersphere.plan.domain.TestPlanPrincipal; +import io.metersphere.plan.domain.*; import io.metersphere.plan.dto.TestPlanDTO; +import io.metersphere.plan.mapper.ExtTestPlanMapper; +import io.metersphere.plan.mapper.TestPlanConfigMapper; import io.metersphere.plan.mapper.TestPlanMapper; +import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.SessionUtils; +import io.metersphere.system.domain.User; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotBlank; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotNull; import java.util.ArrayList; @@ -21,23 +27,59 @@ import java.util.UUID; public class TestPlanService { @Resource private TestPlanMapper testPlanMapper; + @Resource + private ExtTestPlanMapper extTestPlanMapper; + @Resource + private TestPlanConfigMapper testPlanConfigMapper; + @Resource + private TestPlanConfigService testPlanConfigService; @Resource private TestPlanPrincipalService testPlanPrincipalService; @Resource private TestPlanFollowerService testPlanFollowerService; + @Resource + private TestPlanApiCaseService testPlanApiCaseService; + @Resource + private TestPlanApiScenarioService testPlanApiScenarioService; + @Resource + private TestPlanLoadCaseService testPlanLoadCaseService; + @Resource + private TestPlanUiScenarioService testPlanUiScenarioService; + @Resource + private TestPlanFunctionCaseService testPlanFunctionCaseService; + + public TestPlanDTO add(@NotNull TestPlanDTO testPlanCreateRequest) { + User user = SessionUtils.getUser(); + if (user == null) { + MSException.throwException("Cannot find user!"); + } + if (StringUtils.equals(testPlanCreateRequest.getParentId(), testPlanCreateRequest.getId())) { + MSException.throwException("The parent test plan cannot be the same as the current test plan!"); + } + + if (StringUtils.isBlank(testPlanCreateRequest.getId())) { + testPlanCreateRequest.setId(UUID.randomUUID().toString()); + } + testPlanCreateRequest.setCreateUser(user.getId()); + testPlanCreateRequest.setUpdateUser(user.getId()); + testPlanCreateRequest.setCreateTime(System.currentTimeMillis()); + testPlanCreateRequest.setUpdateTime(System.currentTimeMillis()); - public TestPlanDTO add(@NotNull TestPlanDTO testPlanDTO) { TestPlan testPlan = new TestPlan(); - BeanUtils.copyBean(testPlan, testPlanDTO); - testPlan.setId(UUID.randomUUID().toString()); - //todo SongTianyang:暂时没有SessionUtil,创建人先根据前台传值保存 - testPlan.setCreateTime(System.currentTimeMillis()); + BeanUtils.copyBean(testPlan, testPlanCreateRequest); testPlanMapper.insert(testPlan); - if (CollectionUtils.isNotEmpty(testPlanDTO.getFollowers())) { + TestPlanConfig testPlanConfig = new TestPlanConfig(); + testPlanConfig.setTestPlanId(testPlan.getId()); + testPlanConfig.setAutomaticStatusUpdate(testPlanCreateRequest.isAutomaticStatusUpdate()); + testPlanConfig.setRepeatCase(testPlanCreateRequest.isRepeatCase()); + testPlanConfig.setPassThreshold(testPlanCreateRequest.getPassThreshold()); + testPlanConfigMapper.insert(testPlanConfig); + + if (CollectionUtils.isNotEmpty(testPlanCreateRequest.getFollowers())) { List testPlanFollowerList = new ArrayList<>(); - for (String follower : testPlanDTO.getFollowers()) { + for (String follower : testPlanCreateRequest.getFollowers()) { TestPlanFollower testPlanFollower = new TestPlanFollower(); testPlanFollower.setTestPlanId(testPlan.getId()); testPlanFollower.setUserId(follower); @@ -46,9 +88,9 @@ public class TestPlanService { testPlanFollowerService.batchSave(testPlanFollowerList); } - if (CollectionUtils.isNotEmpty(testPlanDTO.getPrincipals())) { + if (CollectionUtils.isNotEmpty(testPlanCreateRequest.getPrincipals())) { List testPlanPrincipalList = new ArrayList<>(); - for (String principal : testPlanDTO.getPrincipals()) { + for (String principal : testPlanCreateRequest.getPrincipals()) { TestPlanPrincipal testPlanPrincipal = new TestPlanPrincipal(); testPlanPrincipal.setTestPlanId(testPlan.getId()); testPlanPrincipal.setUserId(principal); @@ -56,6 +98,60 @@ public class TestPlanService { } testPlanPrincipalService.batchSave(testPlanPrincipalList); } - return testPlanDTO; + return testPlanCreateRequest; + } + + public void batchDelete(List idList) { + TestPlanExample example = new TestPlanExample(); + example.createCriteria().andIdIn(idList); + testPlanMapper.deleteByExample(example); + + this.cascadeDelete(idList); + } + + public void delete(@NotBlank String id) { + TestPlanExample example = new TestPlanExample(); + example.createCriteria().andIdEqualTo(id); + testPlanMapper.deleteByExample(example); + this.cascadeDelete(id); + } + + public void deleteByParentId(String parentTestPlanId) { + List childrenTestPlanIdList = extTestPlanMapper.selectByParentId(parentTestPlanId); + if (CollectionUtils.isNotEmpty(childrenTestPlanIdList)) { + this.batchDelete(childrenTestPlanIdList); + } + } + + @Validated + public void deleteBatchByParentId(List parentTestPlanId) { + List childrenTestPlanIdList = extTestPlanMapper.selectByParentIdList(parentTestPlanId); + if (CollectionUtils.isNotEmpty(childrenTestPlanIdList)) { + this.batchDelete(childrenTestPlanIdList); + } + } + + private void cascadeDelete(String id) { + //删除子计划 + this.deleteByParentId(id); + //删除当前计划对应的资源 + this.testPlanConfigService.delete(id); + this.testPlanFunctionCaseService.deleteByTestPlanId(id); + this.testPlanApiCaseService.deleteByTestPlanId(id); + this.testPlanApiScenarioService.deleteByTestPlanId(id); + this.testPlanUiScenarioService.deleteByTestPlanId(id); + this.testPlanLoadCaseService.deleteByTestPlanId(id); + } + + private void cascadeDelete(List idList) { + //删除子计划 + this.deleteBatchByParentId(idList); + //删除当前计划对应的资源 + this.testPlanConfigService.deleteBatch(idList); + this.testPlanFunctionCaseService.deleteBatchByTestPlanId(idList); + this.testPlanApiCaseService.deleteBatchByTestPlanId(idList); + this.testPlanApiScenarioService.deleteBatchByTestPlanId(idList); + this.testPlanUiScenarioService.deleteBatchByTestPlanId(idList); + this.testPlanLoadCaseService.deleteBatchByTestPlanId(idList); } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanUiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanUiScenarioService.java new file mode 100644 index 0000000000..4624bdec11 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanUiScenarioService.java @@ -0,0 +1,28 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlanUiScenarioExample; +import io.metersphere.plan.mapper.TestPlanUiScenarioMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanUiScenarioService { + @Resource + private TestPlanUiScenarioMapper TestPlanUiScenarioMapper; + + public int deleteByTestPlanId(String testPlanId) { + TestPlanUiScenarioExample example = new TestPlanUiScenarioExample(); + example.createCriteria().andTestPlanIdEqualTo(testPlanId); + return TestPlanUiScenarioMapper.deleteByExample(example); + } + + public int deleteBatchByTestPlanId(List testPlanIdList) { + TestPlanUiScenarioExample example = new TestPlanUiScenarioExample(); + example.createCriteria().andTestPlanIdIn(testPlanIdList); + return TestPlanUiScenarioMapper.deleteByExample(example); + } +} diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanControllerTests.java index 9ae98f2dec..5e3c3f6b6e 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanControllerTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanControllerTests.java @@ -4,21 +4,23 @@ import com.jayway.jsonpath.JsonPath; import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.dto.TestPlanDTO; import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.controller.handler.ResultHolder; import io.metersphere.sdk.util.JSON; +import io.metersphere.utils.JsonUtils; import jakarta.annotation.Resource; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.*; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -35,13 +37,15 @@ public class TestPlanControllerTests { private static String sessionId; private static String csrfToken; + static List SAVED_TEST_PLAN_DTO_LIST = new ArrayList<>(); + + static final String STATIC_UUID = UUID.randomUUID().toString(); private TestPlanDTO getSimpleTestPlan() { TestPlanDTO testPlan = new TestPlanDTO(); - testPlan.setId("test"); testPlan.setName("test"); - testPlan.setProjectId("1"); - testPlan.setParentId("1"); + testPlan.setProjectId("test-project-id"); + testPlan.setParentId("root"); testPlan.setCreateUser("JianGuo"); testPlan.setStage("Smock"); testPlan.setStatus("PREPARE"); @@ -49,23 +53,46 @@ public class TestPlanControllerTests { return testPlan; } + private void isPreDataOk() throws Exception { + if (SAVED_TEST_PLAN_DTO_LIST.isEmpty()) { + this.testAddSuccess(); + } + } - @Test - @Order(0) + private void addTestPlanToSavedList(MockHttpServletResponse mockResponse) throws Exception { + + String returnData = mockResponse.getContentAsString(); + ResultHolder resultHolder = JsonUtils.parseObject(returnData, ResultHolder.class); + + //返回请求正常 + Assertions.assertNotNull(resultHolder); + + TestPlanDTO testPlanDTO = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanDTO.class); + + //返回值不为空 + Assertions.assertNotNull(testPlanDTO); + + SAVED_TEST_PLAN_DTO_LIST.add(testPlanDTO); + } + + @BeforeEach public void login() throws Exception { - MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login") - .content("{\"username\":\"admin\",\"password\":\"metersphere\"}") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId"); - csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken"); + if (StringUtils.isAnyBlank(sessionId, csrfToken)) { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login") + .content("{\"username\":\"admin\",\"password\":\"metersphere\"}") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andReturn(); + sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId"); + csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken"); + } } @Test @Order(1) - public void testAdd1() throws Exception { + public void testAddSuccess() throws Exception { + //测试有责任人、关注人 TestPlanDTO testPlan = this.getSimpleTestPlan(); List followerList = new ArrayList<>(); @@ -80,72 +107,139 @@ public class TestPlanControllerTests { participantList.add("SongGuoyu"); testPlan.setPrincipals(participantList); - mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") .header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) .content(JSON.toJSONString(testPlan)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); - } + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); - @Test - @Order(2) - public void testAdd2() throws Exception { - TestPlanDTO testPlan = this.getSimpleTestPlan(); + //测试自动赋予了UUID + testPlan = this.getSimpleTestPlan(); + testPlan.setId(STATIC_UUID); + mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); - List followerList = new ArrayList<>(); + //测试创建子计划 + testPlan = this.getSimpleTestPlan(); + followerList = new ArrayList<>(); followerList.add("JianGuo"); followerList.add("SongGuoyu"); followerList.add("SongYingyu"); followerList.add("SongFanti"); testPlan.setFollowers(followerList); - mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + participantList = new ArrayList<>(); + participantList.add("JianGuo"); + participantList.add("SongGuoyu"); + testPlan.setPrincipals(participantList); + testPlan.setParentId(SAVED_TEST_PLAN_DTO_LIST.get(0).getId()); + mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") .header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) .content(JSON.toJSONString(testPlan)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); + + //测试只有关注人 + testPlan = this.getSimpleTestPlan(); + followerList = new ArrayList<>(); + followerList.add("JianGuo"); + followerList.add("SongGuoyu"); + followerList.add("SongYingyu"); + followerList.add("SongFanti"); + testPlan.setFollowers(followerList); + mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); + + //测试只有责任人 + testPlan = this.getSimpleTestPlan(); + participantList = new ArrayList<>(); + participantList.add("JianGuo"); + participantList.add("SongGuoyu"); + testPlan.setPrincipals(participantList); + mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); + + //测试没有责任人没有关注人 + testPlan = this.getSimpleTestPlan(); + mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + this.addTestPlanToSavedList(mvcResult.getResponse()); } @Test @Order(2) - public void testAdd3() throws Exception { - TestPlanDTO testPlan = this.getSimpleTestPlan(); + public void testDeleteSuccess() throws Exception { + this.isPreDataOk(); - List participantList = new ArrayList<>(); - participantList.add("JianGuo"); - participantList.add("SongGuoyu"); - testPlan.setPrincipals(participantList); - - mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + String testPlanId = SAVED_TEST_PLAN_DTO_LIST.get(SAVED_TEST_PLAN_DTO_LIST.size() - 1).getId(); + mockMvc.perform(MockMvcRequestBuilders.get("/test-plan/delete/" + testPlanId) .header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) - .content(JSON.toJSONString(testPlan)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); } @Test - @Order(4) - public void testAdd4() throws Exception { - TestPlanDTO testPlan = this.getSimpleTestPlan(); + @Order(3) + public void testDeleteBatchSuccess() throws Exception { + this.isPreDataOk(); - mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + List testPlanIdList = new ArrayList<>(); + testPlanIdList.add(SAVED_TEST_PLAN_DTO_LIST.get(0).getId()); + mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/delete/batch") .header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.CSRF_TOKEN, csrfToken) - .content(JSON.toJSONString(testPlan)) + .content(JSON.toJSONString(testPlanIdList)) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(print()); + .andExpect(status().isOk()); } @Test - @Order(5) - public void testAddUserFalse() throws Exception { + public void testBatchDelete_Error_System() throws Exception { + //没有必填项 + List testPlanIdList = new ArrayList<>(); + mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/delete/batch") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlanIdList)) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect(status().is5xxServerError()); + } + + //添加测试计划反例校验:参数不合法 + @Test + public void testAdd_Error_Param() throws Exception { + //没有必填项 TestPlan testPlan = new TestPlan(); testPlan.setName("test"); @@ -156,5 +250,32 @@ public class TestPlanControllerTests { .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + + } + + //添加测试计划反例校验:系统错误(例如ID重复、parentId不合法) + @Test + public void testAdd_Error_System() throws Exception { + this.isPreDataOk(); + //测试重复存储UUID不成功 + TestPlanDTO testPlan = this.getSimpleTestPlan(); + testPlan.setId(STATIC_UUID); + mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()).andDo(print()); + + //测试parentId和id相同 + testPlan = this.getSimpleTestPlan(); + testPlan.setId(UUID.randomUUID().toString()); + testPlan.setParentId(testPlan.getId()); + mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add") + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .content(JSON.toJSONString(testPlan)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is5xxServerError()); } }