增加部署日志

This commit is contained in:
dengqichen 2025-11-04 20:42:51 +08:00
parent 2f2637763c
commit e45ec45fb0
7 changed files with 482 additions and 0 deletions

View File

@ -0,0 +1,99 @@
package com.qqchen.deploy.backend.deploy.listener;
import com.qqchen.deploy.backend.deploy.enums.DeployRecordStatusEnums;
import com.qqchen.deploy.backend.deploy.service.IDeployRecordService;
import com.qqchen.deploy.backend.workflow.dto.event.ApprovalCompletedEvent;
import com.qqchen.deploy.backend.workflow.dto.event.ApprovalTaskCreatedEvent;
import com.qqchen.deploy.backend.workflow.enums.ApprovalResultEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 部署记录审批状态同步监听器
* 监听审批相关事件同步更新部署记录状态
*
* @author qqchen
* @since 2025-11-02
*/
@Slf4j
@Component
public class DeployRecordApprovalStatusSyncListener {
private static final String DEPLOYMENT_CATEGORY_CODE = "DEPLOYMENT";
@Resource
private IDeployRecordService deployRecordService;
/**
* 监听审批任务创建事件
* 当审批任务创建时更新部署记录状态为"待审批"
*/
@EventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleApprovalTaskCreated(ApprovalTaskCreatedEvent event) {
// 判断是否为部署类型的工作流
if (!isDeploymentWorkflow(event.getWorkflowCategoryCode())) {
return;
}
try {
log.debug("处理审批任务创建事件: workflowInstanceId={}, nodeId={}",
event.getWorkflowInstanceId(), event.getNodeId());
// 更新部署记录状态为"待审批"
deployRecordService.updateStatusToPendingApproval(event.getWorkflowInstanceId());
log.info("部署记录状态已更新为待审批: workflowInstanceId={}", event.getWorkflowInstanceId());
} catch (Exception e) {
log.error("同步部署记录状态失败(审批任务创建): workflowInstanceId={}",
event.getWorkflowInstanceId(), e);
// 不影响主流程只记录错误
}
}
/**
* 监听审批完成事件
* 根据审批结果更新部署记录状态
*/
@EventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleApprovalCompleted(ApprovalCompletedEvent event) {
// 判断是否为部署类型的工作流
if (!isDeploymentWorkflow(event.getWorkflowCategoryCode())) {
return;
}
try {
log.debug("处理审批完成事件: workflowInstanceId={}, nodeId={}, result={}",
event.getWorkflowInstanceId(), event.getNodeId(), event.getResult());
// 根据审批结果更新部署记录状态
if (event.getResult() == ApprovalResultEnum.APPROVED) {
// 审批通过更新为运行中
deployRecordService.updateStatusFromApproval(event.getWorkflowInstanceId(), true);
} else if (event.getResult() == ApprovalResultEnum.REJECTED) {
// 审批被拒绝更新为已取消
deployRecordService.updateStatusFromApproval(event.getWorkflowInstanceId(), false);
}
log.info("部署记录状态已同步(审批完成): workflowInstanceId={}, result={}",
event.getWorkflowInstanceId(), event.getResult());
} catch (Exception e) {
log.error("同步部署记录状态失败(审批完成): workflowInstanceId={}",
event.getWorkflowInstanceId(), e);
// 不影响主流程只记录错误
}
}
/**
* 判断是否为部署类型的工作流
*/
private boolean isDeploymentWorkflow(String workflowCategoryCode) {
return DEPLOYMENT_CATEGORY_CODE.equals(workflowCategoryCode);
}
}

View File

@ -52,5 +52,20 @@ public interface IDeployRecordService {
* @return 部署记录DTO
*/
DeployRecordDTO findByWorkflowInstanceId(Long workflowInstanceId);
/**
* 更新部署记录状态为待审批
*
* @param workflowInstanceId 工作流实例ID
*/
void updateStatusToPendingApproval(Long workflowInstanceId);
/**
* 根据审批结果更新部署记录状态
*
* @param workflowInstanceId 工作流实例ID
* @param approved 是否通过审批
*/
void updateStatusFromApproval(Long workflowInstanceId, boolean approved);
}

