diff --git a/backend/pom.xml b/backend/pom.xml index 8cfbd29b81..34721c92c4 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -162,9 +162,15 @@ - org.quartz-scheduler - quartz - 2.3.0 + com.fit2cloud + quartz-spring-boot-starter + + + org.springframework.boot + spring-boot-autoconfigure + + + 0.0.4 @@ -288,5 +294,43 @@ + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone + + false + + + + jcenter-snapshots + jcenter + https://jcenter.bintray.com/ + + + fit2cloud-enterprise-release + Fit2Cloud Enterprise Release + http://repository.fit2cloud.com/content/repositories/fit2cloud-enterprise-release/ + + true + + + true + + + + fit2cloud + fit2cloud + http://repository.fit2cloud.com/content/groups/public/ + + true + + + true + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/Application.java b/backend/src/main/java/io/metersphere/Application.java index 730b148045..33f41bce49 100644 --- a/backend/src/main/java/io/metersphere/Application.java +++ b/backend/src/main/java/io/metersphere/Application.java @@ -8,6 +8,7 @@ import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.annotation.PropertySource; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(exclude = {QuartzAutoConfiguration.class}) @ServletComponentScan @@ -15,6 +16,7 @@ import org.springframework.context.annotation.PropertySource; KafkaProperties.class, JmeterProperties.class }) +@EnableScheduling @PropertySource(value = {"file:/opt/metersphere/conf/metersphere.properties"}, encoding = "UTF-8", ignoreResourceNotFound = true) public class Application { public static void main(String[] args) { diff --git a/backend/src/main/java/io/metersphere/api/dto/SaveAPITestRequest.java b/backend/src/main/java/io/metersphere/api/dto/SaveAPITestRequest.java index 8531011633..6785fcf166 100644 --- a/backend/src/main/java/io/metersphere/api/dto/SaveAPITestRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/SaveAPITestRequest.java @@ -22,4 +22,6 @@ public class SaveAPITestRequest { private String userId; private Schedule schedule; + + private String triggerMode; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractRegex.java b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractRegex.java index 3a2216fa27..d5d6160bf5 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractRegex.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractRegex.java @@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data public class ExtractRegex extends ExtractCommon { + private String useHeaders; public ExtractRegex() { setType(ExtractType.REGEX); } diff --git a/backend/src/main/java/io/metersphere/api/service/APIReportService.java b/backend/src/main/java/io/metersphere/api/service/APIReportService.java index 42de9fbc56..34143135a5 100644 --- a/backend/src/main/java/io/metersphere/api/service/APIReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/APIReportService.java @@ -94,7 +94,7 @@ public class APIReportService { apiTestReportMapper.updateByPrimaryKeySelective(report); } - public String create(ApiTest test) { + public String create(ApiTest test, String triggerMode) { ApiTestReport running = getRunningReport(test.getId()); if (running != null) { return running.getId(); @@ -104,6 +104,7 @@ public class APIReportService { report.setId(UUID.randomUUID().toString()); report.setTestId(test.getId()); report.setName(test.getName()); + report.setTriggerMode(triggerMode); report.setDescription(test.getDescription()); report.setCreateTime(System.currentTimeMillis()); report.setUpdateTime(System.currentTimeMillis()); diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index 69e96738f6..f9acef1cd7 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -14,35 +14,24 @@ import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.constants.ScheduleType; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.BeanUtils; -import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; -import io.metersphere.job.QuartzManager; import io.metersphere.job.sechedule.ApiTestJob; -import io.metersphere.job.sechedule.PerformanceTestJob; import io.metersphere.service.FileService; import io.metersphere.service.ScheduleService; -import org.apache.commons.lang3.StringUtils; -import org.quartz.JobDataMap; -import org.quartz.JobKey; -import org.quartz.SchedulerException; -import org.quartz.TriggerKey; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; - import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.UUID; -import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Collectors; @Service @@ -148,7 +137,7 @@ public class APITestService { if (SessionUtils.getUser() == null) { apiTest.setUserId(request.getUserId()); } - String reportId = apiReportService.create(apiTest); + String reportId = apiReportService.create(apiTest, request.getTriggerMode()); changeStatus(request.getId(), APITestStatus.Running); jMeterService.run(request.getId(), is); diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCase.java b/backend/src/main/java/io/metersphere/base/domain/TestCase.java index 2585f68787..0fbe689c3d 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCase.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCase.java @@ -31,5 +31,7 @@ public class TestCase implements Serializable { private String testId; + private Integer sort; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java b/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java index 1196b8477b..6576f8f575 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java @@ -385,142 +385,142 @@ public class TestCaseExample { } public Criteria andNameIsNull() { - addCriterion("name is null"); + addCriterion("`name` is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { - addCriterion("name is not null"); + addCriterion("`name` is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { - addCriterion("name =", value, "name"); + addCriterion("`name` =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { - addCriterion("name <>", value, "name"); + addCriterion("`name` <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { - addCriterion("name >", value, "name"); + addCriterion("`name` >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { - addCriterion("name >=", value, "name"); + addCriterion("`name` >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { - addCriterion("name <", value, "name"); + addCriterion("`name` <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { - addCriterion("name <=", value, "name"); + addCriterion("`name` <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { - addCriterion("name like", value, "name"); + addCriterion("`name` like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { - addCriterion("name not like", value, "name"); + addCriterion("`name` not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { - addCriterion("name in", values, "name"); + addCriterion("`name` in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { - addCriterion("name not in", values, "name"); + addCriterion("`name` not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { - addCriterion("name between", value1, value2, "name"); + addCriterion("`name` between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { - addCriterion("name not between", value1, value2, "name"); + addCriterion("`name` not between", value1, value2, "name"); return (Criteria) this; } public Criteria andTypeIsNull() { - addCriterion("type is null"); + addCriterion("`type` is null"); return (Criteria) this; } public Criteria andTypeIsNotNull() { - addCriterion("type is not null"); + addCriterion("`type` is not null"); return (Criteria) this; } public Criteria andTypeEqualTo(String value) { - addCriterion("type =", value, "type"); + addCriterion("`type` =", value, "type"); return (Criteria) this; } public Criteria andTypeNotEqualTo(String value) { - addCriterion("type <>", value, "type"); + addCriterion("`type` <>", value, "type"); return (Criteria) this; } public Criteria andTypeGreaterThan(String value) { - addCriterion("type >", value, "type"); + addCriterion("`type` >", value, "type"); return (Criteria) this; } public Criteria andTypeGreaterThanOrEqualTo(String value) { - addCriterion("type >=", value, "type"); + addCriterion("`type` >=", value, "type"); return (Criteria) this; } public Criteria andTypeLessThan(String value) { - addCriterion("type <", value, "type"); + addCriterion("`type` <", value, "type"); return (Criteria) this; } public Criteria andTypeLessThanOrEqualTo(String value) { - addCriterion("type <=", value, "type"); + addCriterion("`type` <=", value, "type"); return (Criteria) this; } public Criteria andTypeLike(String value) { - addCriterion("type like", value, "type"); + addCriterion("`type` like", value, "type"); return (Criteria) this; } public Criteria andTypeNotLike(String value) { - addCriterion("type not like", value, "type"); + addCriterion("`type` not like", value, "type"); return (Criteria) this; } public Criteria andTypeIn(List values) { - addCriterion("type in", values, "type"); + addCriterion("`type` in", values, "type"); return (Criteria) this; } public Criteria andTypeNotIn(List values) { - addCriterion("type not in", values, "type"); + addCriterion("`type` not in", values, "type"); return (Criteria) this; } public Criteria andTypeBetween(String value1, String value2) { - addCriterion("type between", value1, value2, "type"); + addCriterion("`type` between", value1, value2, "type"); return (Criteria) this; } public Criteria andTypeNotBetween(String value1, String value2) { - addCriterion("type not between", value1, value2, "type"); + addCriterion("`type` not between", value1, value2, "type"); return (Criteria) this; } @@ -665,72 +665,72 @@ public class TestCaseExample { } public Criteria andMethodIsNull() { - addCriterion("method is null"); + addCriterion("`method` is null"); return (Criteria) this; } public Criteria andMethodIsNotNull() { - addCriterion("method is not null"); + addCriterion("`method` is not null"); return (Criteria) this; } public Criteria andMethodEqualTo(String value) { - addCriterion("method =", value, "method"); + addCriterion("`method` =", value, "method"); return (Criteria) this; } public Criteria andMethodNotEqualTo(String value) { - addCriterion("method <>", value, "method"); + addCriterion("`method` <>", value, "method"); return (Criteria) this; } public Criteria andMethodGreaterThan(String value) { - addCriterion("method >", value, "method"); + addCriterion("`method` >", value, "method"); return (Criteria) this; } public Criteria andMethodGreaterThanOrEqualTo(String value) { - addCriterion("method >=", value, "method"); + addCriterion("`method` >=", value, "method"); return (Criteria) this; } public Criteria andMethodLessThan(String value) { - addCriterion("method <", value, "method"); + addCriterion("`method` <", value, "method"); return (Criteria) this; } public Criteria andMethodLessThanOrEqualTo(String value) { - addCriterion("method <=", value, "method"); + addCriterion("`method` <=", value, "method"); return (Criteria) this; } public Criteria andMethodLike(String value) { - addCriterion("method like", value, "method"); + addCriterion("`method` like", value, "method"); return (Criteria) this; } public Criteria andMethodNotLike(String value) { - addCriterion("method not like", value, "method"); + addCriterion("`method` not like", value, "method"); return (Criteria) this; } public Criteria andMethodIn(List values) { - addCriterion("method in", values, "method"); + addCriterion("`method` in", values, "method"); return (Criteria) this; } public Criteria andMethodNotIn(List values) { - addCriterion("method not in", values, "method"); + addCriterion("`method` not in", values, "method"); return (Criteria) this; } public Criteria andMethodBetween(String value1, String value2) { - addCriterion("method between", value1, value2, "method"); + addCriterion("`method` between", value1, value2, "method"); return (Criteria) this; } public Criteria andMethodNotBetween(String value1, String value2) { - addCriterion("method not between", value1, value2, "method"); + addCriterion("`method` not between", value1, value2, "method"); return (Criteria) this; } @@ -993,6 +993,66 @@ public class TestCaseExample { addCriterion("test_id not between", value1, value2, "testId"); return (Criteria) this; } + + public Criteria andSortIsNull() { + addCriterion("sort is null"); + return (Criteria) this; + } + + public Criteria andSortIsNotNull() { + addCriterion("sort is not null"); + return (Criteria) this; + } + + public Criteria andSortEqualTo(Integer value) { + addCriterion("sort =", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortNotEqualTo(Integer value) { + addCriterion("sort <>", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortGreaterThan(Integer value) { + addCriterion("sort >", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortGreaterThanOrEqualTo(Integer value) { + addCriterion("sort >=", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortLessThan(Integer value) { + addCriterion("sort <", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortLessThanOrEqualTo(Integer value) { + addCriterion("sort <=", value, "sort"); + return (Criteria) this; + } + + public Criteria andSortIn(List values) { + addCriterion("sort in", values, "sort"); + return (Criteria) this; + } + + public Criteria andSortNotIn(List values) { + addCriterion("sort not in", values, "sort"); + return (Criteria) this; + } + + public Criteria andSortBetween(Integer value1, Integer value2) { + addCriterion("sort between", value1, value2, "sort"); + return (Criteria) this; + } + + public Criteria andSortNotBetween(Integer value1, Integer value2) { + addCriterion("sort not between", value1, value2, "sort"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestCaseMapper.xml index 6c2936ce2c..fae25c14d2 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/TestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/TestCaseMapper.xml @@ -15,6 +15,7 @@ + @@ -79,8 +80,8 @@ - id, node_id, node_path, project_id, name, type, maintainer, priority, method, prerequisite, - create_time, update_time, test_id + id, node_id, node_path, project_id, `name`, `type`, maintainer, priority, `method`, + prerequisite, create_time, update_time, test_id, sort remark, steps @@ -135,17 +136,17 @@ insert into test_case (id, node_id, node_path, - project_id, name, type, - maintainer, priority, method, + project_id, `name`, `type`, + maintainer, priority, `method`, prerequisite, create_time, update_time, - test_id, remark, steps - ) + test_id, sort, remark, + steps) values (#{id,jdbcType=VARCHAR}, #{nodeId,jdbcType=VARCHAR}, #{nodePath,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{maintainer,jdbcType=VARCHAR}, #{priority,jdbcType=VARCHAR}, #{method,jdbcType=VARCHAR}, #{prerequisite,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, - #{testId,jdbcType=VARCHAR}, #{remark,jdbcType=LONGVARCHAR}, #{steps,jdbcType=LONGVARCHAR} - ) + #{testId,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{remark,jdbcType=LONGVARCHAR}, + #{steps,jdbcType=LONGVARCHAR}) insert into test_case @@ -163,10 +164,10 @@ project_id, - name, + `name`, - type, + `type`, maintainer, @@ -175,7 +176,7 @@ priority, - method, + `method`, prerequisite, @@ -189,6 +190,9 @@ test_id, + + sort, + remark, @@ -236,6 +240,9 @@ #{testId,jdbcType=VARCHAR}, + + #{sort,jdbcType=INTEGER}, + #{remark,jdbcType=LONGVARCHAR}, @@ -266,10 +273,10 @@ project_id = #{record.projectId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, - type = #{record.type,jdbcType=VARCHAR}, + `type` = #{record.type,jdbcType=VARCHAR}, maintainer = #{record.maintainer,jdbcType=VARCHAR}, @@ -278,7 +285,7 @@ priority = #{record.priority,jdbcType=VARCHAR}, - method = #{record.method,jdbcType=VARCHAR}, + `method` = #{record.method,jdbcType=VARCHAR}, prerequisite = #{record.prerequisite,jdbcType=VARCHAR}, @@ -292,6 +299,9 @@ test_id = #{record.testId,jdbcType=VARCHAR}, + + sort = #{record.sort,jdbcType=INTEGER}, + remark = #{record.remark,jdbcType=LONGVARCHAR}, @@ -309,15 +319,16 @@ node_id = #{record.nodeId,jdbcType=VARCHAR}, node_path = #{record.nodePath,jdbcType=VARCHAR}, project_id = #{record.projectId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, - type = #{record.type,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, + `type` = #{record.type,jdbcType=VARCHAR}, maintainer = #{record.maintainer,jdbcType=VARCHAR}, priority = #{record.priority,jdbcType=VARCHAR}, - method = #{record.method,jdbcType=VARCHAR}, + `method` = #{record.method,jdbcType=VARCHAR}, prerequisite = #{record.prerequisite,jdbcType=VARCHAR}, create_time = #{record.createTime,jdbcType=BIGINT}, update_time = #{record.updateTime,jdbcType=BIGINT}, test_id = #{record.testId,jdbcType=VARCHAR}, + sort = #{record.sort,jdbcType=INTEGER}, remark = #{record.remark,jdbcType=LONGVARCHAR}, steps = #{record.steps,jdbcType=LONGVARCHAR} @@ -330,15 +341,16 @@ node_id = #{record.nodeId,jdbcType=VARCHAR}, node_path = #{record.nodePath,jdbcType=VARCHAR}, project_id = #{record.projectId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, - type = #{record.type,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, + `type` = #{record.type,jdbcType=VARCHAR}, maintainer = #{record.maintainer,jdbcType=VARCHAR}, priority = #{record.priority,jdbcType=VARCHAR}, - method = #{record.method,jdbcType=VARCHAR}, + `method` = #{record.method,jdbcType=VARCHAR}, prerequisite = #{record.prerequisite,jdbcType=VARCHAR}, create_time = #{record.createTime,jdbcType=BIGINT}, update_time = #{record.updateTime,jdbcType=BIGINT}, - test_id = #{record.testId,jdbcType=VARCHAR} + test_id = #{record.testId,jdbcType=VARCHAR}, + sort = #{record.sort,jdbcType=INTEGER} @@ -356,10 +368,10 @@ project_id = #{projectId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, - type = #{type,jdbcType=VARCHAR}, + `type` = #{type,jdbcType=VARCHAR}, maintainer = #{maintainer,jdbcType=VARCHAR}, @@ -368,7 +380,7 @@ priority = #{priority,jdbcType=VARCHAR}, - method = #{method,jdbcType=VARCHAR}, + `method` = #{method,jdbcType=VARCHAR}, prerequisite = #{prerequisite,jdbcType=VARCHAR}, @@ -382,6 +394,9 @@ test_id = #{testId,jdbcType=VARCHAR}, + + sort = #{sort,jdbcType=INTEGER}, + remark = #{remark,jdbcType=LONGVARCHAR}, @@ -396,15 +411,16 @@ set node_id = #{nodeId,jdbcType=VARCHAR}, node_path = #{nodePath,jdbcType=VARCHAR}, project_id = #{projectId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, - type = #{type,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, + `type` = #{type,jdbcType=VARCHAR}, maintainer = #{maintainer,jdbcType=VARCHAR}, priority = #{priority,jdbcType=VARCHAR}, - method = #{method,jdbcType=VARCHAR}, + `method` = #{method,jdbcType=VARCHAR}, prerequisite = #{prerequisite,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=BIGINT}, update_time = #{updateTime,jdbcType=BIGINT}, test_id = #{testId,jdbcType=VARCHAR}, + sort = #{sort,jdbcType=INTEGER}, remark = #{remark,jdbcType=LONGVARCHAR}, steps = #{steps,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} @@ -414,15 +430,16 @@ set node_id = #{nodeId,jdbcType=VARCHAR}, node_path = #{nodePath,jdbcType=VARCHAR}, project_id = #{projectId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, - type = #{type,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, + `type` = #{type,jdbcType=VARCHAR}, maintainer = #{maintainer,jdbcType=VARCHAR}, priority = #{priority,jdbcType=VARCHAR}, - method = #{method,jdbcType=VARCHAR}, + `method` = #{method,jdbcType=VARCHAR}, prerequisite = #{prerequisite,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=BIGINT}, update_time = #{updateTime,jdbcType=BIGINT}, - test_id = #{testId,jdbcType=VARCHAR} + test_id = #{testId,jdbcType=VARCHAR}, + sort = #{sort,jdbcType=INTEGER} where id = #{id,jdbcType=VARCHAR} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml index 6d0700e3cb..737f4932d5 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml @@ -11,7 +11,7 @@ select ltr.id, ltr.name, ltr.test_id as testId, ltr.description, user.name as userName, project.name as - projectName, + projectName, ltr.trigger_mode, ltr.create_time as createTime, ltr.update_time as updateTime, ltr.status as status, lt.name as testName from load_test_report ltr join load_test lt on ltr.test_id = lt.id diff --git a/backend/src/main/java/io/metersphere/commons/constants/ReportKeys.java b/backend/src/main/java/io/metersphere/commons/constants/ReportKeys.java index 2b483d1fd7..809d8c4936 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ReportKeys.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ReportKeys.java @@ -1,6 +1,6 @@ package io.metersphere.commons.constants; public enum ReportKeys { - LoadChart, ResponseTimeChart, Errors, ErrorsTop5, RequestStatistics, Overview, TimeInfo + LoadChart, ResponseTimeChart, Errors, ErrorsTop5, RequestStatistics, Overview, TimeInfo, ResultStatus } diff --git a/backend/src/main/java/io/metersphere/commons/constants/ReportTriggerMode.java b/backend/src/main/java/io/metersphere/commons/constants/ReportTriggerMode.java new file mode 100644 index 0000000000..59ce691580 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/ReportTriggerMode.java @@ -0,0 +1,5 @@ +package io.metersphere.commons.constants; + +public enum ReportTriggerMode { + MANUAL,SCHEDULE,API +} diff --git a/backend/src/main/java/io/metersphere/config/QuartzAutoConfiguration.java b/backend/src/main/java/io/metersphere/config/QuartzAutoConfiguration.java new file mode 100644 index 0000000000..38b219d4f5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/config/QuartzAutoConfiguration.java @@ -0,0 +1,93 @@ +package io.metersphere.config; + +import com.fit2cloud.autoconfigure.QuartzProperties; +import com.fit2cloud.quartz.QuartzInstanceIdGenerator; +import com.fit2cloud.quartz.SchedulerStarter; +import com.fit2cloud.quartz.service.QuartzManageService; +import com.fit2cloud.quartz.util.QuartzBeanFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.util.StringUtils; + +import javax.sql.DataSource; +import java.util.Properties; +import java.util.TimeZone; + +@Configuration +@EnableConfigurationProperties(QuartzProperties.class) +@ConditionalOnClass(DataSource.class) +@AutoConfigureAfter(DataSource.class) +public class QuartzAutoConfiguration { + private DataSource dataSource; + + private QuartzProperties properties; + + public QuartzAutoConfiguration(ObjectProvider dataSourceProvider, QuartzProperties properties) { + this.dataSource = dataSourceProvider.getIfAvailable(); + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true") + public SchedulerStarter schedulerStarter() { + return new SchedulerStarter(); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true") + public QuartzBeanFactory quartzBeanFactory() { + return new QuartzBeanFactory(); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true") + public QuartzManageService quartzManageService() { + return new QuartzManageService(); + } + + @Bean + @ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true") + public TimeZone quartzTimeZone() { + return TimeZone.getTimeZone(properties.getTimeZone()); + } + + @Bean + @ConditionalOnClass(DataSource.class) + @ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true") + public SchedulerFactoryBean clusterSchedulerFactoryBean() { + SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); + schedulerFactoryBean.setDataSource(this.dataSource); + schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey"); + schedulerFactoryBean.setOverwriteExistingJobs(true); + schedulerFactoryBean.setStartupDelay(60);// 60 秒之后开始执行定时任务 + Properties props = new Properties(); + props.put("org.quartz.scheduler.instanceName", "clusterScheduler"); + props.put("org.quartz.scheduler.instanceId", "AUTO"); // 集群下的instanceId 必须唯一 + props.put("org.quartz.scheduler.instanceIdGenerator.class", QuartzInstanceIdGenerator.class.getName());// instanceId 生成的方式 + props.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); + props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate"); + props.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); + props.put("org.quartz.jobStore.isClustered", "true"); + props.put("org.quartz.jobStore.clusterCheckinInterval", "20000"); + props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + props.put("org.quartz.threadPool.threadCount", "10"); + props.put("org.quartz.threadPool.threadPriority", "5"); + props.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true"); + schedulerFactoryBean.setQuartzProperties(props); + if (!StringUtils.isEmpty(this.properties.getSchedulerName())) { + schedulerFactoryBean.setBeanName(this.properties.getSchedulerName()); + } + return schedulerFactoryBean; + } +} diff --git a/backend/src/main/java/io/metersphere/dto/ReportDTO.java b/backend/src/main/java/io/metersphere/dto/ReportDTO.java index 6baf51fe59..f342bd4e74 100644 --- a/backend/src/main/java/io/metersphere/dto/ReportDTO.java +++ b/backend/src/main/java/io/metersphere/dto/ReportDTO.java @@ -19,4 +19,5 @@ public class ReportDTO { private String projectId; private String projectName; private String userName; + private String triggerMode; } diff --git a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java index 6c4a991e79..5922cf6e4c 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java @@ -10,6 +10,7 @@ import io.metersphere.i18n.Translator; import io.metersphere.track.service.TestCaseService; import org.apache.commons.lang3.StringUtils; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.UUID; @@ -72,6 +73,8 @@ public class TestCaseDataListener extends EasyExcelListener { return; } + Collections.reverse(list); + List result = list.stream() .map(item -> this.convert2TestCase(item)) .collect(Collectors.toList()); diff --git a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java index 44643d93f9..bb4ce5bcb0 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java @@ -2,12 +2,13 @@ package io.metersphere.job.sechedule; import io.metersphere.api.dto.SaveAPITestRequest; import io.metersphere.api.service.APITestService; +import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.job.QuartzManager; -import org.apache.commons.lang3.StringUtils; -import org.quartz.*; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.TriggerKey; + public class ApiTestJob extends MsScheduleJob { @@ -18,15 +19,11 @@ public class ApiTestJob extends MsScheduleJob { } @Override - public void execute(JobExecutionContext context) throws JobExecutionException { - if (StringUtils.isBlank(resourceId)) { - QuartzManager.removeJob(getJobKey(resourceId), getTriggerKey(resourceId)); - } - LogUtil.info("ApiTestSchedule Running: " + resourceId); - LogUtil.info("CronExpression: " + expression); + void businessExecute(JobExecutionContext context) { SaveAPITestRequest request = new SaveAPITestRequest(); request.setId(resourceId); request.setUserId(userId); + request.setTriggerMode(ReportTriggerMode.SCHEDULE.name()); apiTestService.run(request); } diff --git a/backend/src/main/java/io/metersphere/job/sechedule/MsScheduleJob.java b/backend/src/main/java/io/metersphere/job/sechedule/MsScheduleJob.java index 3e947d2bdd..2ddaff0345 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/MsScheduleJob.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/MsScheduleJob.java @@ -1,8 +1,9 @@ package io.metersphere.job.sechedule; -import org.quartz.Job; +import io.metersphere.commons.utils.LogUtil; +import org.quartz.*; -public abstract class MsScheduleJob implements Job{ +public abstract class MsScheduleJob implements Job { protected String resourceId; @@ -10,15 +11,19 @@ public abstract class MsScheduleJob implements Job{ protected String expression; - public void setResourceId(String resourceId) { - this.resourceId = resourceId; + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + + JobKey jobKey = context.getTrigger().getJobKey(); + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + this.resourceId = jobDataMap.getString("resourceId"); + this.userId = jobDataMap.getString("userId"); + this.expression = jobDataMap.getString("expression"); + + LogUtil.info(jobKey.getGroup()+ " Running: " + resourceId); + LogUtil.info("CronExpression: " + expression); + businessExecute(context); } - public void setExpression(String expression) { - this.expression = expression; - } - - public void setUserId(String userId) { - this.userId = userId; - } + abstract void businessExecute(JobExecutionContext context); } diff --git a/backend/src/main/java/io/metersphere/job/sechedule/PerformanceTestJob.java b/backend/src/main/java/io/metersphere/job/sechedule/PerformanceTestJob.java index bca394e8fa..e17daeae0c 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/PerformanceTestJob.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/PerformanceTestJob.java @@ -1,14 +1,11 @@ package io.metersphere.job.sechedule; +import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.job.QuartzManager; import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.track.request.testplan.RunTestPlanRequest; -import org.apache.commons.lang3.StringUtils; import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.TriggerKey; @@ -21,17 +18,12 @@ public class PerformanceTestJob extends MsScheduleJob { } @Override - public void execute(JobExecutionContext context) throws JobExecutionException { - if (StringUtils.isBlank(resourceId)) { - QuartzManager.removeJob(getJobKey(resourceId), getTriggerKey(resourceId)); - } - LogUtil.info("PerformanceTestSchedule Running: " + resourceId); - LogUtil.info("CronExpression: " + expression); + void businessExecute(JobExecutionContext context) { RunTestPlanRequest request = new RunTestPlanRequest(); request.setId(resourceId); request.setUserId(userId); + request.setTriggerMode(ReportTriggerMode.SCHEDULE.name()); performanceTestService.run(request); - } public static JobKey getJobKey(String testId) { diff --git a/backend/src/main/java/io/metersphere/job/QuartzManager.java b/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java similarity index 69% rename from backend/src/main/java/io/metersphere/job/QuartzManager.java rename to backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java index a3d9ff42ab..9fd2aaa882 100644 --- a/backend/src/main/java/io/metersphere/job/QuartzManager.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java @@ -1,12 +1,16 @@ -package io.metersphere.job; +package io.metersphere.job.sechedule; import io.metersphere.commons.utils.LogUtil; import org.quartz.*; -import org.quartz.impl.StdSchedulerFactory; +import org.springframework.stereotype.Component; -public class QuartzManager { +import javax.annotation.Resource; - public static StdSchedulerFactory sf = new StdSchedulerFactory(); +@Component +public class ScheduleManager { + + @Resource + private Scheduler scheduler; /** * 添加 simpleJob @@ -17,24 +21,28 @@ public class QuartzManager { * @param jobDataMap * @throws SchedulerException */ - public static void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime, - JobDataMap jobDataMap) throws SchedulerException { + public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime, + JobDataMap jobDataMap) throws SchedulerException { - Scheduler sched = sf.getScheduler(); + JobBuilder jobBuilder = JobBuilder.newJob(cls).withIdentity(jobKey); - JobDetail jd = JobBuilder.newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build(); + if (jobDataMap != null) { + jobBuilder.usingJobData(jobDataMap); + } + + JobDetail jd = jobBuilder.build(); SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey) .withSchedule( SimpleScheduleBuilder.simpleSchedule().withIntervalInHours(repeatIntervalTime).repeatForever()) .startNow().build(); - sched.scheduleJob(jd, trigger); + scheduler.scheduleJob(jd, trigger); try { - if (!sched.isShutdown()) { - sched.start(); + if (!scheduler.isShutdown()) { + scheduler.start(); } } catch (SchedulerException e) { @@ -44,8 +52,8 @@ public class QuartzManager { } } - public static void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime) throws SchedulerException { - addSimpleJob(jobKey, triggerKey, cls, repeatIntervalTime); + public void addSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class cls, int repeatIntervalTime) throws SchedulerException { + addSimpleJob(jobKey, triggerKey, cls, repeatIntervalTime); } /** @@ -56,11 +64,14 @@ public class QuartzManager { * @param cron * @param jobDataMap */ - public static void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) { + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) { try { + + LogUtil.info("addCronJob: " + triggerKey.getName() + "," + triggerKey.getGroup()); + JobBuilder jobBuilder = JobBuilder.newJob(jobClass).withIdentity(jobKey); if (jobDataMap != null) { - jobBuilder.setJobData(jobDataMap); + jobBuilder.usingJobData(jobDataMap); } JobDetail jobDetail = jobBuilder.build(); @@ -74,12 +85,10 @@ public class QuartzManager { CronTrigger trigger = (CronTrigger) triggerBuilder.build(); - Scheduler sched = sf.getScheduler(); + scheduler.scheduleJob(jobDetail, trigger); - sched.scheduleJob(jobDetail, trigger); - - if (!sched.isShutdown()) { - sched.start(); + if (!scheduler.isShutdown()) { + scheduler.start(); } } catch (Exception e) { LogUtil.error(e.getMessage(), e); @@ -87,7 +96,7 @@ public class QuartzManager { } } - public static void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) { + public void addCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) { addCronJob(jobKey, triggerKey, jobClass, cron, null); } @@ -97,14 +106,12 @@ public class QuartzManager { * @param cron * @throws SchedulerException */ - public static void modifyCronJobTime(TriggerKey triggerKey, String cron) throws SchedulerException { - - Scheduler sched = sf.getScheduler(); + public void modifyCronJobTime(TriggerKey triggerKey, String cron) throws SchedulerException { LogUtil.info("modifyCronJobTime: " + triggerKey.getName() + "," + triggerKey.getGroup()); try { - CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { return; @@ -125,7 +132,7 @@ public class QuartzManager { trigger = (CronTrigger) triggerBuilder.build();// 创建Trigger对象 - sched.rescheduleJob(triggerKey, trigger);// 修改一个任务的触发时间 + scheduler.rescheduleJob(triggerKey, trigger);// 修改一个任务的触发时间 /** 方式一 :调用 rescheduleJob 结束 */ /** 方式二:先删除,然后在创建一个新的Job */ @@ -146,15 +153,13 @@ public class QuartzManager { * @param repeatIntervalTime * @throws SchedulerException */ - public static void modifySimpleJobTime(TriggerKey triggerKey, int repeatIntervalTime) throws SchedulerException { - - Scheduler sched = sf.getScheduler(); + public void modifySimpleJobTime(TriggerKey triggerKey, int repeatIntervalTime) throws SchedulerException { try { LogUtil.info("modifySimpleJobTime: " + triggerKey.getName() + "," + triggerKey.getGroup()); - SimpleTrigger trigger = (SimpleTrigger) sched.getTrigger(triggerKey); + SimpleTrigger trigger = (SimpleTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { return; @@ -175,7 +180,7 @@ public class QuartzManager { trigger = (SimpleTrigger) triggerBuilder.build();// 创建Trigger对象 - sched.rescheduleJob(triggerKey, trigger);// 修改一个任务的触发时间 + scheduler.rescheduleJob(triggerKey, trigger);// 修改一个任务的触发时间 /** 方式一 :调用 rescheduleJob 结束 */ @@ -201,19 +206,17 @@ public class QuartzManager { * @param jobKey * @param triggerKey */ - public static void removeJob(JobKey jobKey, TriggerKey triggerKey) { + public void removeJob(JobKey jobKey, TriggerKey triggerKey) { try { LogUtil.info("RemoveJob: " + jobKey.getName() + "," + jobKey.getGroup()); - Scheduler sched = sf.getScheduler(); + scheduler.pauseTrigger(triggerKey); - sched.pauseTrigger(triggerKey); + scheduler.unscheduleJob(triggerKey); - sched.unscheduleJob(triggerKey); - - sched.deleteJob(jobKey); + scheduler.deleteJob(jobKey); } catch (Exception e) { LogUtil.error(e.getMessage(), e); @@ -232,7 +235,7 @@ public class QuartzManager { } - public static void shutdownJobs(Scheduler sched) { + public void shutdownJobs(Scheduler sched) { try { if (!sched.isShutdown()) { sched.shutdown(); @@ -252,12 +255,10 @@ public class QuartzManager { * @param jobDataMap * @throws SchedulerException */ - public static void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, - int intervalTime, JobDataMap jobDataMap) throws SchedulerException { + public void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, + int intervalTime, JobDataMap jobDataMap) throws SchedulerException { - Scheduler sched = sf.getScheduler(); - - if (sched.checkExists(triggerKey)) { + if (scheduler.checkExists(triggerKey)) { modifySimpleJobTime(triggerKey, intervalTime); } else { addSimpleJob(jobKey, triggerKey, clz, intervalTime, jobDataMap); @@ -265,7 +266,7 @@ public class QuartzManager { } - public static void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, int intervalTime) throws SchedulerException { + public void addOrUpdateSimpleJob(JobKey jobKey, TriggerKey triggerKey, Class clz, int intervalTime) throws SchedulerException { addOrUpdateSimpleJob(jobKey, triggerKey, clz, intervalTime, null); } @@ -279,23 +280,22 @@ public class QuartzManager { * @param jobDataMap * @throws SchedulerException */ - public static void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) throws SchedulerException { - Scheduler sched = sf.getScheduler(); + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron, JobDataMap jobDataMap) throws SchedulerException { LogUtil.info("AddOrUpdateCronJob: " + jobKey.getName() + "," + triggerKey.getGroup()); - if (sched.checkExists(triggerKey)) { + if (scheduler.checkExists(triggerKey)) { modifyCronJobTime(triggerKey, cron); } else { addCronJob(jobKey, triggerKey, jobClass, cron, jobDataMap); } } - public static void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) throws SchedulerException { + public void addOrUpdateCronJob(JobKey jobKey, TriggerKey triggerKey, Class jobClass, String cron) throws SchedulerException { addOrUpdateCronJob(jobKey, triggerKey, jobClass, cron, null); } - public static JobDataMap getDefaultJobDataMap(String resourceId, String expression, String userId) { + public JobDataMap getDefaultJobDataMap(String resourceId, String expression, String userId) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("resourceId", resourceId); jobDataMap.put("expression", expression); diff --git a/backend/src/main/java/io/metersphere/performance/controller/PerformanceReportController.java b/backend/src/main/java/io/metersphere/performance/controller/PerformanceReportController.java index 9656ff0451..b3d806ef85 100644 --- a/backend/src/main/java/io/metersphere/performance/controller/PerformanceReportController.java +++ b/backend/src/main/java/io/metersphere/performance/controller/PerformanceReportController.java @@ -20,9 +20,8 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.List; - import javax.annotation.Resource; +import java.util.List; @RestController @RequestMapping(value = "performance/report") @@ -63,7 +62,7 @@ public class PerformanceReportController { @GetMapping("/content/{reportId}") public List getReportContent(@PathVariable String reportId) { - return reportService.getReport(reportId); + return reportService.getReportStatistics(reportId); } @GetMapping("/content/errors/{reportId}") diff --git a/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java b/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java index b4c3c0ace7..4620020bef 100644 --- a/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java +++ b/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java @@ -21,6 +21,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; + import javax.annotation.Resource; import java.util.List; @@ -101,6 +102,11 @@ public class PerformanceTestController { return performanceTestService.run(request); } + @GetMapping("stop/{reportId}") + public void stopTest(@PathVariable String reportId) { + performanceTestService.stopTest(reportId); + } + @GetMapping("/file/metadata/{testId}") public List getFileMetadata(@PathVariable String testId) { return fileService.getFileMetadataByTestId(testId); diff --git a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java index 75517fb612..8d199b0808 100644 --- a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java @@ -5,10 +5,7 @@ import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.ext.ExtLoadTestMapper; import io.metersphere.base.mapper.ext.ExtLoadTestReportDetailMapper; import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper; -import io.metersphere.commons.constants.APITestStatus; -import io.metersphere.commons.constants.PerformanceTestStatus; -import io.metersphere.commons.constants.ScheduleGroup; -import io.metersphere.commons.constants.ScheduleType; +import io.metersphere.commons.constants.*; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.ServiceUtils; @@ -74,6 +71,8 @@ public class PerformanceTestService { private KafkaProperties kafkaProperties; @Resource private ScheduleService scheduleService; + @Resource + private TestCaseMapper testCaseMapper; public List list(QueryTestPlanRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); @@ -82,6 +81,20 @@ public class PerformanceTestService { public void delete(DeleteTestPlanRequest request) { String testId = request.getId(); + + // 是否关联测试用例 + TestCaseExample testCaseExample = new TestCaseExample(); + testCaseExample.createCriteria().andTestIdEqualTo(testId); + List testCases = testCaseMapper.selectByExample(testCaseExample); + if (testCases.size() > 0) { + String caseName = ""; + for (int i = 0; i < testCases.size(); i++) { + caseName = caseName + testCases.get(i).getName() + ","; + } + caseName = caseName.substring(0, caseName.length() - 1); + MSException.throwException(Translator.get("related_case_del_fail_prefix") + caseName + Translator.get("related_case_del_fail_suffix")); + } + LoadTestReportExample loadTestReportExample = new LoadTestReportExample(); loadTestReportExample.createCriteria().andTestIdEqualTo(testId); List loadTestReports = loadTestReportMapper.selectByExample(loadTestReportExample); @@ -222,7 +235,7 @@ public class PerformanceTestService { MSException.throwException(String.format("Test cannot be run,test ID:%s", request.getId())); } - startEngine(loadTest, engine); + startEngine(loadTest, engine, request.getTriggerMode()); // todo:通过调用stop方法能够停止正在运行的engine,但是如果部署了多个backend实例,页面发送的停止请求如何定位到具体的engine @@ -254,14 +267,19 @@ public class PerformanceTestService { } } - private void startEngine(LoadTestWithBLOBs loadTest, Engine engine) { + private void startEngine(LoadTestWithBLOBs loadTest, Engine engine, String triggerMode) { LoadTestReport testReport = new LoadTestReport(); testReport.setId(engine.getReportId()); testReport.setCreateTime(engine.getStartTime()); testReport.setUpdateTime(engine.getStartTime()); testReport.setTestId(loadTest.getId()); testReport.setName(loadTest.getName()); - testReport.setUserId(Optional.ofNullable(SessionUtils.getUser().getId()).orElse(loadTest.getUserId())); + testReport.setTriggerMode(triggerMode); + if (SessionUtils.getUser() == null) { + testReport.setUserId(loadTest.getUserId()); + } else { + testReport.setUserId(SessionUtils.getUser().getId()); + } // 启动测试 try { @@ -280,6 +298,13 @@ public class PerformanceTestService { loadTestReportDetailMapper.insertSelective(reportDetail); // append \n extLoadTestReportDetailMapper.appendLine(testReport.getId(), "\n"); + // 保存一个 reportStatus + LoadTestReportResult reportResult = new LoadTestReportResult(); + reportResult.setId(UUID.randomUUID().toString()); + reportResult.setReportId(testReport.getId()); + reportResult.setReportKey(ReportKeys.ResultStatus.name()); + reportResult.setReportValue("Ready"); // 初始化一个 result_status, 这个值用在data-streaming中 + loadTestReportResultMapper.insertSelective(reportResult); } catch (MSException e) { LogUtil.error(e); loadTest.setStatus(PerformanceTestStatus.Error.name()); @@ -388,4 +413,8 @@ public class PerformanceTestService { private void addOrUpdatePerformanceTestCronJob(Schedule request) { scheduleService.addOrUpdateCronJob(request, PerformanceTestJob.getJobKey(request.getResourceId()), PerformanceTestJob.getTriggerKey(request.getResourceId()), PerformanceTestJob.class); } + + public void stopTest(String reportId) { + reportService.deleteReport(reportId); + } } diff --git a/backend/src/main/java/io/metersphere/performance/service/ReportService.java b/backend/src/main/java/io/metersphere/performance/service/ReportService.java index 14e52e2817..5f3e3a66ad 100644 --- a/backend/src/main/java/io/metersphere/performance/service/ReportService.java +++ b/backend/src/main/java/io/metersphere/performance/service/ReportService.java @@ -25,11 +25,10 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; -import javax.annotation.Resource; - @Service @Transactional(rollbackFor = Exception.class) public class ReportService { @@ -109,7 +108,7 @@ public class ReportService { return loadTestReportResults.get(0).getReportValue(); } - public List getReport(String id) { + public List getReportStatistics(String id) { checkReportStatus(id); String reportValue = getContent(id, ReportKeys.RequestStatistics); return JSON.parseArray(reportValue, Statistics.class); @@ -154,11 +153,7 @@ public class ReportService { public void checkReportStatus(String reportId) { LoadTestReport loadTestReport = loadTestReportMapper.selectByPrimaryKey(reportId); String reportStatus = loadTestReport.getStatus(); - if (StringUtils.equals(PerformanceTestStatus.Running.name(), reportStatus)) { - MSException.throwException("Reporting in progress..."); - } else if (StringUtils.equals(PerformanceTestStatus.Reporting.name(), reportStatus)) { - MSException.throwException("Reporting in progress..."); - } else if (StringUtils.equals(PerformanceTestStatus.Error.name(), reportStatus)) { + if (StringUtils.equals(PerformanceTestStatus.Error.name(), reportStatus)) { MSException.throwException("Report generation error!"); } } @@ -214,4 +209,8 @@ public class ReportService { String content = loadTestReportLogs.stream().map(LoadTestReportLog::getContent).reduce("", (a, b) -> a + b); return content.getBytes(); } + + public LoadTestReport getReport(String reportId) { + return loadTestReportMapper.selectByPrimaryKey(reportId); + } } diff --git a/backend/src/main/java/io/metersphere/service/ScheduleService.java b/backend/src/main/java/io/metersphere/service/ScheduleService.java index d15becdeca..4ddb050080 100644 --- a/backend/src/main/java/io/metersphere/service/ScheduleService.java +++ b/backend/src/main/java/io/metersphere/service/ScheduleService.java @@ -4,13 +4,10 @@ import com.alibaba.fastjson.JSON; import io.metersphere.base.domain.Schedule; import io.metersphere.base.domain.ScheduleExample; import io.metersphere.base.mapper.ScheduleMapper; -import io.metersphere.commons.constants.ScheduleGroup; -import io.metersphere.commons.constants.ScheduleType; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; -import io.metersphere.job.QuartzManager; -import io.metersphere.job.sechedule.ApiTestJob; +import io.metersphere.job.sechedule.ScheduleManager; import org.apache.commons.lang3.StringUtils; import org.quartz.JobKey; import org.quartz.SchedulerException; @@ -28,6 +25,8 @@ public class ScheduleService { @Resource private ScheduleMapper scheduleMapper; + @Resource + private ScheduleManager scheduleManager; public void addSchedule(Schedule schedule) { schedule.setId(UUID.randomUUID().toString()); @@ -73,11 +72,10 @@ public class ScheduleService { try { if (schedule.getEnable()) { LogUtil.error("初始化任务:" + JSON.toJSONString(schedule)); - QuartzManager.addOrUpdateCronJob(new JobKey(schedule.getKey(), schedule.getGroup()), + scheduleManager.addOrUpdateCronJob(new JobKey(schedule.getKey(), schedule.getGroup()), new TriggerKey(schedule.getKey(), schedule.getGroup()), Class.forName(schedule.getJob()), schedule.getValue(), - QuartzManager.getDefaultJobDataMap(schedule.getResourceId(), schedule.getValue(), schedule.getUserId())); + scheduleManager.getDefaultJobDataMap(schedule.getResourceId(), schedule.getValue(), schedule.getUserId())); } - Thread.sleep(1*60*1000); } catch (Exception e) { LogUtil.error("初始化任务失败", e); e.printStackTrace(); @@ -100,14 +98,14 @@ public class ScheduleService { String cronExpression = request.getValue(); if (enable != null && enable && StringUtils.isNotBlank(cronExpression)) { try { - QuartzManager.addOrUpdateCronJob(jobKey, triggerKey, clazz, cronExpression, QuartzManager.getDefaultJobDataMap(request.getResourceId(), cronExpression, SessionUtils.getUser().getId())); + scheduleManager.addOrUpdateCronJob(jobKey, triggerKey, clazz, cronExpression, scheduleManager.getDefaultJobDataMap(request.getResourceId(), cronExpression, SessionUtils.getUser().getId())); } catch (SchedulerException e) { LogUtil.error(e.getMessage(), e); MSException.throwException("定时任务开启异常"); } } else { try { - QuartzManager.removeJob(jobKey, triggerKey); + scheduleManager.removeJob(jobKey, triggerKey); } catch (Exception e) { MSException.throwException("定时任务关闭异常"); } diff --git a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java index 39f04e4f46..adc393888a 100644 --- a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java +++ b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java @@ -231,7 +231,7 @@ public class TestResourcePoolService { List testResourcePools = listResourcePools(request); // 重新校验 pool for (TestResourcePoolDTO pool : testResourcePools) { - // 手动设置成无效的, 排除 + // 手动设置成无效的, 排除 if (INVALID.name().equals(pool.getStatus())) { continue; } diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/RunTestPlanRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/RunTestPlanRequest.java index c9fd036171..3f05f1ce3c 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/RunTestPlanRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/RunTestPlanRequest.java @@ -7,4 +7,5 @@ import lombok.Setter; @Setter public class RunTestPlanRequest extends TestPlanRequest { private String userId; + private String triggerMode; } diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java index d9b0734f74..4155783918 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java @@ -1,5 +1,6 @@ package io.metersphere.track.request.testplan; +import io.metersphere.base.domain.Schedule; import lombok.Getter; import lombok.Setter; @@ -27,7 +28,7 @@ public class TestPlanRequest { private String runtimeConfiguration; - private String schedule; + private Schedule schedule; private String testResourcePoolId; diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java index 3f6981b90f..4093b26249 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java @@ -229,6 +229,9 @@ public class TestCaseNodeService { public List getAllNodeByPlanId(String planId) { TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId); + if (testPlan == null) { + return Collections.emptyList(); + } return getNodeTreeByProjectId(testPlan.getProjectId()); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index b2578aeb90..628cb9bf95 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -35,6 +35,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service @@ -121,7 +122,13 @@ public class TestCaseService { } public List listTestCase(QueryTestCaseRequest request) { - request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); + List orderList = ServiceUtils.getDefaultOrder(request.getOrders()); + OrderRequest order = new OrderRequest(); + // 对模板导入的测试用例排序 + order.setName("sort"); + order.setType("desc"); + orderList.add(order); + request.setOrders(orderList); return extTestCaseMapper.list(request); } @@ -140,7 +147,9 @@ public class TestCaseService { public List getTestCaseNames(QueryTestCaseRequest request) { if (StringUtils.isNotBlank(request.getPlanId())) { TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId()); - request.setProjectId(testPlan.getProjectId()); + if (testPlan != null) { + request.setProjectId(testPlan.getProjectId()); + } } List testCaseNames = extTestCaseMapper.getTestCaseNames(request); @@ -182,7 +191,7 @@ public class TestCaseService { TestCaseExample testCaseExample = new TestCaseExample(); testCaseExample.createCriteria().andProjectIdIn(projectIds); - testCaseExample.setOrderByClause("update_time desc"); + testCaseExample.setOrderByClause("update_time desc, sort desc"); return testCaseMapper.selectByExample(testCaseExample); } @@ -241,8 +250,10 @@ public class TestCaseService { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class); if (!testCases.isEmpty()) { + AtomicInteger sort = new AtomicInteger(); testCases.forEach(testcase -> { testcase.setNodeId(nodePathMap.get(testcase.getNodePath())); + testcase.setSort(sort.getAndIncrement()); mapper.insert(testcase); }); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 284da5d934..245e7c5838 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -91,7 +91,7 @@ public class TestPlanService { } public TestPlan getTestPlan(String testPlanId) { - return testPlanMapper.selectByPrimaryKey(testPlanId); + return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan()); } public int editTestPlan(TestPlan testPlan) { diff --git a/backend/src/main/java/io/metersphere/websocket/ReportWebSocket.java b/backend/src/main/java/io/metersphere/websocket/ReportWebSocket.java new file mode 100644 index 0000000000..c8f6aff33d --- /dev/null +++ b/backend/src/main/java/io/metersphere/websocket/ReportWebSocket.java @@ -0,0 +1,97 @@ +package io.metersphere.websocket; + +import io.metersphere.base.domain.LoadTestReport; +import io.metersphere.commons.constants.PerformanceTestStatus; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.performance.service.ReportService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +@ServerEndpoint("/performance/report/{reportId}") +@Component +public class ReportWebSocket { + + private static ReportService reportService; + + @Resource + public void setReportService(ReportService reportService) { + ReportWebSocket.reportService = reportService; + } + + /** + * 开启连接的操作 + */ + @OnOpen + public void onOpen(@PathParam("reportId") String reportId, Session session) throws IOException { + //开启一个线程对数据库中的数据进行轮询 + ReportThread reportThread = new ReportThread(session, reportId); + Thread thread = new Thread(reportThread); + thread.start(); + } + + /** + * 连接关闭的操作 + */ + @OnClose + public void onClose(Session session) { + + } + + /** + * 给服务器发送消息告知数据库发生变化 + */ + @OnMessage + public void onMessage(Session session, String message) { + } + + /** + * 出错的操作 + */ + @OnError + public void onError(Throwable error) { + System.out.println(error); + error.printStackTrace(); + } + + public static class ReportThread implements Runnable { + private boolean stopMe = true; + private final String reportId; + private final Session session; + private int refresh; + + public ReportThread(Session session, String reportId) { + this.session = session; + this.reportId = reportId; + this.refresh = 0; + } + + public void stopMe() { + stopMe = false; + } + + public void run() { + while (stopMe) { + try { + LoadTestReport report = reportService.getReport(reportId); + if (StringUtils.equalsAny(report.getStatus(), PerformanceTestStatus.Completed.name(), PerformanceTestStatus.Error.name())) { + this.stopMe(); + session.close(); + break; + } + if (PerformanceTestStatus.Running.name().equals(report.getStatus())) { + session.getBasicRemote().sendText("refresh-" + this.refresh++); + } + Thread.sleep(20 * 1000L); + } catch (Exception e) { + LogUtil.error(e); + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 2afe88e538..4a02c75933 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -64,4 +64,13 @@ kafka.ssl.provider= kafka.ssl.truststore-type= # jmeter -jmeter.home=/opt/jmeter \ No newline at end of file +jmeter.home=/opt/jmeter + +# quartz +quartz.enabled=true +quartz.scheduler-name=msServerJob + +# file upload +spring.servlet.multipart.max-file-size=30MB +spring.servlet.multipart.max-request-size=30MB + diff --git a/backend/src/main/resources/db/migration/V5__schedule.sql b/backend/src/main/resources/db/migration/V5__schedule.sql index 4d33828f10..41f7e62cd6 100644 --- a/backend/src/main/resources/db/migration/V5__schedule.sql +++ b/backend/src/main/resources/db/migration/V5__schedule.sql @@ -1,3 +1,155 @@ +-- quartz start +CREATE TABLE `qrtz_job_details` ( + `SCHED_NAME` varchar(120) NOT NULL, + `JOB_NAME` varchar(200) NOT NULL, + `JOB_GROUP` varchar(200) NOT NULL, + `DESCRIPTION` varchar(250) DEFAULT NULL, + `JOB_CLASS_NAME` varchar(250) NOT NULL, + `IS_DURABLE` varchar(1) NOT NULL, + `IS_NONCONCURRENT` varchar(1) NOT NULL, + `IS_UPDATE_DATA` varchar(1) NOT NULL, + `REQUESTS_RECOVERY` varchar(1) NOT NULL, + `JOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), + KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`), + KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `JOB_NAME` varchar(200) NOT NULL, + `JOB_GROUP` varchar(200) NOT NULL, + `DESCRIPTION` varchar(250) DEFAULT NULL, + `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL, + `PREV_FIRE_TIME` bigint(13) DEFAULT NULL, + `PRIORITY` int(11) DEFAULT NULL, + `TRIGGER_STATE` varchar(16) NOT NULL, + `TRIGGER_TYPE` varchar(8) NOT NULL, + `START_TIME` bigint(13) NOT NULL, + `END_TIME` bigint(13) DEFAULT NULL, + `CALENDAR_NAME` varchar(200) DEFAULT NULL, + `MISFIRE_INSTR` smallint(2) DEFAULT NULL, + `JOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), + KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`), + KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`), + KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`), + KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`), + KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`), + KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`), + KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`), + KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`), + KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`), + KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`), + KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`), + CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `qrtz_job_details` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_blob_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `BLOB_DATA` blob, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_calendars` ( + `SCHED_NAME` varchar(120) NOT NULL, + `CALENDAR_NAME` varchar(200) NOT NULL, + `CALENDAR` blob NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_cron_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `CRON_EXPRESSION` varchar(120) NOT NULL, + `TIME_ZONE_ID` varchar(80) DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_fired_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `ENTRY_ID` varchar(95) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `INSTANCE_NAME` varchar(200) NOT NULL, + `FIRED_TIME` bigint(13) NOT NULL, + `SCHED_TIME` bigint(13) NOT NULL, + `PRIORITY` int(11) NOT NULL, + `STATE` varchar(16) NOT NULL, + `JOB_NAME` varchar(200) DEFAULT NULL, + `JOB_GROUP` varchar(200) DEFAULT NULL, + `IS_NONCONCURRENT` varchar(1) DEFAULT NULL, + `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`), + KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`), + KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`), + KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), + KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`), + KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_locks` ( + `SCHED_NAME` varchar(120) NOT NULL, + `LOCK_NAME` varchar(40) NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_paused_trigger_grps` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_scheduler_state` ( + `SCHED_NAME` varchar(120) NOT NULL, + `INSTANCE_NAME` varchar(200) NOT NULL, + `LAST_CHECKIN_TIME` bigint(13) NOT NULL, + `CHECKIN_INTERVAL` bigint(13) NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_simple_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `REPEAT_COUNT` bigint(7) NOT NULL, + `REPEAT_INTERVAL` bigint(12) NOT NULL, + `TIMES_TRIGGERED` bigint(10) NOT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `qrtz_simprop_triggers` ( + `SCHED_NAME` varchar(120) NOT NULL, + `TRIGGER_NAME` varchar(200) NOT NULL, + `TRIGGER_GROUP` varchar(200) NOT NULL, + `STR_PROP_1` varchar(512) DEFAULT NULL, + `STR_PROP_2` varchar(512) DEFAULT NULL, + `STR_PROP_3` varchar(512) DEFAULT NULL, + `INT_PROP_1` int(11) DEFAULT NULL, + `INT_PROP_2` int(11) DEFAULT NULL, + `LONG_PROP_1` bigint(20) DEFAULT NULL, + `LONG_PROP_2` bigint(20) DEFAULT NULL, + `DEC_PROP_1` decimal(13,4) DEFAULT NULL, + `DEC_PROP_2` decimal(13,4) DEFAULT NULL, + `BOOL_PROP_1` varchar(1) DEFAULT NULL, + `BOOL_PROP_2` varchar(1) DEFAULT NULL, + PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), + CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- quartz end + CREATE TABLE IF NOT EXISTS `schedule` ( `id` varchar(50) NOT NULL COMMENT 'Schedule ID', `key` varchar(50) NOT NULL COMMENT 'Schedule Key', @@ -16,4 +168,8 @@ CREATE TABLE IF NOT EXISTS `schedule` ( ALTER TABLE `api_test` DROP COLUMN `schedule`; ALTER TABLE `load_test` DROP COLUMN `schedule`; ALTER TABLE `api_test_report` ADD `trigger_mode` varchar(64) NULL; -ALTER TABLE `load_test_report` ADD `trigger_mode` varchar(64) NULL; \ No newline at end of file +ALTER TABLE `load_test_report` ADD `trigger_mode` varchar(64) NULL; +UPDATE `api_test_report` SET `trigger_mode` = 'MANUAL' WHERE 1; +UPDATE `load_test_report` SET `trigger_mode` = 'MANUAL' WHERE 1; + +alter table test_case add sort int null comment 'Import test case sort'; \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 681aaa4fb1..cd0bf2223f 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -43,6 +43,8 @@ load_test_already_exists=Duplicate load test name no_nodes_message=No node message duplicate_node_ip=Duplicate IPs max_thread_insufficient=The number of concurrent users exceeds +related_case_del_fail_prefix=Connected to +related_case_del_fail_suffix=TestCase, please disassociate first #workspace workspace_name_is_null=Workspace name cannot be null workspace_name_already_exists=The workspace name already exists diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 32b03e7441..d8ce92c489 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -43,6 +43,8 @@ load_test_already_exists=测试名称不能重复 no_nodes_message=没有节点信息 duplicate_node_ip=节点 IP 重复 max_thread_insufficient=并发用户数超额 +related_case_del_fail_prefix=已关联到 +related_case_del_fail_suffix=测试用例,请先解除关联 #workspace workspace_name_is_null=工作空间名不能为空 workspace_name_already_exists=工作空间名已存在 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 08e83414ab..45ac9d5749 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -43,6 +43,8 @@ load_test_already_exists=測試名稱不能重復 no_nodes_message=沒有節點信息 duplicate_node_ip=節點 IP 重復 max_thread_insufficient=並發用戶數超額 +related_case_del_fail_prefix=已關聯到 +related_case_del_fail_suffix=測試用例,請先解除關聯 #workspace workspace_name_is_null=工作空間名不能為空 workspace_name_already_exists=工作空間名已存在 diff --git a/frontend/src/business/components/api/home/ApiReportRecentList.vue b/frontend/src/business/components/api/home/ApiReportRecentList.vue index a5c386dcda..3fc94c1bbe 100644 --- a/frontend/src/business/components/api/home/ApiReportRecentList.vue +++ b/frontend/src/business/components/api/home/ApiReportRecentList.vue @@ -15,6 +15,11 @@ {{ scope.row.updateTime | timestampFormatDate }} + + +