团队app配置增加绑定工作流
This commit is contained in:
parent
d59d21a062
commit
87b8023e1c
@ -1,5 +1,7 @@
|
||||
package com.qqchen.deploy.backend.deploy.api;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployRequestDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployResultDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.UserDeployableDTO;
|
||||
import com.qqchen.deploy.backend.deploy.service.IDeployService;
|
||||
import com.qqchen.deploy.backend.framework.api.Response;
|
||||
@ -8,9 +10,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 部署管理API控制器
|
||||
@ -36,5 +38,15 @@ public class DeployApiController {
|
||||
public Response<UserDeployableDTO> getDeployableEnvironments() {
|
||||
return Response.success(deployService.getDeployableEnvironments());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行部署
|
||||
*/
|
||||
@Operation(summary = "执行部署", description = "根据团队应用配置启动部署工作流")
|
||||
@PostMapping("/execute")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public Response<DeployResultDTO> executeDeploy(@Validated @RequestBody DeployRequestDTO request) {
|
||||
return Response.success(deployService.executeDeploy(request));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.qqchen.deploy.backend.deploy.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部署请求DTO
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-02
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "部署请求")
|
||||
public class DeployRequestDTO {
|
||||
|
||||
@Schema(description = "团队应用关联ID", required = true)
|
||||
@NotNull(message = "团队应用关联ID不能为空")
|
||||
private Long teamApplicationId;
|
||||
|
||||
@Schema(description = "部署备注")
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package com.qqchen.deploy.backend.deploy.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部署结果DTO
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-02
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "部署结果")
|
||||
public class DeployResultDTO {
|
||||
|
||||
@Schema(description = "工作流实例ID")
|
||||
private Long workflowInstanceId;
|
||||
|
||||
@Schema(description = "业务标识")
|
||||
private String businessKey;
|
||||
|
||||
@Schema(description = "流程实例ID")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "部署状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String message;
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.qqchen.deploy.backend.deploy.service;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployRequestDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployResultDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.UserDeployableDTO;
|
||||
|
||||
/**
|
||||
@ -16,5 +18,13 @@ public interface IDeployService {
|
||||
* @return 用户可部署环境信息
|
||||
*/
|
||||
UserDeployableDTO getDeployableEnvironments();
|
||||
|
||||
/**
|
||||
* 执行部署
|
||||
*
|
||||
* @param request 部署请求
|
||||
* @return 部署结果
|
||||
*/
|
||||
DeployResultDTO executeDeploy(DeployRequestDTO request);
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,17 @@ import com.qqchen.deploy.backend.deploy.entity.*;
|
||||
import com.qqchen.deploy.backend.deploy.repository.*;
|
||||
import com.qqchen.deploy.backend.deploy.service.IDeployService;
|
||||
import com.qqchen.deploy.backend.framework.security.SecurityUtils;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.system.entity.User;
|
||||
import com.qqchen.deploy.backend.system.repository.IUserRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.dto.inputmapping.JenkinsBuildInputMapping;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -56,6 +63,12 @@ public class DeployServiceImpl implements IDeployService {
|
||||
@Resource
|
||||
private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowInstanceService workflowInstanceService;
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public UserDeployableDTO getDeployableEnvironments() {
|
||||
@ -388,5 +401,80 @@ public class DeployServiceImpl implements IDeployService {
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public DeployResultDTO executeDeploy(DeployRequestDTO request) {
|
||||
// 1. 查询团队应用配置
|
||||
TeamApplication teamApp = teamApplicationRepository.findById(request.getTeamApplicationId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.NOT_FOUND));
|
||||
|
||||
// 2. 查询工作流定义(获取 processKey)
|
||||
WorkflowDefinition workflowDefinition = workflowDefinitionRepository.findById(teamApp.getWorkflowDefinitionId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.NOT_FOUND, new Object[]{"工作流定义"}));
|
||||
|
||||
// 3. 查询应用信息
|
||||
Application application = applicationRepository.findById(teamApp.getApplicationId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.NOT_FOUND, new Object[]{"应用"}));
|
||||
|
||||
// 4. 查询环境信息
|
||||
Environment environment = environmentRepository.findById(teamApp.getEnvironmentId())
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.NOT_FOUND, new Object[]{"环境"}));
|
||||
|
||||
// 5. 生成业务标识(UUID)
|
||||
String businessKey = UUID.randomUUID().toString();
|
||||
|
||||
// 6. 构造流程变量
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
// 部署上下文
|
||||
Map<String, Object> deployContext = new HashMap<>();
|
||||
deployContext.put("teamApplicationId", teamApp.getId());
|
||||
deployContext.put("teamId", teamApp.getTeamId());
|
||||
deployContext.put("applicationId", teamApp.getApplicationId());
|
||||
deployContext.put("applicationCode", application.getAppCode());
|
||||
deployContext.put("applicationName", application.getAppName());
|
||||
deployContext.put("environmentId", teamApp.getEnvironmentId());
|
||||
deployContext.put("environmentCode", environment.getEnvCode());
|
||||
deployContext.put("environmentName", environment.getEnvName());
|
||||
deployContext.put("by", SecurityUtils.getCurrentUsername());
|
||||
deployContext.put("remark", request.getRemark());
|
||||
variables.put("deploy", deployContext);
|
||||
|
||||
// Jenkins 配置(使用强类型 JenkinsBuildInputMapping)
|
||||
if (teamApp.getDeploySystemId() != null && teamApp.getDeployJob() != null) {
|
||||
JenkinsBuildInputMapping jenkinsInput = new JenkinsBuildInputMapping();
|
||||
jenkinsInput.setServerId(teamApp.getDeploySystemId());
|
||||
jenkinsInput.setJobName(teamApp.getDeployJob());
|
||||
if (teamApp.getBranch() != null) {
|
||||
jenkinsInput.setBranch(teamApp.getBranch());
|
||||
}
|
||||
|
||||
// 转换为 Map(Flowable 只支持基本类型)
|
||||
variables.put("jenkins", objectMapper.convertValue(jenkinsInput, Map.class));
|
||||
}
|
||||
|
||||
// 7. 构造工作流启动请求
|
||||
WorkflowInstanceStartRequest workflowRequest = new WorkflowInstanceStartRequest();
|
||||
workflowRequest.setProcessKey(workflowDefinition.getKey());
|
||||
workflowRequest.setBusinessKey(businessKey);
|
||||
workflowRequest.setVariables(variables);
|
||||
|
||||
// 8. 启动工作流
|
||||
WorkflowInstanceDTO workflowInstance = workflowInstanceService.startWorkflow(workflowRequest);
|
||||
|
||||
log.info("部署流程已启动: businessKey={}, workflowInstanceId={}, application={}, environment={}",
|
||||
businessKey, workflowInstance.getId(), application.getAppCode(), environment.getEnvCode());
|
||||
|
||||
// 9. 返回结果
|
||||
DeployResultDTO result = new DeployResultDTO();
|
||||
result.setWorkflowInstanceId(workflowInstance.getId());
|
||||
result.setBusinessKey(businessKey);
|
||||
result.setProcessInstanceId(workflowInstance.getProcessInstanceId());
|
||||
result.setStatus(workflowInstance.getStatus().name());
|
||||
result.setMessage("部署流程已启动");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -184,6 +184,8 @@ public abstract class BaseNodeDelegate<I, O> implements JavaDelegate {
|
||||
protected Map<String, Object> resolveExpressions(Map<String, Object> inputMap, DelegateExecution execution) {
|
||||
Map<String, Object> resolvedMap = new HashMap<>();
|
||||
|
||||
log.debug("开始解析 inputMap: {}", inputMap);
|
||||
|
||||
for (Map.Entry<String, Object> entry : inputMap.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
|
||||
@ -193,9 +195,10 @@ public abstract class BaseNodeDelegate<I, O> implements JavaDelegate {
|
||||
try {
|
||||
// 使用简单的变量替换:${sid_xxx.fieldName} -> 从execution中获取
|
||||
String resolvedValue = resolveVariableExpression(strValue, execution);
|
||||
log.debug("解析表达式: {} = {} -> {}", entry.getKey(), strValue, resolvedValue);
|
||||
resolvedMap.put(entry.getKey(), resolvedValue);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to resolve expression: {}, using original value", strValue);
|
||||
log.warn("Failed to resolve expression: {}, using original value", strValue, e);
|
||||
resolvedMap.put(entry.getKey(), value);
|
||||
}
|
||||
} else {
|
||||
@ -206,6 +209,7 @@ public abstract class BaseNodeDelegate<I, O> implements JavaDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("解析后 resolvedMap: {}", resolvedMap);
|
||||
return resolvedMap;
|
||||
}
|
||||
|
||||
@ -240,19 +244,27 @@ public abstract class BaseNodeDelegate<I, O> implements JavaDelegate {
|
||||
private Object resolveVariable(String varExpression, DelegateExecution execution) {
|
||||
String[] parts = varExpression.split("\\.", 2);
|
||||
|
||||
log.debug("解析变量表达式: {}, parts: {}", varExpression, java.util.Arrays.toString(parts));
|
||||
|
||||
if (parts.length == 1) {
|
||||
// 直接变量:${xxx}
|
||||
return execution.getVariable(parts[0]);
|
||||
Object result = execution.getVariable(parts[0]);
|
||||
log.debug("直接变量 {} = {}", parts[0], result);
|
||||
return result;
|
||||
} else {
|
||||
// 对象属性:${sid_xxx.buildNumber}
|
||||
// 对象属性:${sid_xxx.buildNumber} 或 ${jenkins.systemId}
|
||||
String varName = parts[0];
|
||||
String fieldName = parts[1];
|
||||
|
||||
Object varValue = execution.getVariable(varName);
|
||||
log.debug("获取变量 {} = {}, 类型: {}", varName, varValue, varValue != null ? varValue.getClass().getName() : "null");
|
||||
|
||||
if (varValue instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) varValue;
|
||||
return map.get(fieldName);
|
||||
Object fieldValue = map.get(fieldName);
|
||||
log.debug("从 Map 中获取字段 {} = {}", fieldName, fieldValue);
|
||||
return fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -41,24 +41,22 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
||||
private IJenkinsJobRepository jenkinsJobRepository;
|
||||
|
||||
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分钟超时
|
||||
|
||||
@Override
|
||||
protected JenkinsBuildOutputs executeInternal(
|
||||
DelegateExecution execution,
|
||||
Map<String, Object> configs,
|
||||
JenkinsBuildInputMapping input
|
||||
) {
|
||||
log.info("Jenkins Build - serverId: {}, jobName: {}",
|
||||
input.getJenkinsServerId(), input.getProject());
|
||||
protected JenkinsBuildOutputs executeInternal(DelegateExecution execution, Map<String, Object> configs, JenkinsBuildInputMapping input) {
|
||||
log.info("Jenkins Build - serverId: {}, jobName: {}", input.getServerId(), input.getJobName());
|
||||
|
||||
// 1. 获取外部系统
|
||||
ExternalSystem externalSystem = externalSystemRepository.findById(input.getJenkinsServerId().longValue())
|
||||
.orElseThrow(() -> new RuntimeException("Jenkins服务器不存在: " + input.getJenkinsServerId()));
|
||||
ExternalSystem externalSystem = externalSystemRepository.findById(input.getServerId())
|
||||
.orElseThrow(() -> new RuntimeException("Jenkins服务器不存在: " + input.getServerId()));
|
||||
|
||||
String jobName = input.getProject();
|
||||
String jobName = input.getJobName();
|
||||
|
||||
// 2. 触发构建
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
@ -19,12 +19,18 @@ public class JenkinsBuildInputMapping {
|
||||
* Jenkins服务器ID
|
||||
*/
|
||||
@NotNull(message = "Jenkins服务器ID不能为空")
|
||||
private Integer jenkinsServerId;
|
||||
private Long serverId;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
@NotBlank(message = "项目名称不能为空")
|
||||
private String project;
|
||||
private String jobName;
|
||||
|
||||
/**
|
||||
* 项目分支
|
||||
*/
|
||||
@NotBlank(message = "项目分支不能为空")
|
||||
private String branch;
|
||||
}
|
||||
|
||||
|
||||
@ -173,12 +173,11 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
||||
.orElseThrow(() -> new RuntimeException("Workflow definition not found: " + request.getProcessKey()));
|
||||
|
||||
// 2. 获取流程变量(如果没有则使用空 Map)
|
||||
Map<String, Object> variables = request.getVariables() != null ? request.getVariables() : new HashMap<>();
|
||||
|
||||
// 3. 启动 Flowable 流程
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
|
||||
.processDefinitionKey(request.getProcessKey())
|
||||
.variables(variables)
|
||||
.variables(request.getVariables())
|
||||
.businessKey(request.getBusinessKey())
|
||||
.startAsync(); // 异步启动,会自动执行任务
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user