View File

@ -85,6 +85,18 @@ public class DeployRecordServiceImpl extends BaseServiceImpl<DeployRecord, Deplo
return;
}
// 如果当前状态已经是终态特别是审批被拒绝的CANCELLED不应该被覆盖
if (isFinalState(record.getStatus())) {
log.debug("部署记录已处于终态,跳过同步: id={}, workflowInstanceId={}, currentStatus={}, workflowStatus={}",
record.getId(), instance.getId(), record.getStatus(), status);
// 只更新时间不更新状态
if (instance.getEndTime() != null && record.getEndTime() == null) {
record.setEndTime(instance.getEndTime());
deployRecordRepository.save(record);
}
return;
}
// 转换状态
DeployRecordStatusEnums deployStatus = DeployRecordStatusEnums.fromWorkflowStatus(status.name());
@ -109,5 +121,75 @@ public class DeployRecordServiceImpl extends BaseServiceImpl<DeployRecord, Deplo
.map(deployRecordConverter::toDto)
.orElse(null);
}
@Override
@Transactional
public void updateStatusToPendingApproval(Long workflowInstanceId) {
DeployRecord record = deployRecordRepository
.findByWorkflowInstanceIdAndDeletedFalse(workflowInstanceId)
.orElse(null);
if (record == null) {
log.warn("部署记录不存在,无法更新状态: workflowInstanceId={}", workflowInstanceId);
return;
}
// 只有非终态才能更新为待审批
if (isFinalState(record.getStatus())) {
log.debug("部署记录已处于终态,跳过更新: workflowInstanceId={}, status={}",
workflowInstanceId, record.getStatus());
return;
}
record.setStatus(DeployRecordStatusEnums.PENDING_APPROVAL);
deployRecordRepository.save(record);
log.debug("部署记录状态已更新为待审批: id={}, workflowInstanceId={}",
record.getId(), workflowInstanceId);
}
@Override
@Transactional
public void updateStatusFromApproval(Long workflowInstanceId, boolean approved) {
DeployRecord record = deployRecordRepository
.findByWorkflowInstanceIdAndDeletedFalse(workflowInstanceId)
.orElse(null);
if (record == null) {
log.warn("部署记录不存在,无法更新状态: workflowInstanceId={}", workflowInstanceId);
return;
}
// 只有非终态才能更新
if (isFinalState(record.getStatus())) {
log.debug("部署记录已处于终态,跳过更新: workflowInstanceId={}, status={}",
workflowInstanceId, record.getStatus());
return;
}
if (approved) {
// 审批通过更新为运行中
record.setStatus(DeployRecordStatusEnums.RUNNING);
log.info("部署记录状态已更新为运行中(审批通过): id={}, workflowInstanceId={}",
record.getId(), workflowInstanceId);
} else {
// 审批被拒绝更新为已取消
record.setStatus(DeployRecordStatusEnums.CANCELLED);
log.info("部署记录状态已更新为已取消(审批被拒): id={}, workflowInstanceId={}",
record.getId(), workflowInstanceId);
}
deployRecordRepository.save(record);
}
/**
* 判断是否为终态
*/
private boolean isFinalState(DeployRecordStatusEnums status) {
return status == DeployRecordStatusEnums.SUCCESS
|| status == DeployRecordStatusEnums.FAILED
|| status == DeployRecordStatusEnums.CANCELLED
|| status == DeployRecordStatusEnums.TERMINATED
|| status == DeployRecordStatusEnums.PARTIAL_SUCCESS;
}
}

View File

