反序列化问题。
This commit is contained in:
parent
7ad20a0cbd
commit
201d9ca054
@ -31,12 +31,6 @@ public class EnvironmentDTO extends BaseDTO {
|
||||
@Schema(description = "环境描述")
|
||||
private String envDesc;
|
||||
|
||||
@Schema(description = "构建方式:JENKINS-Jenkins构建, GITLAB_RUNNER-GitLab Runner构建, GITHUB_ACTION-GitHub Action构建")
|
||||
private BuildTypeEnum buildType;
|
||||
|
||||
@Schema(description = "部署方式:K8S-Kubernetes集群部署, DOCKER-Docker容器部署, VM-虚拟机部署")
|
||||
private DeployTypeEnum deployType;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
@NotNull(message = "排序号不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.qqchen.deploy.backend.deploy.dto;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.ProjectGroupTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@ -15,6 +16,9 @@ public class ProjectGroupDTO extends BaseDTO {
|
||||
// @NotNull(message = "租户ID不能为空")
|
||||
private Long tenantCode;
|
||||
|
||||
@NotNull(message = "项目组类型不能为空")
|
||||
private ProjectGroupTypeEnum type;
|
||||
|
||||
@NotBlank(message = "项目组编码不能为空")
|
||||
private String projectGroupCode;
|
||||
|
||||
@ -25,6 +29,8 @@ public class ProjectGroupDTO extends BaseDTO {
|
||||
|
||||
private List<EnvironmentDTO> environments;
|
||||
|
||||
private List<ApplicationDTO> applications;
|
||||
|
||||
@NotBlank(message = "项目组状态不能为空")
|
||||
private String projectGroupStatus;
|
||||
|
||||
|
||||
@ -43,20 +43,6 @@ public class Environment extends Entity<Long> {
|
||||
@Column(name = "env_desc")
|
||||
private String envDesc;
|
||||
|
||||
/**
|
||||
* 构建方式
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "build_type")
|
||||
private BuildTypeEnum buildType;
|
||||
|
||||
/**
|
||||
* 部署方式
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "deploy_type")
|
||||
private DeployTypeEnum deployType;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.qqchen.deploy.backend.deploy.entity;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.ProjectGroupTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.domain.Entity;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
@ -17,8 +18,8 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@jakarta.persistence.Entity
|
||||
@Table(name = "deploy_project_group")
|
||||
@SQLDelete(sql = "UPDATE deploy_project_group SET deleted = true WHERE id = ?; DELETE FROM deploy_project_group_environment WHERE project_group_id = ?")
|
||||
@Where(clause = "deleted = false")
|
||||
//@SQLDelete(sql = "UPDATE deploy_project_group SET deleted = TRUE WHERE id = ?; DELETE FROM deploy_project_group_environment WHERE project_group_id = ?")
|
||||
//@Where(clause = "deleted = false")
|
||||
public class ProjectGroup extends Entity<Long> {
|
||||
|
||||
/**
|
||||
@ -27,12 +28,17 @@ public class ProjectGroup extends Entity<Long> {
|
||||
@Column(name = "tenant_code")
|
||||
private Long tenantCode;
|
||||
|
||||
@Column(name = "type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ProjectGroupTypeEnum type;
|
||||
|
||||
/**
|
||||
* 项目组编码
|
||||
*/
|
||||
@Column(name = "project_group_code", nullable = false)
|
||||
private String projectGroupCode;
|
||||
|
||||
|
||||
/**
|
||||
* 项目组名称
|
||||
*/
|
||||
@ -67,4 +73,9 @@ public class ProjectGroup extends Entity<Long> {
|
||||
inverseJoinColumns = @JoinColumn(name = "environment_id")
|
||||
)
|
||||
private List<Environment> environments = new ArrayList<>();
|
||||
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "project_group_id")
|
||||
private List<Application> applications = new ArrayList<>();
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.qqchen.deploy.backend.deploy.enums;
|
||||
|
||||
// Jenkins构建状态枚举
|
||||
public enum JenkinsBuildStatus {
|
||||
SUCCESS, // 构建成功
|
||||
FAILURE, // 构建失败
|
||||
IN_PROGRESS,// 构建中
|
||||
ABORTED, // 构建被取消
|
||||
NOT_FOUND // 构建不存在
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.qqchen.deploy.backend.deploy.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ProjectGroupTypeEnum {
|
||||
|
||||
PRODUCT("PRODUCT", "产品"),
|
||||
PROJECT("PROJECT", "项目");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String description;
|
||||
|
||||
ProjectGroupTypeEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildInfoResponse;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsQueueBuildInfoResponse;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsCrumbIssuerResponse;
|
||||
|
||||
public interface IJenkinsServiceIntegration extends IExternalSystemIntegration {
|
||||
|
||||
|
||||
JenkinsCrumbIssuerResponse getJenkinsCrumbIssue();
|
||||
|
||||
String buildWithParameters();
|
||||
|
||||
JenkinsQueueBuildInfoResponse getQueuedBuildInfo(String queueId);
|
||||
|
||||
JenkinsBuildStatus getBuildStatus(String jobName, Integer buildNumber);
|
||||
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.integration.IExternalSystemIntegration;
|
||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||
import com.qqchen.deploy.backend.system.enums.ExternalSystemTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class JenkinsIntegration implements IExternalSystemIntegration {
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public boolean testConnection(ExternalSystem system) {
|
||||
try {
|
||||
String url = system.getUrl() + "/api/json";
|
||||
HttpHeaders headers = createHeaders(system);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
String.class
|
||||
);
|
||||
|
||||
return response.getStatusCode() == HttpStatus.OK;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to connect to Jenkins: {}", system.getUrl(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHeaders createHeaders(ExternalSystem system) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
switch (system.getAuthType()) {
|
||||
case BASIC -> {
|
||||
String auth = system.getUsername() + ":" + system.getPassword();
|
||||
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||
headers.set("Authorization", "Basic " + new String(encodedAuth));
|
||||
}
|
||||
case TOKEN -> headers.set("Authorization", "Bearer " + system.getToken());
|
||||
case OAUTH -> headers.set("Authorization", "Bearer " + system.getToken());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalSystemTypeEnum getSystemType() {
|
||||
return ExternalSystemTypeEnum.JENKINS;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,221 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildInfoResponse;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsQueueBuildInfoResponse;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsCrumbIssuerResponse;
|
||||
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
|
||||
import com.qqchen.deploy.backend.system.enums.ExternalSystemTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
|
||||
|
||||
@Resource
|
||||
private IExternalSystemRepository systemRepository;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public boolean testConnection(ExternalSystem system) {
|
||||
try {
|
||||
String url = system.getUrl() + "/api/json";
|
||||
HttpHeaders headers = createHeaders(system);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
String.class
|
||||
);
|
||||
|
||||
return response.getStatusCode() == HttpStatus.OK;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to connect to Jenkins: {}", system.getUrl(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHeaders createHeaders(ExternalSystem system) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
switch (system.getAuthType()) {
|
||||
case BASIC -> {
|
||||
String auth = system.getUsername() + ":" + system.getPassword();
|
||||
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||
headers.set("Authorization", "Basic " + new String(encodedAuth));
|
||||
}
|
||||
case TOKEN -> headers.set("Authorization", "Bearer " + system.getToken());
|
||||
case OAUTH -> headers.set("Authorization", "Bearer " + system.getToken());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
private HttpEntity<String> createHttpEntity(ExternalSystem jenkins) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
// 根据认证类型设置认证信息
|
||||
switch (jenkins.getAuthType()) {
|
||||
case BASIC -> {
|
||||
// Basic认证
|
||||
String auth = jenkins.getUsername() + ":" + jenkins.getPassword();
|
||||
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||
String authHeader = "Basic " + new String(encodedAuth);
|
||||
headers.set("Authorization", authHeader);
|
||||
}
|
||||
case TOKEN -> {
|
||||
// Token认证
|
||||
headers.set("Authorization", "Bearer " + jenkins.getToken());
|
||||
}
|
||||
default -> throw new RuntimeException("Unsupported authentication type: " + jenkins.getAuthType());
|
||||
}
|
||||
|
||||
// 设置接受JSON响应
|
||||
headers.set("Accept", "application/json");
|
||||
|
||||
return new HttpEntity<>(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalSystemTypeEnum getSystemType() {
|
||||
return ExternalSystemTypeEnum.JENKINS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JenkinsCrumbIssuerResponse getJenkinsCrumbIssue() {
|
||||
ExternalSystem externalSystem = systemRepository.findById(1L).orElseThrow(() -> new RuntimeException("没有找到三方系统"));
|
||||
String url = externalSystem.getUrl() + "/crumbIssuer/api/json";
|
||||
HttpHeaders headers = createHeaders(externalSystem);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
return convertResponse(restTemplate.exchange(url, HttpMethod.GET, entity, String.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildWithParameters() {
|
||||
JenkinsCrumbIssuerResponse jenkinsCrumbIssue = getJenkinsCrumbIssue();
|
||||
ExternalSystem externalSystem = systemRepository.findById(1L).orElseThrow(() -> new RuntimeException("没有找到三方系统"));
|
||||
HttpHeaders headers = createHeaders(externalSystem);
|
||||
headers.set("Jenkins-Crumb", jenkinsCrumbIssue.getCrumb());
|
||||
headers.set("Cookie", jenkinsCrumbIssue.getCookie());
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
String url = externalSystem.getUrl() + String.format("/job/%s/buildWithParameters", "scp-meta");
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
|
||||
// 2. 从Location头获取队列ID
|
||||
String location = response.getHeaders().getFirst("Location");
|
||||
if (location == null) {
|
||||
throw new RuntimeException("未获取到构建队列信息");
|
||||
}
|
||||
return extractQueueId(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JenkinsQueueBuildInfoResponse getQueuedBuildInfo(String queueId) {
|
||||
ExternalSystem externalSystem = systemRepository.findById(1L).orElseThrow(() -> new RuntimeException("没有找到三方系统"));
|
||||
String queueUrl = String.format("%s/queue/item/%s/api/json", externalSystem.getUrl().trim(), queueId);
|
||||
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(queueUrl, HttpMethod.GET, createHttpEntity(externalSystem), new ParameterizedTypeReference<>() {
|
||||
});
|
||||
|
||||
Map<String, Object> queueInfo = response.getBody();
|
||||
if (queueInfo == null) {
|
||||
throw new RuntimeException("Failed to get queue information");
|
||||
}
|
||||
|
||||
// 检查是否被取消
|
||||
Boolean cancelled = (Boolean) queueInfo.get("cancelled");
|
||||
if (Boolean.TRUE.equals(cancelled)) {
|
||||
throw new RuntimeException("Build was cancelled in queue");
|
||||
}
|
||||
|
||||
// 检查是否已开始执行
|
||||
Map<String, Object> executable = (Map<String, Object>) queueInfo.get("executable");
|
||||
if (executable != null) {
|
||||
return new JenkinsQueueBuildInfoResponse(
|
||||
((Number) executable.get("number")).intValue(),
|
||||
(String) executable.get("url")
|
||||
);
|
||||
}
|
||||
// 还在队列中
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JenkinsBuildStatus getBuildStatus(String jobName, Integer buildNumber) {
|
||||
try {
|
||||
ExternalSystem externalSystem = systemRepository.findById(1L).orElseThrow(() -> new RuntimeException("没有找到三方系统"));
|
||||
|
||||
String url = String.format("%s/job/%s/%d/api/json", externalSystem.getUrl().trim(), jobName, buildNumber);
|
||||
|
||||
ResponseEntity<Map<String, Object>> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
createHttpEntity(externalSystem),
|
||||
new ParameterizedTypeReference<>() {
|
||||
}
|
||||
);
|
||||
if (response.getBody() == null) {
|
||||
return JenkinsBuildStatus.NOT_FOUND;
|
||||
}
|
||||
String result = (String) response.getBody().get("result");
|
||||
if (result == null) {
|
||||
return JenkinsBuildStatus.IN_PROGRESS;
|
||||
}
|
||||
return switch (result) {
|
||||
case "SUCCESS" -> JenkinsBuildStatus.SUCCESS;
|
||||
case "FAILURE" -> JenkinsBuildStatus.FAILURE;
|
||||
case "ABORTED" -> JenkinsBuildStatus.ABORTED;
|
||||
default -> JenkinsBuildStatus.IN_PROGRESS;
|
||||
};
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to get build status: job={}, buildNumber={}", jobName, buildNumber, e);
|
||||
throw new RuntimeException("Failed to get build status", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String extractQueueId(String location) {
|
||||
// location格式: http://jenkins-url/queue/item/12345/
|
||||
return location.replaceAll(".*/item/(\\d+)/.*", "$1");
|
||||
}
|
||||
|
||||
public JenkinsCrumbIssuerResponse convertResponse(ResponseEntity<String> response) {
|
||||
// 1. 从响应体中解析JSON
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
JsonNode jsonNode;
|
||||
try {
|
||||
jsonNode = objectMapper.readTree(response.getBody());
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("解析Jenkins响应失败", e);
|
||||
}
|
||||
|
||||
// 2. 从响应头中获取cookie
|
||||
String cookie = response.getHeaders().getFirst(HttpHeaders.SET_COOKIE); // 获取SET_COOKIE头的第一个值
|
||||
|
||||
// 3. 构建返回对象
|
||||
JenkinsCrumbIssuerResponse result = new JenkinsCrumbIssuerResponse();
|
||||
result.setCrumb(jsonNode.get("crumb").asText());
|
||||
result.setCookie(cookie);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Jenkins构建信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JenkinsBuildInfoResponse {
|
||||
|
||||
/**
|
||||
* 构建号
|
||||
*/
|
||||
private Integer buildNumber;
|
||||
|
||||
/**
|
||||
* 构建URL
|
||||
*/
|
||||
private String buildUrl;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String jobName;
|
||||
|
||||
/**
|
||||
* 构建状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 持续时间(毫秒)
|
||||
*/
|
||||
private Long duration;
|
||||
|
||||
/**
|
||||
* 构建结果
|
||||
*/
|
||||
private String result;
|
||||
|
||||
/**
|
||||
* 是否在队列中
|
||||
*/
|
||||
private Boolean inQueue;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class JenkinsCrumbIssuerResponse {
|
||||
|
||||
private String crumb;
|
||||
|
||||
private String cookie;
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Jenkins构建信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JenkinsQueueBuildInfoResponse {
|
||||
|
||||
/**
|
||||
* 构建号
|
||||
*/
|
||||
private Integer buildNumber;
|
||||
|
||||
/**
|
||||
* 构建URL
|
||||
*/
|
||||
private String buildUrl;
|
||||
}
|
||||
@ -11,6 +11,8 @@ import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -29,12 +31,19 @@ public class EnvironmentServiceImpl extends BaseServiceImpl<Environment, Environ
|
||||
@Resource
|
||||
private EntityManager entityManager;
|
||||
|
||||
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public EnvironmentDTO create(EnvironmentDTO dto) {
|
||||
// return this.create(dto);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<EnvironmentDTO> getProjectEnvironments(Long projectGroupId) {
|
||||
QEnvironment environment = QEnvironment.environment;
|
||||
|
||||
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
|
||||
|
||||
|
||||
List<Environment> environments = queryFactory
|
||||
.select(environment)
|
||||
.from(environment)
|
||||
@ -43,8 +52,8 @@ public class EnvironmentServiceImpl extends BaseServiceImpl<Environment, Environ
|
||||
.fetch();
|
||||
|
||||
return environments.stream()
|
||||
.map(environmentConverter::toDto)
|
||||
.collect(Collectors.toList());
|
||||
.map(environmentConverter::toDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -101,7 +101,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional
|
||||
public boolean testConnection(Long id) {
|
||||
ExternalSystem system = findEntityById(id);
|
||||
if (!system.getEnabled()) {
|
||||
|
||||
@ -0,0 +1,124 @@
|
||||
package com.qqchen.deploy.backend.workflow.delegate;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsQueueBuildInfoResponse;
|
||||
import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.DeployNodeLocalVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.DeployNodePanelVariables;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.BpmnError;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Shell脚本任务的委派者实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeployNodeDelegate extends BaseNodeDelegate<DeployNodePanelVariables, DeployNodeLocalVariables> {
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Resource
|
||||
private IJenkinsServiceIntegration jenkinsServiceIntegration;
|
||||
|
||||
private static final int QUEUE_POLL_INTERVAL = 10; // 10秒
|
||||
|
||||
private static final int MAX_QUEUE_POLLS = 30; // 最多等待5分钟
|
||||
|
||||
// 轮询间隔(秒)
|
||||
private static final int BUILD_POLL_INTERVAL = 10;
|
||||
|
||||
// 最大轮询次数
|
||||
private static final int MAX_BUILD_POLLS = 180; // 30分钟超时
|
||||
|
||||
// 用于存储实时输出的Map
|
||||
private static final Map<String, StringBuilder> outputMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<String, StringBuilder> errorMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
protected Class<DeployNodePanelVariables> getPanelVariablesClass() {
|
||||
return DeployNodePanelVariables.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<DeployNodeLocalVariables> getLocalVariablesClass() {
|
||||
return DeployNodeLocalVariables.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeInternal(DelegateExecution execution, DeployNodePanelVariables panelVariables, DeployNodeLocalVariables localVariables) {
|
||||
String queueId = jenkinsServiceIntegration.buildWithParameters();
|
||||
JenkinsQueueBuildInfoResponse buildInfo = waitForBuildToStart(queueId);
|
||||
// 3. 轮询构建状态
|
||||
pollBuildStatus("scp-meta", buildInfo.getBuildNumber());
|
||||
}
|
||||
|
||||
private JenkinsQueueBuildInfoResponse waitForBuildToStart(String queueId) {
|
||||
int attempts = 0;
|
||||
while (attempts < MAX_QUEUE_POLLS) {
|
||||
try {
|
||||
JenkinsQueueBuildInfoResponse buildInfo = jenkinsServiceIntegration.getQueuedBuildInfo(queueId);
|
||||
if (buildInfo != null) {
|
||||
return buildInfo;
|
||||
}
|
||||
|
||||
log.debug("Build still in queue, waiting... attempts: {}/{}", attempts + 1, MAX_QUEUE_POLLS);
|
||||
|
||||
Thread.sleep(QUEUE_POLL_INTERVAL * 1000L);
|
||||
attempts++;
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BpmnError("POLLING_INTERRUPTED", "Interrupted while waiting for build to start");
|
||||
}
|
||||
}
|
||||
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, String.format("Build did not start within %d seconds", MAX_QUEUE_POLLS * QUEUE_POLL_INTERVAL));
|
||||
}
|
||||
|
||||
private void pollBuildStatus(String jobName, Integer buildNumber) {
|
||||
int attempts = 0;
|
||||
while (attempts < MAX_BUILD_POLLS) {
|
||||
try {
|
||||
// 等待一定时间后再检查
|
||||
Thread.sleep(BUILD_POLL_INTERVAL * 1000L);
|
||||
|
||||
// 获取构建状态
|
||||
JenkinsBuildStatus status = jenkinsServiceIntegration.getBuildStatus(jobName, buildNumber);
|
||||
log.info("Jenkins build status: job={}, buildNumber={}, status={}", jobName, buildNumber, status);
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
// 构建成功,返回继续流程
|
||||
return;
|
||||
case FAILURE:
|
||||
// 构建失败,抛出错误
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, String.format("Jenkins build failed: job=%s, buildNumber=%d", jobName, buildNumber));
|
||||
case ABORTED:
|
||||
// 构建被取消,抛出错误
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, String.format("Jenkins build was aborted: job=%s, buildNumber=%d", jobName, buildNumber));
|
||||
case IN_PROGRESS:
|
||||
// 继续轮询
|
||||
attempts++;
|
||||
break;
|
||||
case NOT_FOUND:
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, String.format("Jenkins build not found: job=%s, buildNumber=%d", jobName, buildNumber));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Build status polling was interrupted");
|
||||
}
|
||||
}
|
||||
// 超过最大轮询次数,视为超时
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, String.format("Jenkins build timed out after %d minutes: job=%s, buildNumber=%d", MAX_BUILD_POLLS * BUILD_POLL_INTERVAL / 60, jobName, buildNumber));
|
||||
}
|
||||
}
|
||||
@ -1,176 +0,0 @@
|
||||
package com.qqchen.deploy.backend.workflow.delegate;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.ScriptNodeLocalVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.ScriptNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeLogTypeEnums;
|
||||
import com.qqchen.deploy.backend.workflow.event.ShellLogEvent;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.BpmnError;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Shell脚本任务的委派者实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JenkinsJobTriggerDelegate extends BaseNodeDelegate<ScriptNodePanelVariables, ScriptNodeLocalVariables> {
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
// 用于存储实时输出的Map
|
||||
private static final Map<String, StringBuilder> outputMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<String, StringBuilder> errorMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
protected Class<ScriptNodePanelVariables> getPanelVariablesClass() {
|
||||
return ScriptNodePanelVariables.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<ScriptNodeLocalVariables> getLocalVariablesClass() {
|
||||
return ScriptNodeLocalVariables.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeInternal(DelegateExecution execution,
|
||||
ScriptNodePanelVariables panelVariables,
|
||||
ScriptNodeLocalVariables localVariables) {
|
||||
if (panelVariables == null || panelVariables.getScript() == null) {
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Script is required but not provided");
|
||||
}
|
||||
|
||||
// try {
|
||||
// log.info("准备执行脚本: {}", panelVariables.getScript());
|
||||
// // 使用processInstanceId而不是executionId
|
||||
// String processInstanceId = execution.getProcessInstanceId();
|
||||
// outputMap.put(processInstanceId, new StringBuilder());
|
||||
// errorMap.put(processInstanceId, new StringBuilder());
|
||||
//
|
||||
// // 创建进程构建器
|
||||
// ProcessBuilder processBuilder = new ProcessBuilder();
|
||||
//
|
||||
// // 根据操作系统选择合适的shell
|
||||
// String os = System.getProperty("os.name").toLowerCase();
|
||||
// if (os.contains("win")) {
|
||||
// // Windows系统使用cmd
|
||||
// processBuilder.command("cmd", "/c", panelVariables.getScript());
|
||||
// } else {
|
||||
// // Unix-like系统使用bash
|
||||
// processBuilder.command("bash", "-c", panelVariables.getScript());
|
||||
// }
|
||||
//
|
||||
// // 设置工作目录
|
||||
// if (StringUtils.hasText(localVariables.getWorkDir())) {
|
||||
// // Windows系统路径处理
|
||||
// String workDirValue = localVariables.getWorkDir();
|
||||
// if (os.contains("win")) {
|
||||
// // 确保使用Windows风格的路径分隔符
|
||||
// workDirValue = workDirValue.replace("/", "\\");
|
||||
// // 如果路径以\开头,去掉第一个\
|
||||
// if (workDirValue.startsWith("\\")) {
|
||||
// workDirValue = workDirValue.substring(1);
|
||||
// }
|
||||
// }
|
||||
// File workDirFile = new File(workDirValue);
|
||||
// if (!workDirFile.exists()) {
|
||||
// workDirFile.mkdirs();
|
||||
// }
|
||||
// processBuilder.directory(workDirFile);
|
||||
// }
|
||||
//
|
||||
// // 设置环境变量
|
||||
// if (localVariables.getEnv() != null) {
|
||||
// processBuilder.environment().putAll(localVariables.getEnv());
|
||||
// }
|
||||
//
|
||||
// // 执行命令
|
||||
// log.info("执行shell脚本: {}", panelVariables.getScript());
|
||||
// Process process = processBuilder.start();
|
||||
//
|
||||
// // 创建线程池处理输出
|
||||
// ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
//
|
||||
// // 处理标准输出
|
||||
// Future<?> outputFuture = executorService.submit(() ->
|
||||
// processInputStream(process.getInputStream(), processInstanceId, NodeLogTypeEnums.STDOUT));
|
||||
//
|
||||
// // 处理错误输出
|
||||
// Future<?> errorFuture = executorService.submit(() ->
|
||||
// processInputStream(process.getErrorStream(), processInstanceId, NodeLogTypeEnums.STDERR));
|
||||
//
|
||||
// // 等待进程完成
|
||||
// int exitCode = process.waitFor();
|
||||
//
|
||||
// // 等待输出处理完成
|
||||
// outputFuture.get(5, TimeUnit.SECONDS);
|
||||
// errorFuture.get(5, TimeUnit.SECONDS);
|
||||
//
|
||||
// // 关闭线程池
|
||||
// executorService.shutdown();
|
||||
//
|
||||
// // 设置最终结果
|
||||
// StringBuilder finalOutput = outputMap.get(processInstanceId);
|
||||
// StringBuilder finalError = errorMap.get(processInstanceId);
|
||||
//
|
||||
// execution.setVariable("shellOutput", finalOutput.toString());
|
||||
// execution.setVariable("shellError", finalError.toString());
|
||||
// execution.setVariable("shellExitCode", exitCode);
|
||||
//
|
||||
// // 清理缓存
|
||||
// outputMap.remove(processInstanceId);
|
||||
// errorMap.remove(processInstanceId);
|
||||
//
|
||||
// if (exitCode != 0) {
|
||||
// log.error("Shell脚本执行失败,退出码: {}", exitCode);
|
||||
// execution.setVariable("errorDetail", "Shell脚本执行失败,退出码: " + exitCode);
|
||||
// throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Shell脚本执行失败,退出码: " + exitCode);
|
||||
// }
|
||||
// log.info("Shell脚本执行成功");
|
||||
// log.debug("脚本输出: {}", finalOutput);
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.error("Shell脚本执行失败", e);
|
||||
// throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, e.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
private void processInputStream(InputStream inputStream, String processInstanceId, NodeLogTypeEnums logType) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
// 发布日志事件
|
||||
eventPublisher.publishEvent(new ShellLogEvent(processInstanceId, line, logType));
|
||||
|
||||
// 同时保存到StringBuilder中
|
||||
if (logType == NodeLogTypeEnums.STDOUT) {
|
||||
StringBuilder output = outputMap.get(processInstanceId);
|
||||
synchronized (output) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
// log.info("Shell output: {}", line);
|
||||
} else {
|
||||
StringBuilder error = errorMap.get(processInstanceId);
|
||||
synchronized (error) {
|
||||
error.append(line).append("\n");
|
||||
}
|
||||
// log.error("Shell error: {}", line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading process output", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JenkinsJobTriggerNodeFormVariables extends BaseNodeFormVariables {
|
||||
public class DeployNodeFormVariables extends BaseNodeFormVariables {
|
||||
|
||||
@SchemaProperty(
|
||||
title = "项目",
|
||||
@ -6,12 +6,12 @@ import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JenkinsJobTriggerNodeLocalVariables extends BaseNodeLocalVariables {
|
||||
public class DeployNodeLocalVariables extends BaseNodeLocalVariables {
|
||||
|
||||
@SchemaProperty(
|
||||
title = "委派者",
|
||||
description = "委派者",
|
||||
defaultValue = "${jenkinsJobTriggerDelegate}",
|
||||
defaultValue = "${deployNodeDelegate}",
|
||||
required = true
|
||||
)
|
||||
private String delegate;
|
||||
@ -0,0 +1,14 @@
|
||||
package com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 脚本执行器配置
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeployNodePanelVariables extends BaseNodePanelVariables {
|
||||
|
||||
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 脚本执行器配置
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JenkinsJobTriggerNodePanelVariables extends BaseNodePanelVariables {
|
||||
|
||||
// @SchemaProperty(
|
||||
// title = "环境",
|
||||
// description = "环境",
|
||||
// required = true
|
||||
// )
|
||||
// private String environment;
|
||||
//
|
||||
// /**
|
||||
// * 脚本语言
|
||||
// */
|
||||
// @SchemaProperty(
|
||||
// title = "任务名",
|
||||
// description = "任务名",
|
||||
// required = true,
|
||||
// enumNames = {"Shell脚本 (已支持)", "Python脚本 (开发中)", "JavaScript脚本 (开发中)", "Groovy脚本 (开发中)"},
|
||||
// defaultValue = "shell"
|
||||
// )
|
||||
// private String job;
|
||||
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
package com.qqchen.deploy.backend.workflow.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.fromVariables.JenkinsJobTriggerNodeFormVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.fromVariables.DeployNodeFormVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.fromVariables.ScriptNodeFormVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.JenkinsJobTriggerNodeLocalVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.DeployNodeLocalVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.localVariables.ScriptNodeLocalVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.DeployNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.EndNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.JenkinsJobTriggerNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.ScriptNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.panelVariables.StartNodePanelVariables;
|
||||
import com.qqchen.deploy.backend.workflow.dto.definition.node.uiVariables.NodeUiVariables;
|
||||
@ -38,7 +38,7 @@ public enum NodeTypeEnums {
|
||||
NodeUiVariables.class,
|
||||
BpmnNodeTypeEnums.START_EVENT,
|
||||
NodeCategoryEnums.EVENT,
|
||||
"工作流的起点", // 节点简要描述
|
||||
"工作流开始", // 节点简要描述
|
||||
new WorkflowNodeGraph() // UI配置
|
||||
.setShape("circle")
|
||||
.setSize(40, 40)
|
||||
@ -62,7 +62,7 @@ public enum NodeTypeEnums {
|
||||
NodeUiVariables.class,
|
||||
BpmnNodeTypeEnums.END_EVENT,
|
||||
NodeCategoryEnums.EVENT,
|
||||
"工作流的终点",
|
||||
"工作流结束",
|
||||
new WorkflowNodeGraph()
|
||||
.setShape("circle")
|
||||
.setSize(40, 40)
|
||||
@ -85,16 +85,16 @@ public enum NodeTypeEnums {
|
||||
.setStyle("#ffffff", "#1890ff", "code")
|
||||
.configPorts(Arrays.asList("in", "out"))
|
||||
),
|
||||
JENKINS_NODE(
|
||||
"JENKINS_JOB_TRIGGER",
|
||||
"JENKINS任务触发",
|
||||
JenkinsJobTriggerNodeLocalVariables.class,
|
||||
JenkinsJobTriggerNodePanelVariables.class,
|
||||
JenkinsJobTriggerNodeFormVariables.class,
|
||||
DEPLOY_NODE(
|
||||
"DEPLOY_NODE",
|
||||
"构建任务",
|
||||
DeployNodeLocalVariables.class,
|
||||
DeployNodePanelVariables.class,
|
||||
DeployNodeFormVariables.class,
|
||||
NodeUiVariables.class,
|
||||
BpmnNodeTypeEnums.SERVICE_TASK,
|
||||
NodeCategoryEnums.TASK,
|
||||
"JENKINS任务触发",
|
||||
"构建任务",
|
||||
null
|
||||
);
|
||||
//
|
||||
|
||||
@ -542,31 +542,34 @@ CREATE TABLE workflow_log
|
||||
-- 项目组表
|
||||
CREATE TABLE deploy_project_group
|
||||
(
|
||||
-- 基础字段
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
create_by VARCHAR(100) NULL COMMENT '创建人',
|
||||
create_time DATETIME(6) NULL COMMENT '创建时间',
|
||||
update_by VARCHAR(100) NULL COMMENT '更新人',
|
||||
update_time DATETIME(6) NULL COMMENT '更新时间',
|
||||
version INT NOT NULL DEFAULT 1 COMMENT '版本号',
|
||||
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除:0-未删除,1-已删除',
|
||||
-- 基础字段
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
create_by VARCHAR(100) NULL COMMENT '创建人',
|
||||
create_time DATETIME(6) NULL COMMENT '创建时间',
|
||||
update_by VARCHAR(100) NULL COMMENT '更新人',
|
||||
update_time DATETIME(6) NULL COMMENT '更新时间',
|
||||
version INT NOT NULL DEFAULT 1 COMMENT '版本号',
|
||||
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除:0-未删除,1-已删除',
|
||||
|
||||
-- 业务字段
|
||||
tenant_code BIGINT DEFAULT NULL COMMENT '租户CODE',
|
||||
project_group_code VARCHAR(50) NOT NULL COMMENT '项目组编码',
|
||||
project_group_name VARCHAR(100) NOT NULL COMMENT '项目组名称',
|
||||
project_group_desc VARCHAR(255) NULL COMMENT '项目组描述',
|
||||
project_group_status VARCHAR(50) NOT NULL DEFAULT 'ENABLED' COMMENT '项目组状态:ENABLED-启用,DISABLED-禁用',
|
||||
sort INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
-- 业务字段
|
||||
tenant_code BIGINT DEFAULT NULL COMMENT '租户CODE',
|
||||
type VARCHAR(50) NULL COMMENT '项目组类型',
|
||||
project_group_code VARCHAR(50) NOT NULL COMMENT '项目组编码',
|
||||
project_group_name VARCHAR(100) NOT NULL COMMENT '项目组名称',
|
||||
project_group_desc VARCHAR(255) NULL COMMENT '项目组描述',
|
||||
project_group_status VARCHAR(50) NOT NULL DEFAULT 'ENABLED' COMMENT '项目组状态:ENABLED-启用,DISABLED-禁用',
|
||||
sort INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
|
||||
-- 索引
|
||||
INDEX idx_tenant_id (tenant_code) COMMENT '租户ID索引',
|
||||
UNIQUE INDEX uk_project_group_code (tenant_code, project_group_code) COMMENT '租户下项目组编码唯一',
|
||||
-- 索引
|
||||
INDEX idx_tenant_id (tenant_code) COMMENT '租户ID索引',
|
||||
UNIQUE INDEX uk_project_group_code (tenant_code, project_group_code) COMMENT '租户下项目组编码唯一',
|
||||
|
||||
-- 外键约束
|
||||
CONSTRAINT fk_project_tenant FOREIGN KEY (tenant_code)
|
||||
REFERENCES sys_tenant (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目组表';
|
||||
-- 外键约束
|
||||
CONSTRAINT fk_project_tenant FOREIGN KEY (tenant_code)
|
||||
REFERENCES sys_tenant (id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci COMMENT ='项目组表';
|
||||
|
||||
-- 应用表
|
||||
CREATE TABLE deploy_application
|
||||
@ -608,11 +611,11 @@ CREATE TABLE deploy_environment
|
||||
(
|
||||
-- 业务字段
|
||||
tenant_code VARCHAR(50) DEFAULT NULL COMMENT '租户编码',
|
||||
build_type VARCHAR(50) NULL COMMENT '构建方式:JENKINS-Jenkins构建,GITLAB_RUNNER-GitLab Runner构建,GITHUB_ACTION-GitHub Action构建',
|
||||
deploy_type VARCHAR(50) NULL COMMENT '部署方式:K8S-Kubernetes集群部署,DOCKER-Docker容器部署,VM-虚拟机部署',
|
||||
-- build_type VARCHAR(50) NULL COMMENT '构建方式:JENKINS-Jenkins构建,GITLAB_RUNNER-GitLab Runner构建,GITHUB_ACTION-GitHub Action构建',
|
||||
env_code VARCHAR(50) NOT NULL COMMENT '环境编码',
|
||||
env_name VARCHAR(100) NOT NULL COMMENT '环境名称',
|
||||
env_desc VARCHAR(255) NULL COMMENT '环境描述',
|
||||
|
||||
sort INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
|
||||
-- 基础字段
|
||||
|
||||
@ -167,8 +167,8 @@ INSERT INTO sys_external_system (
|
||||
sync_status, last_sync_time, last_connect_time, config
|
||||
) VALUES (
|
||||
1, 'admin', '2023-12-01 00:00:00', 0, 'admin', '2023-12-01 00:00:00', 0,
|
||||
'Jenkins测试环境', 'JENKINS', 'http://jenkins.test.com', '测试环境Jenkins服务器', 1, 1,
|
||||
'BASIC', 'admin', 'password123', NULL,
|
||||
'链宇JENKINS', 'JENKINS', 'https://ly-jenkins.iscmtech.com', '链宇JENKINS', 1, 1,
|
||||
'BASIC', 'admin', 'Lianyu!@#~123456', NULL,
|
||||
'SUCCESS', '2023-12-01 00:00:00', '2023-12-01 00:00:00', '{}'
|
||||
), (
|
||||
2, 'admin', '2023-12-01 00:00:00', 0, 'admin', '2023-12-01 00:00:00', 0,
|
||||
@ -854,35 +854,35 @@ INSERT INTO workflow_definition (
|
||||
-- --------------------------------------------------------------------------------------
|
||||
|
||||
-- 初始化项目组数据
|
||||
INSERT INTO deploy_project_group (id, create_by, create_time, tenant_code, project_group_code, project_group_name, project_group_desc, project_group_status, sort)
|
||||
VALUES
|
||||
(1, 'admin', NOW(), 1, 'DEMO', '示例项目组', '用于演示的项目组', 'ENABLED', 1),
|
||||
(2, 'admin', NOW(), 1, 'PLATFORM', '平台项目组', '平台相关的项目组', 'ENABLED', 2);
|
||||
# INSERT INTO deploy_project_group (id, create_by, create_time, tenant_code, type, project_group_code, project_group_name, project_group_desc, project_group_status, sort)
|
||||
# VALUES
|
||||
# (1, 'admin', NOW(), 1, 'PRODUCT' 'DEMO', '示例项目组', '用于演示的项目组', 'ENABLED', 1),
|
||||
# (2, 'admin', NOW(), 1, 'PRODUCT', 'PLATFORM', '平台项目组', '平台相关的项目组', 'ENABLED', 2);
|
||||
|
||||
-- 初始化应用数据
|
||||
INSERT INTO deploy_application (
|
||||
id, create_by, create_time,
|
||||
project_group_id, app_code, app_name, app_desc, app_status,
|
||||
repo_url, repo_branch, repo_type, build_type, dev_language, dev_framework, sort
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
1, 'admin', NOW(),
|
||||
1, 'DEMO-APP', '示例应用', '用于演示的应用', 'ENABLED',
|
||||
'https://github.com/demo/demo-app.git', 'main', 'GIT', 'MAVEN', 'JAVA', 'SPRING_BOOT', 1
|
||||
),
|
||||
(
|
||||
2, 'admin', NOW(),
|
||||
1, 'DEMO-WEB', '示例前端', '用于演示的前端应用', 'ENABLED',
|
||||
'https://github.com/demo/demo-web.git', 'main', 'GIT', 'NPM', 'NODEJS', 'VUE', 2
|
||||
),
|
||||
(
|
||||
3, 'admin', NOW(),
|
||||
2, 'PLATFORM-API', '平台API', '平台后端服务', 'ENABLED',
|
||||
'https://github.com/platform/platform-api.git', 'main', 'GIT', 'MAVEN', 'JAVA', 'SPRING_BOOT', 1
|
||||
),
|
||||
(
|
||||
4, 'admin', NOW(),
|
||||
2, 'PLATFORM-WEB', '平台前端', '平台前端应用', 'ENABLED',
|
||||
'https://github.com/platform/platform-web.git', 'main', 'GIT', 'NPM', 'NODEJS', 'VUE', 2
|
||||
);
|
||||
# INSERT INTO deploy_application (
|
||||
# id, create_by, create_time,
|
||||
# project_group_id, app_code, app_name, app_desc, app_status,
|
||||
# repo_url, repo_branch, repo_type, build_type, dev_language, dev_framework, sort
|
||||
# )
|
||||
# VALUES
|
||||
# (
|
||||
# 1, 'admin', NOW(),
|
||||
# 1, 'DEMO-APP', '示例应用', '用于演示的应用', 'ENABLED',
|
||||
# 'https://github.com/demo/demo-app.git', 'main', 'GIT', 'MAVEN', 'JAVA', 'SPRING_BOOT', 1
|
||||
# ),
|
||||
# (
|
||||
# 2, 'admin', NOW(),
|
||||
# 1, 'DEMO-WEB', '示例前端', '用于演示的前端应用', 'ENABLED',
|
||||
# 'https://github.com/demo/demo-web.git', 'main', 'GIT', 'NPM', 'NODEJS', 'VUE', 2
|
||||
# ),
|
||||
# (
|
||||
# 3, 'admin', NOW(),
|
||||
# 2, 'PLATFORM-API', '平台API', '平台后端服务', 'ENABLED',
|
||||
# 'https://github.com/platform/platform-api.git', 'main', 'GIT', 'MAVEN', 'JAVA', 'SPRING_BOOT', 1
|
||||
# ),
|
||||
# (
|
||||
# 4, 'admin', NOW(),
|
||||
# 2, 'PLATFORM-WEB', '平台前端', '平台前端应用', 'ENABLED',
|
||||
# 'https://github.com/platform/platform-web.git', 'main', 'GIT', 'NPM', 'NODEJS', 'VUE', 2
|
||||
# );
|
||||
Loading…
Reference in New Issue
Block a user