From 201d9ca05446fcb41ca4eb5bba15b533888f2709 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Tue, 24 Dec 2024 15:18:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/deploy/dto/EnvironmentDTO.java | 6 - .../backend/deploy/dto/ProjectGroupDTO.java | 6 + .../backend/deploy/entity/Environment.java | 14 -- .../backend/deploy/entity/ProjectGroup.java | 15 +- .../deploy/enums/JenkinsBuildStatus.java | 10 + .../deploy/enums/ProjectGroupTypeEnum.java | 19 ++ .../IJenkinsServiceIntegration.java | 19 ++ .../integration/impl/JenkinsIntegration.java | 58 ----- .../impl/JenkinsServiceIntegration.java | 221 ++++++++++++++++++ .../response/JenkinsBuildInfoResponse.java | 58 +++++ .../response/JenkinsCrumbIssuerResponse.java | 12 + .../JenkinsQueueBuildInfoResponse.java | 26 +++ .../service/impl/EnvironmentServiceImpl.java | 15 +- .../impl/ExternalSystemServiceImpl.java | 2 +- .../workflow/delegate/DeployNodeDelegate.java | 124 ++++++++++ .../delegate/JenkinsJobTriggerDelegate.java | 176 -------------- ...bles.java => DeployNodeFormVariables.java} | 2 +- ...les.java => DeployNodeLocalVariables.java} | 4 +- .../DeployNodePanelVariables.java | 14 ++ .../JenkinsJobTriggerNodePanelVariables.java | 35 --- .../backend/workflow/enums/NodeTypeEnums.java | 24 +- .../db/migration/V1.0.0__init_schema.sql | 51 ++-- .../db/migration/V1.0.1__init_data.sql | 64 ++--- 23 files changed, 609 insertions(+), 366 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/JenkinsBuildStatus.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/ProjectGroupTypeEnum.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IJenkinsServiceIntegration.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsIntegration.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsServiceIntegration.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsBuildInfoResponse.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsCrumbIssuerResponse.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsQueueBuildInfoResponse.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/DeployNodeDelegate.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsJobTriggerDelegate.java rename backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/{JenkinsJobTriggerNodeFormVariables.java => DeployNodeFormVariables.java} (95%) rename backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/{JenkinsJobTriggerNodeLocalVariables.java => DeployNodeLocalVariables.java} (74%) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/DeployNodePanelVariables.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/JenkinsJobTriggerNodePanelVariables.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java index db4cfdd2..104aa9b3 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java @@ -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; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ProjectGroupDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ProjectGroupDTO.java index bbf0c74e..005de4d0 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ProjectGroupDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ProjectGroupDTO.java @@ -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 environments; + private List applications; + @NotBlank(message = "项目组状态不能为空") private String projectGroupStatus; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Environment.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Environment.java index d331c035..0a53ba41 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Environment.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Environment.java @@ -43,20 +43,6 @@ public class Environment extends Entity { @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; - /** * 排序号 */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/ProjectGroup.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/ProjectGroup.java index 34215b68..ff76b236 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/ProjectGroup.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/ProjectGroup.java @@ -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 { /** @@ -27,12 +28,17 @@ public class ProjectGroup extends Entity { @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 { inverseJoinColumns = @JoinColumn(name = "environment_id") ) private List environments = new ArrayList<>(); + + + @OneToMany(fetch = FetchType.LAZY) + @JoinColumn(name = "project_group_id") + private List applications = new ArrayList<>(); } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/JenkinsBuildStatus.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/JenkinsBuildStatus.java new file mode 100644 index 00000000..da75517b --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/JenkinsBuildStatus.java @@ -0,0 +1,10 @@ +package com.qqchen.deploy.backend.deploy.enums; + +// Jenkins构建状态枚举 +public enum JenkinsBuildStatus { + SUCCESS, // 构建成功 + FAILURE, // 构建失败 + IN_PROGRESS,// 构建中 + ABORTED, // 构建被取消 + NOT_FOUND // 构建不存在 +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/ProjectGroupTypeEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/ProjectGroupTypeEnum.java new file mode 100644 index 00000000..3cf5fbd1 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/ProjectGroupTypeEnum.java @@ -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; + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IJenkinsServiceIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IJenkinsServiceIntegration.java new file mode 100644 index 00000000..b93ee81e --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IJenkinsServiceIntegration.java @@ -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); + +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsIntegration.java deleted file mode 100644 index 665ada09..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsIntegration.java +++ /dev/null @@ -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 entity = new HttpEntity<>(headers); - - ResponseEntity 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; - } -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsServiceIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsServiceIntegration.java new file mode 100644 index 00000000..721b1815 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/JenkinsServiceIntegration.java @@ -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 entity = new HttpEntity<>(headers); + + ResponseEntity 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 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 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 entity = new HttpEntity<>(headers); + String url = externalSystem.getUrl() + String.format("/job/%s/buildWithParameters", "scp-meta"); + ResponseEntity 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> response = restTemplate.exchange(queueUrl, HttpMethod.GET, createHttpEntity(externalSystem), new ParameterizedTypeReference<>() { + }); + + Map 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 executable = (Map) 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> 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 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; + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsBuildInfoResponse.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsBuildInfoResponse.java new file mode 100644 index 00000000..2e72a73b --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsBuildInfoResponse.java @@ -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; +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsCrumbIssuerResponse.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsCrumbIssuerResponse.java new file mode 100644 index 00000000..fa5d95ac --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsCrumbIssuerResponse.java @@ -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; + +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsQueueBuildInfoResponse.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsQueueBuildInfoResponse.java new file mode 100644 index 00000000..f457306c --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/JenkinsQueueBuildInfoResponse.java @@ -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; +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java index e7c2cf90..385ea13a 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java @@ -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 getProjectEnvironments(Long projectGroupId) { QEnvironment environment = QEnvironment.environment; JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); - + List environments = queryFactory .select(environment) .from(environment) @@ -43,8 +52,8 @@ public class EnvironmentServiceImpl extends BaseServiceImpl { + + @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 outputMap = new ConcurrentHashMap<>(); + + private static final Map errorMap = new ConcurrentHashMap<>(); + + @Override + protected Class getPanelVariablesClass() { + return DeployNodePanelVariables.class; + } + + @Override + protected Class 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)); + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsJobTriggerDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsJobTriggerDelegate.java deleted file mode 100644 index 4c88fbc4..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsJobTriggerDelegate.java +++ /dev/null @@ -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 { - - @Resource - private ApplicationEventPublisher eventPublisher; - - // 用于存储实时输出的Map - private static final Map outputMap = new ConcurrentHashMap<>(); - - private static final Map errorMap = new ConcurrentHashMap<>(); - - @Override - protected Class getPanelVariablesClass() { - return ScriptNodePanelVariables.class; - } - - @Override - protected Class 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); - } - } -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/JenkinsJobTriggerNodeFormVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/DeployNodeFormVariables.java similarity index 95% rename from backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/JenkinsJobTriggerNodeFormVariables.java rename to backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/DeployNodeFormVariables.java index c93e4963..a0f49443 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/JenkinsJobTriggerNodeFormVariables.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/fromVariables/DeployNodeFormVariables.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) -public class JenkinsJobTriggerNodeFormVariables extends BaseNodeFormVariables { +public class DeployNodeFormVariables extends BaseNodeFormVariables { @SchemaProperty( title = "项目", diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/JenkinsJobTriggerNodeLocalVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/DeployNodeLocalVariables.java similarity index 74% rename from backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/JenkinsJobTriggerNodeLocalVariables.java rename to backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/DeployNodeLocalVariables.java index 3632e531..74deb307 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/JenkinsJobTriggerNodeLocalVariables.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/localVariables/DeployNodeLocalVariables.java @@ -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; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/DeployNodePanelVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/DeployNodePanelVariables.java new file mode 100644 index 00000000..ab2a6a61 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/DeployNodePanelVariables.java @@ -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 { + + +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/JenkinsJobTriggerNodePanelVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/JenkinsJobTriggerNodePanelVariables.java deleted file mode 100644 index 485fb561..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/definition/node/panelVariables/JenkinsJobTriggerNodePanelVariables.java +++ /dev/null @@ -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; - -} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java index 7b45caf2..8d7aeabe 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java @@ -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 ); // diff --git a/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql b/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql index 4960b65f..f7a836a4 100644 --- a/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql +++ b/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql @@ -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 '排序号', -- 基础字段 diff --git a/backend/src/main/resources/db/migration/V1.0.1__init_data.sql b/backend/src/main/resources/db/migration/V1.0.1__init_data.sql index 11f8fc59..13e4f156 100644 --- a/backend/src/main/resources/db/migration/V1.0.1__init_data.sql +++ b/backend/src/main/resources/db/migration/V1.0.1__init_data.sql @@ -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 -); \ No newline at end of file +# 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 +# ); \ No newline at end of file