@ -1,14 +1,20 @@
package com.qqchen.deploy.backend.workflow.delegate;
import com.qqchen.deploy.backend.framework.utils.SpelExpressionResolver;
import com.qqchen.deploy.backend.workflow.dto.event.ApprovalTaskCreatedEvent;
import com.qqchen.deploy.backend.workflow.dto.inputmapping.ApprovalInputMapping;
import com.qqchen.deploy.backend.workflow.dto.outputs.ApprovalOutputs;
import com.qqchen.deploy.backend.workflow.enums.ApprovalModeEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowCategoryRepository;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
@ -25,6 +31,18 @@ import java.util.Map;
@Component("approvalTaskListener")
public class ApprovalTaskListener extends BaseTaskListener<ApprovalInputMapping, ApprovalOutputs> {
@Resource
private ApplicationEventPublisher eventPublisher;
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private IWorkflowCategoryRepository workflowCategoryRepository;
@Override
protected void configureTask(
DelegateTask delegateTask,
@ -56,6 +74,62 @@ public class ApprovalTaskListener extends BaseTaskListener<ApprovalInputMapping,
saveApprovalInfoToTaskVariables(delegateTask, inputMapping);
log.info("Approval task configured - assignee: {}", delegateTask.getAssignee());
// 5. 发布审批任务创建事件
publishApprovalTaskCreatedEvent(delegateTask);
}
/**
* 发布审批任务创建事件
*/
private void publishApprovalTaskCreatedEvent(DelegateTask delegateTask) {
try {
String processInstanceId = delegateTask.getProcessInstanceId();
// 查询工作流实例信息
var workflowInstance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId).orElse(null);
if (workflowInstance == null) {
log.warn("工作流实例不存在,无法发布审批任务创建事件: processInstanceId={}", processInstanceId);
return;
}
// 查询工作流定义获取分类信息
String workflowCategoryCode = null;
if (workflowInstance.getWorkflowDefinitionId() != null) {
var definition = workflowDefinitionRepository.findById(workflowInstance.getWorkflowDefinitionId()).orElse(null);
if (definition != null && definition.getCategoryId() != null) {
var category = workflowCategoryRepository.findById(definition.getCategoryId()).orElse(null);
if (category != null) {
workflowCategoryCode = category.getCode();
}
}
}
// 获取候选人信息从任务变量或通过其他方式
// 注意Flowable DelegateTask API 可能不直接提供 getCandidates()这里暂时设为 null
// 如果需要可以通过任务查询服务获取
List<String> candidateUsers = null;
ApprovalTaskCreatedEvent event = ApprovalTaskCreatedEvent.builder()
.processInstanceId(processInstanceId)
.workflowInstanceId(workflowInstance.getId())
.nodeId(getFieldValue(nodeId, delegateTask))
.nodeName(delegateTask.getName())
.taskId(delegateTask.getId())
.assignee(delegateTask.getAssignee())
.candidateUsers(candidateUsers)
.workflowCategoryCode(workflowCategoryCode)
.businessKey((String) delegateTask.getVariable("businessKey"))
.createTime(java.time.LocalDateTime.now())
.build();
eventPublisher.publishEvent(event);
log.debug("发布审批任务创建事件: taskId={}, nodeId={}", delegateTask.getId(), getFieldValue(nodeId, delegateTask));
} catch (Exception e) {
log.error("发布审批任务创建事件失败: taskId={}", delegateTask.getId(), e);
// 不影响主流程只记录错误
}
}
/**

View File

@ -0,0 +1,70 @@
package com.qqchen.deploy.backend.workflow.dto.event;
import com.qqchen.deploy.backend.workflow.enums.ApprovalResultEnum;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 审批完成事件
* 当审批节点完成时发布此事件
*
* @author qqchen
* @since 2025-11-02
*/
@Data
@Builder
public class ApprovalCompletedEvent {
/**
* 流程实例ID
*/
private String processInstanceId;
/**
* 工作流实例ID
*/
private Long workflowInstanceId;
/**
* 节点ID
*/
private String nodeId;
/**
* 节点名称
*/
private String nodeName;
/**
* 审批结果
*/
private ApprovalResultEnum result;
/**
* 审批时间
*/
private LocalDateTime approvalTime;
/**
* 审批人
*/
private String approver;
/**
* 审批意见
*/
private String comment;
/**
* 工作流分类代码用于判断业务类型
*/
private String workflowCategoryCode;
/**
* 业务标识
*/
private String businessKey;
}

View File

