diff --git a/.gitignore b/.gitignore
index 2c0c98f873..cfbf0b31fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,7 +38,6 @@ qywx.json
### VS Code ###
.vscode/
-**/src/test/
### flattened
.flattened-pom.xml
diff --git a/backend/.gitignore b/backend/.gitignore
index df8a7b93e6..5d81d691f5 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -27,7 +27,7 @@ yarn-error.log*
src/main/resources/static
src/main/resources/public
-src/test/
+
target
.settings
.project
diff --git a/backend/app/src/main/resources/migration/3.0.0/ddl/V2__ddl.sql b/backend/app/src/main/resources/migration/3.0.0/ddl/V2__ddl.sql
new file mode 100644
index 0000000000..34d9b06689
--- /dev/null
+++ b/backend/app/src/main/resources/migration/3.0.0/ddl/V2__ddl.sql
@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS `project`
+(
+ `id` VARCHAR(50) NOT NULL COMMENT '项目ID',
+ `workspace_id` VARCHAR(50) NOT NULL COMMENT '工作空间ID',
+ `name` VARCHAR(64) NOT NULL COMMENT '项目名称',
+ `description` VARCHAR(255) COMMENT '项目描述',
+ `create_time` BIGINT NOT NULL COMMENT '创建时间',
+ `update_time` BIGINT NOT NULL COMMENT '更新时间',
+ `create_user` VARCHAR(100) COMMENT '创建人',
+ `system_id` VARCHAR(50) COMMENT '',
+ PRIMARY KEY (id)
+) COMMENT = '项目';
diff --git a/backend/services/issue-management/pom.xml b/backend/services/issue-management/pom.xml
new file mode 100644
index 0000000000..405e98c29c
--- /dev/null
+++ b/backend/services/issue-management/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+ io.metersphere
+ services
+ ${revision}
+
+ metersphere-issue-management
+ ${revision}
+ issue-management
+
+
+
+
+ io.metersphere
+ metersphere-sdk
+ ${revision}
+
+
+
+
+
+
+
diff --git a/backend/services/issue-management/src/main/java/io/metersphere/issue/domain/TestCase.java b/backend/services/issue-management/src/main/java/io/metersphere/issue/domain/TestCase.java
new file mode 100644
index 0000000000..3ab5d7c0a5
--- /dev/null
+++ b/backend/services/issue-management/src/main/java/io/metersphere/issue/domain/TestCase.java
@@ -0,0 +1,72 @@
+package io.metersphere.track.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TestCase implements Serializable {
+ private String id;
+
+ private String nodeId;
+
+ private String testId;
+
+ private String nodePath;
+
+ private String projectId;
+
+ private String name;
+
+ private String type;
+
+ private String maintainer;
+
+ private String priority;
+
+ private String method;
+
+ private Long createTime;
+
+ private Long updateTime;
+
+ private Integer sort;
+
+ private Integer num;
+
+ private String reviewStatus;
+
+ private String tags;
+
+ private String demandId;
+
+ private String demandName;
+
+ private String status;
+
+ private String stepModel;
+
+ private String customNum;
+
+ private String createUser;
+
+ private String originalStatus;
+
+ private Long deleteTime;
+
+ private String deleteUserId;
+
+ private Long order;
+
+ private Boolean casePublic;
+
+ private String versionId;
+
+ private String refId;
+
+ private Boolean latest;
+
+ private String lastExecuteResult;
+
+ private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/backend/services/issue-management/src/main/java/io/metersphere/issue/util/TrackLogUtils.java b/backend/services/issue-management/src/main/java/io/metersphere/issue/util/TrackLogUtils.java
new file mode 100644
index 0000000000..33a8e8dc6f
--- /dev/null
+++ b/backend/services/issue-management/src/main/java/io/metersphere/issue/util/TrackLogUtils.java
@@ -0,0 +1,93 @@
+package io.metersphere.track.util;
+
+import io.metersphere.sdk.util.LogUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TrackLogUtils extends LogUtils {
+
+ /**
+ * 初始化日志
+ *
+ * @return
+ */
+ public static Logger getLogger() {
+ return LoggerFactory.getLogger("test-track");
+ }
+
+ public static void writeLog(Object msg, String level) {
+ writeLog(msg, level, getLogger());
+ }
+
+ public static void info(Object msg) {
+ info(msg, getLogger());
+ }
+
+ public static void info(Object msg, Object o1) {
+ info(msg, o1, getLogger().atError());
+ }
+
+ public static void info(Object msg, Object o1, Object o2) {
+ info(msg, o1, o2, getLogger());
+ }
+
+ public static void info(Object msg, Object[] obj) {
+ info(msg, obj, getLogger());
+ }
+
+ public static void debug(Object msg) {
+ debug(msg, getLogger());
+ }
+
+ public static void debug(Object msg, Object o) {
+ debug(msg, o, getLogger());
+ }
+
+ public static void debug(Object msg, Object o1, Object o2) {
+ debug(msg, o1, o2, getLogger());
+ }
+
+ public static void debug(Object msg, Object[] obj) {
+ debug(msg, obj, getLogger());
+ }
+
+ public static void warn(Object msg) {
+ warn(msg, getLogger());
+ }
+
+ public static void warn(Object msg, Object o) {
+ warn(msg, o, getLogger());
+ }
+
+ public static void warn(Object msg, Object o1, Object o2) {
+ warn(msg, o1, o2, getLogger());
+ }
+
+ public static void warn(Object msg, Object[] obj) {
+ warn(msg, obj, getLogger());
+ }
+
+ public static void error(Object msg) {
+ error(msg, getLogger());
+ }
+
+ public static void error(Throwable e) {
+ error(e, getLogger());
+ }
+
+ public static void error(Object msg, Object o) {
+ error(msg, o, getLogger());
+ }
+
+ public static void error(Object msg, Object o1, Object o2) {
+ error(msg, o1, o2, getLogger());
+ }
+
+ public static void error(Object msg, Object[] obj) {
+ error(msg, obj, getLogger());
+ }
+
+ public static void error(Object msg, Throwable ex) {
+ error(msg, ex, getLogger());
+ }
+}
diff --git a/backend/services/issue-management/src/main/resources/i18n/issue.properties b/backend/services/issue-management/src/main/resources/i18n/issue.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/backend/services/issue-management/src/main/resources/i18n/issue_en_US.properties b/backend/services/issue-management/src/main/resources/i18n/issue_en_US.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/backend/services/issue-management/src/main/resources/i18n/issue_zh_CN.properties b/backend/services/issue-management/src/main/resources/i18n/issue_zh_CN.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/backend/services/issue-management/src/main/resources/i18n/issue_zh_TW.properties b/backend/services/issue-management/src/main/resources/i18n/issue_zh_TW.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/backend/services/load-test/src/main/java/io/metersphere/load/domain/LoadTest.java b/backend/services/load-test/src/main/java/io/metersphere/load/domain/LoadTest.java
index 209aba0931..f949f64959 100644
--- a/backend/services/load-test/src/main/java/io/metersphere/load/domain/LoadTest.java
+++ b/backend/services/load-test/src/main/java/io/metersphere/load/domain/LoadTest.java
@@ -22,17 +22,11 @@ public class LoadTest implements Serializable {
private String testResourcePoolId;
- private String userId;
-
private Integer num;
private String createUser;
- private Integer scenarioVersion;
-
- private String scenarioId;
-
- private Long order;
+ private Long pos;
private String versionId;
diff --git a/backend/services/pom.xml b/backend/services/pom.xml
index cea307cb79..01ad486042 100644
--- a/backend/services/pom.xml
+++ b/backend/services/pom.xml
@@ -15,6 +15,7 @@
api-test
+ issue-management
load-test
project-management
system-setting
diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectController.java
new file mode 100644
index 0000000000..9baebee4d0
--- /dev/null
+++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/ProjectController.java
@@ -0,0 +1,22 @@
+package io.metersphere.project.controller;
+
+import io.metersphere.project.domain.Project;
+import io.metersphere.project.service.ProjectService;
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("project")
+public class ProjectController {
+ @Resource
+ private ProjectService projectService;
+
+ @GetMapping("list-all")
+ public List selectAll() {
+ return projectService.list();
+ }
+}
diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/domain/Project.java b/backend/services/project-management/src/main/java/io/metersphere/project/domain/Project.java
index 95dbcd52cf..bfc9c54224 100644
--- a/backend/services/project-management/src/main/java/io/metersphere/project/domain/Project.java
+++ b/backend/services/project-management/src/main/java/io/metersphere/project/domain/Project.java
@@ -18,33 +18,7 @@ public class Project implements Serializable {
private Long updateTime;
- private String tapdId;
-
- private String jiraKey;
-
- private String zentaoId;
-
- private String azureDevopsId;
-
- private String caseTemplateId;
-
- private String issueTemplateId;
-
- private String createUser;
-
private String systemId;
- private String azureFilterId;
-
- private String platform;
-
- private Boolean thirdPartTemplate;
-
- private Boolean versionEnable;
-
- private String issueConfig;
-
- private String apiTemplateId;
-
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/mapper/ProjectMapper.java b/backend/services/project-management/src/main/java/io/metersphere/project/mapper/ProjectMapper.java
new file mode 100644
index 0000000000..61c490c9ae
--- /dev/null
+++ b/backend/services/project-management/src/main/java/io/metersphere/project/mapper/ProjectMapper.java
@@ -0,0 +1,7 @@
+package io.metersphere.project.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.metersphere.project.domain.Project;
+
+public interface ProjectMapper extends BaseMapper {
+}
diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java
new file mode 100644
index 0000000000..e61de515ba
--- /dev/null
+++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/ProjectService.java
@@ -0,0 +1,13 @@
+package io.metersphere.project.service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.metersphere.project.domain.Project;
+import io.metersphere.project.mapper.ProjectMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class ProjectService extends ServiceImpl {
+
+}
diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/Application.java b/backend/services/project-management/src/test/java/io/metersphere/project/Application.java
new file mode 100644
index 0000000000..0432f9ae3a
--- /dev/null
+++ b/backend/services/project-management/src/test/java/io/metersphere/project/Application.java
@@ -0,0 +1,23 @@
+package io.metersphere.project;
+
+import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+
+@SpringBootApplication(exclude = {
+ QuartzAutoConfiguration.class,
+ LdapAutoConfiguration.class,
+ Neo4jAutoConfiguration.class,
+ MybatisAutoConfiguration.class
+})
+@ServletComponentScan
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/config/MybatisConfig.java b/backend/services/project-management/src/test/java/io/metersphere/project/config/MybatisConfig.java
new file mode 100644
index 0000000000..d340efa07e
--- /dev/null
+++ b/backend/services/project-management/src/test/java/io/metersphere/project/config/MybatisConfig.java
@@ -0,0 +1,82 @@
+package io.metersphere.project.config;
+
+import com.fit2cloud.quartz.anno.QuartzDataSource;
+import com.github.pagehelper.PageInterceptor;
+import com.zaxxer.hikari.HikariDataSource;
+import io.metersphere.sdk.interceptor.MybatisInterceptor;
+import io.metersphere.sdk.interceptor.UserDesensitizationInterceptor;
+import io.metersphere.sdk.util.MybatisInterceptorConfig;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+@Configuration
+@MapperScan(basePackages = {"io.metersphere.*.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
+@EnableTransactionManagement
+public class MybatisConfig {
+ @Bean
+ @ConditionalOnMissingBean
+ public PageInterceptor pageInterceptor() {
+ PageInterceptor pageInterceptor = new PageInterceptor();
+ Properties properties = new Properties();
+ properties.setProperty("helperDialect", "mysql");
+ properties.setProperty("rowBoundsWithCount", "true");
+ properties.setProperty("reasonable", "true");
+ properties.setProperty("offsetAsPageNum", "true");
+ properties.setProperty("pageSizeZero", "true");
+ pageInterceptor.setProperties(properties);
+ return pageInterceptor;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public MybatisInterceptor dbInterceptor() {
+ MybatisInterceptor interceptor = new MybatisInterceptor();
+ List configList = new ArrayList<>();
+// configList.add(new MybatisInterceptorConfig(FileContent.class, "file", CompressUtils.class, "zip", "unzip"));
+ interceptor.setInterceptorConfigList(configList);
+ return interceptor;
+ }
+
+ @Bean
+ public UserDesensitizationInterceptor userDesensitizationInterceptor() {
+ return new UserDesensitizationInterceptor();
+ }
+
+
+ @Bean
+ @Primary
+ @ConfigurationProperties(prefix = "spring.datasource.hikari")
+ public DataSource dataSource(DataSourceProperties properties) {
+ return DataSourceBuilder.create(properties.getClassLoader()).type(HikariDataSource.class)
+ .driverClassName(properties.determineDriverClassName())
+ .url(properties.determineUrl())
+ .username(properties.determineUsername())
+ .password(properties.determinePassword())
+ .build();
+ }
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource.quartz.hikari")
+ @QuartzDataSource
+ public DataSource quartzDataSource(DataSourceProperties properties) {
+ return DataSourceBuilder.create(properties.getClassLoader()).type(HikariDataSource.class)
+ .driverClassName(properties.determineDriverClassName())
+ .url(properties.determineUrl())
+ .username(properties.determineUsername())
+ .password(properties.determinePassword())
+ .build();
+ }
+
+}
diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java
new file mode 100644
index 0000000000..d2f86440f3
--- /dev/null
+++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/ProjectControllerTests.java
@@ -0,0 +1,31 @@
+package io.metersphere.project.controller;
+
+import jakarta.annotation.Resource;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+
+@SpringBootTest
+@AutoConfigureMockMvc
+public class ProjectControllerTests {
+ @Resource
+ private MockMvc mockMvc;
+
+ @Test
+ public void testSelectAll() throws Exception {
+
+ mockMvc.perform(MockMvcRequestBuilders.get("/project/list-all"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+// .andExpect(jsonPath("$.person.name").value("Jason"))
+ .andDo(print());
+ }
+}
diff --git a/backend/services/project-management/src/test/resources/application.properties b/backend/services/project-management/src/test/resources/application.properties
new file mode 100644
index 0000000000..14120a7a41
--- /dev/null
+++ b/backend/services/project-management/src/test/resources/application.properties
@@ -0,0 +1,76 @@
+spring.application.name=metersphere
+management.server.port=7071
+server.port=8081
+# ?? gzip ??
+server.compression.enabled=true
+server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css,text/javascript,image/jpeg
+server.compression.min-response-size=2048
+#
+quartz.enabled=false
+quartz.scheduler-name=msScheduler
+quartz.thread-count=10
+quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true
+#
+logging.file.path=/opt/metersphere/logs/metersphere
+# Hikari
+spring.datasource.url=jdbc:mysql://localhost:${embedded.mysql.port}/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
+spring.datasource.username=${embedded.mysql.user}
+spring.datasource.password=${embedded.mysql.password}
+spring.datasource.type=com.zaxxer.hikari.HikariDataSource
+spring.datasource.hikari.maximum-pool-size=100
+spring.datasource.hikari.minimum-idle=10
+spring.datasource.hikari.idle-timeout=300000
+spring.datasource.hikari.auto-commit=true
+spring.datasource.hikari.pool-name=DatebookHikariCP
+spring.datasource.hikari.max-lifetime=1800000
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.connection-test-query=SELECT 1
+
+#
+# spring.kafka
+spring.kafka.bootstrap-servers=${kafka.bootstrap-servers}
+spring.kafka.consumer.group-id=metersphere_group_id
+spring.kafka.consumer.debug.group-id=metersphere_group_id_${random.uuid}
+spring.kafka.listener.missing-topics-fatal=false
+spring.kafka.producer.properties.max.request.size=32428800
+spring.kafka.producer.batch-size=16384
+spring.kafka.consumer.properties.max.partition.fetch.bytes=52428800
+# mybatis
+mybatis.configuration.cache-enabled=true
+mybatis.configuration.lazy-loading-enabled=false
+mybatis.configuration.aggressive-lazy-loading=true
+mybatis.configuration.multiple-result-sets-enabled=true
+mybatis.configuration.use-column-label=true
+mybatis.configuration.auto-mapping-behavior=full
+mybatis.configuration.default-statement-timeout=25000
+mybatis.configuration.map-underscore-to-camel-case=true
+# view
+spring.mvc.throw-exception-if-no-handler-found=true
+# flyway enable
+spring.flyway.enabled=true
+spring.flyway.baseline-on-migrate=true
+spring.flyway.locations=filesystem:../../app/src/main/resources/migration
+spring.flyway.table=metersphere_version
+spring.flyway.baseline-version=0
+spring.flyway.encoding=UTF-8
+spring.flyway.validate-on-migrate=false
+# jmeter
+jmeter.home=/opt/jmeter
+# file upload
+spring.servlet.multipart.max-file-size=500MB
+spring.servlet.multipart.max-request-size=500MB
+# i18n
+spring.messages.basename=i18n/messages,i18n/api,i18n/load,i18n/project,i18n/system,i18n/plan,i18n/track,i18n/ui,i18n/workstation
+# actuator
+management.endpoints.web.exposure.include=*
+management.endpoints.enabled-by-default=false
+# redis
+spring.session.timeout=43200s
+spring.data.redis.host=localhost
+spring.data.redis.password=${embedded.redis.password}
+spring.data.redis.port=${embedded.redis.port}
+spring.session.redis.repository-type=indexed
+#
+spring.freemarker.check-template-location=false
+spring.groovy.template.check-template-location=false
+#
diff --git a/backend/services/project-management/src/test/resources/bootstrap.properties b/backend/services/project-management/src/test/resources/bootstrap.properties
new file mode 100644
index 0000000000..38cc3b010b
--- /dev/null
+++ b/backend/services/project-management/src/test/resources/bootstrap.properties
@@ -0,0 +1,9 @@
+# embedded config
+embedded.containers.enabled=true
+embedded.containers.forceShutdown=true
+# mysql
+embedded.mysql.enabled=true
+embedded.mysql.encoding=utf8mb4
+embedded.mysql.collation=utf8mb4_general_ci
+# redis
+embedded.redis.enabled=true
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 18a518388d..82fc3b578f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,7 @@
6.5.0.202303070854-r
5.4.0
22.3.1
+ 2.3.2
1.12.1
v16.10.0
@@ -121,6 +122,39 @@
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+ 4.0.2
+ test
+
+
+ com.playtika.testcontainers
+ embedded-mysql
+ ${embedded.version}
+ test
+
+
+ com.playtika.testcontainers
+ embedded-redis
+ ${embedded.version}
+ test
+
+
+
+