diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/listener/DeployRecordApprovalStatusSyncListener.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/listener/DeployRecordApprovalStatusSyncListener.java new file mode 100644 index 00000000..bf460b1f --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/listener/DeployRecordApprovalStatusSyncListener.java @@ -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); + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployRecordService.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployRecordService.java index d4f4031b..21c7e6de 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployRecordService.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployRecordService.java @@ -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); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployRecordServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployRecordServiceImpl.java index 3aa682c7..9e5762b5 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployRecordServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployRecordServiceImpl.java @@ -85,6 +85,18 @@ public class DeployRecordServiceImpl extends BaseServiceImpl { + @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 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); + // 不影响主流程,只记录错误 + } } /** diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalCompletedEvent.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalCompletedEvent.java new file mode 100644 index 00000000..cbba88c2 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalCompletedEvent.java @@ -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; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalTaskCreatedEvent.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalTaskCreatedEvent.java new file mode 100644 index 00000000..e265f522 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/event/ApprovalTaskCreatedEvent.java @@ -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 candidateUsers; + + /** + * 候选组(如果有) + */ + private java.util.List candidateGroups; + + /** + * 工作流分类代码(用于判断业务类型) + */ + private String workflowCategoryCode; + + /** + * 业务标识 + */ + private String businessKey; + + /** + * 创建时间 + */ + private LocalDateTime createTime; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/listener/flowable/execution/ApprovalExecutionListener.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/listener/flowable/execution/ApprovalExecutionListener.java index f66589aa..738efd0e 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/listener/flowable/execution/ApprovalExecutionListener.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/listener/flowable/execution/ApprovalExecutionListener.java @@ -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 * 自动装配上下文信息(审批用时等)