@ -0,0 +1,74 @@
package com.qqchen.deploy.backend.workflow.dto.event;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 审批任务创建事件
* 当审批任务创建时发布此事件
*
* @author qqchen
* @since 2025-11-02
*/
@Data
@Builder
public class ApprovalTaskCreatedEvent {
/**
* 流程实例ID
*/
private String processInstanceId;
/**
* 工作流实例ID
*/
private Long workflowInstanceId;
/**
* 节点ID
*/
private String nodeId;
/**
* 节点名称
*/
private String nodeName;
/**
* 任务ID
*/
private String taskId;
/**
* 审批人如果有
*/
private String assignee;
/**
* 候选人如果有
*/
private java.util.List<String> candidateUsers;
/**
* 候选组如果有
*/
private java.util.List<String> candidateGroups;
/**
* 工作流分类代码用于判断业务类型
*/
private String workflowCategoryCode;
/**
* 业务标识
*/
private String businessKey;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@ -2,17 +2,22 @@ package com.qqchen.deploy.backend.workflow.listener.flowable.execution;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.workflow.dto.event.ApprovalCompletedEvent;
import com.qqchen.deploy.backend.workflow.dto.inputmapping.ApprovalInputMapping;
import com.qqchen.deploy.backend.workflow.dto.outputs.ApprovalOutputs;
import com.qqchen.deploy.backend.workflow.enums.ApprovalResultEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeExecutionStatusEnum;
import com.qqchen.deploy.backend.workflow.model.NodeContext;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowCategoryRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@ -40,6 +45,18 @@ public class ApprovalExecutionListener implements ExecutionListener {
@Resource
private HistoryService historyService;
@Resource
private ApplicationEventPublisher eventPublisher;
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private IWorkflowCategoryRepository workflowCategoryRepository;
@Override
public void notify(DelegateExecution execution) {
String nodeId = execution.getCurrentActivityId();
@ -76,12 +93,63 @@ public class ApprovalExecutionListener implements ExecutionListener {
log.info("Stored approval outputs for node: {}, result: {}", nodeId, outputs.getApprovalResult());
// 6. 发布审批完成事件
publishApprovalCompletedEvent(execution, nodeId, outputs);
} catch (Exception e) {
log.error("Failed to build approval outputs for node: {}", nodeId, e);
throw new RuntimeException("Failed to build approval outputs: " + nodeId, e);
}
}
/**
* 发布审批完成事件
*/
private void publishApprovalCompletedEvent(DelegateExecution execution, String nodeId, ApprovalOutputs outputs) {
try {
String processInstanceId = execution.getProcessInstanceId();
// 查询工作流实例信息
var workflowInstance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId).orElse(null);
if (workflowInstance == null) {
log.warn("工作流实例不存在,无法发布审批完成事件: processInstanceId={}", processInstanceId);
return;
}
// 查询工作流定义获取分类信息
String workflowCategoryCode = null;
if (workflowInstance.getWorkflowDefinitionId() != null) {
var definition = workflowDefinitionRepository.findById(workflowInstance.getWorkflowDefinitionId()).orElse(null);
if (definition != null && definition.getCategoryId() != null) {
var category = workflowCategoryRepository.findById(definition.getCategoryId()).orElse(null);
if (category != null) {
workflowCategoryCode = category.getCode();
}
}
}
ApprovalCompletedEvent event = ApprovalCompletedEvent.builder()
.processInstanceId(processInstanceId)
.workflowInstanceId(workflowInstance.getId())
.nodeId(nodeId)
.nodeName(execution.getCurrentFlowElement() != null ? execution.getCurrentFlowElement().getName() : nodeId)
.result(outputs.getApprovalResult())
.approvalTime(outputs.getApprovalTime())
.approver(outputs.getApprover())
.comment(outputs.getApprovalComment())
.workflowCategoryCode(workflowCategoryCode)
.businessKey((String) execution.getVariable("businessKey"))
.build();
eventPublisher.publishEvent(event);
log.debug("发布审批完成事件: nodeId={}, result={}", nodeId, outputs.getApprovalResult());
} catch (Exception e) {
log.error("发布审批完成事件失败: nodeId={}", nodeId, e);
// 不影响主流程只记录错误
}
}
/**
* 丰富 ApprovalOutputs
* 自动装配上下文信息审批用时等