flowable已结束流程复活

flowable已结束的流程,如何复活到指定节点,然后继续审批

参考

书籍《深入Flowable流程》,作者贺波、刘晓鹏、胡海琴
第27章 27.1 流程复活

实现

包含3个类

1、RestartProcessInstanceCmd :实现cmd命令,为流程复活的核心
2、BpmRestartProcessInstanceService:封装的退回service
3、ProcessInstanceServiceImpl:该类因为调用样例

1 、RestartProcessInstanceCmd

流程复活的核心代码

package com.flowable.core.cmd;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntityImpl;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntityManager;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@AllArgsConstructor
public class RestartProcessInstanceCmd implements Command<ProcessInstance> {

    // 待复活流程实例编号
    protected String processInstanceId;

    // 要复活到的节点列表
    protected List<String> activityIds;

    @Override
    public ProcessInstance execute(CommandContext commandContext) {
        ProcessEngineConfiguration procEngineConf = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoryService historyService = procEngineConf.getHistoryService();
        // 校验和查询历史流程实例
        HistoricProcessInstance hisProcInst = checkAndGetHistoricProcessInstance(historyService);
        // 校验待复活节点
        checkActivityIds(hisProcInst.getProcessDefinitionId());
        // 校验流程定义
        ProcessDefinition processDefinition = ProcessDefinitionUtil.getProcessDefinition(hisProcInst.getProcessDefinitionId());
        if (processDefinition == null) {
            throw new FlowableException("流程定义不存在");
        }

        // 获取流程启动节点
        Process process = ProcessDefinitionUtil.getProcess(processDefinition.getId());
        StartEvent initialFlowElement = (StartEvent) process.getInitialFlowElement();

        // 重建运行时流程实例的主执行实例
        ExecutionEntity processInstance = reCreateProcessInstance(processDefinition, initialFlowElement, hisProcInst);
        // 创建子执行实例
        ExecutionEntity childExecution = CommandContextUtil.getExecutionEntityManager().createChildExecution(processInstance);
        childExecution.setCurrentFlowElement(initialFlowElement);

        HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager = CommandContextUtil.getProcessEngineConfiguration()
                .getVariableServiceConfiguration().getHistoricVariableInstanceEntityManager();

        // 收集待复活的流程变量
        Map<String, Object> varMap = collectVariables(historyService, hisProcInst, historicVariableInstanceEntityManager);
        // 如果自定义了流程状态,可自行修改
		// varMap.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, ProcessInstanceStatusEnum.RUNNING.getCode());

        // 设置历史流程实例结束节点和结束时间为空
        ((HistoricProcessInstanceEntityImpl) hisProcInst).setEndActivityId(null);
        ((HistoricProcessInstanceEntityImpl) hisProcInst).setEndTime(null);

        // 执行动态跳转
        procEngineConf.getRuntimeService()
                .createChangeActivityStateBuilder()
                .processInstanceId(processInstance.getProcessInstanceId())
                .moveSingleExecutionToActivityIds(childExecution.getId(), activityIds)
                .processVariables(varMap)
                .changeState();

        return procEngineConf.getRuntimeService().createProcessInstanceQuery()
                .processInstanceId(processInstance.getId()).singleResult();
    }

    /**
     * 校验和查询历史流程实例
     *
     * @param historyService 流程引擎历史服务
     * @return 历史任务
     */
    private HistoricProcessInstance checkAndGetHistoricProcessInstance(HistoryService historyService) {
        if (StrUtil.isBlank(processInstanceId)) {
            throw new FlowableIllegalArgumentException("processInstanceId不能为空");
        }
        HistoricProcessInstance hisProcInst = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (hisProcInst == null) {
            throw new FlowableException("id为" + processInstanceId + "的流程实例不存在");
        } else if (hisProcInst.getEndTime() == null) {
            throw new FlowableException("id为" + processInstanceId + "的流程实例没有结束");
        }
        return hisProcInst;
    }

    /**
     * 校验复活节点
     *
     * @param processDefinitionId 流程实例id
     */
    private void checkActivityIds(String processDefinitionId) {
        if (CollUtil.isEmpty(activityIds)) {
            throw new FlowableIllegalArgumentException("activityIds不能为空");
        }
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
        List<String> notExistedFlowElements = new ArrayList<>();
        for (String activityId : activityIds) {
            if (!bpmnModel.getMainProcess().containsFlowElementId(activityId)) {
                notExistedFlowElements.add(activityId);
            }
        }
        if (!CollUtil.isEmpty(notExistedFlowElements)) {
            throw new FlowableIllegalArgumentException("Id为" + String.join("、", notExistedFlowElements) + "节点不存在");
        }
    }

    /**
     * 重建流程实例: ExecutionEntity 继承自 ProcessInstance
     *
     * @param processDefinition  流程定义
     * @param initialFlowElement 发起节点
     * @param hisProcInst        流程历史
     * @return 流程实例
     */
    private ExecutionEntity reCreateProcessInstance(ProcessDefinition processDefinition, StartEvent initialFlowElement, HistoricProcessInstance hisProcInst) {

        // 创建流程实例
        ExecutionEntity procInst = CommandContextUtil.getExecutionEntityManager().createProcessInstanceExecution(processDefinition, hisProcInst.getId(),
                hisProcInst.getBusinessKey(), hisProcInst.getBusinessStatus(),
                hisProcInst.getName(), hisProcInst.getCallbackId(),
                hisProcInst.getCallbackType(), hisProcInst.getReferenceId(),
                hisProcInst.getReferenceType(),
                hisProcInst.getPropagatedStageInstanceId(),
                hisProcInst.getTenantId(), initialFlowElement.getInitiator(),
                hisProcInst.getStartActivityId());

        // 设置流程实例为活跃状态
        procInst.setActive(true);
        // 重设流程开始事件
        procInst.setStartTime(hisProcInst.getStartTime());
        // 设置流程的发起人
        procInst.setStartUserId(hisProcInst.getStartUserId());
        return procInst;
    }

    /**
     * 收集待复活的流程变量
     *
     * @param historyService 历史服务
     * @param hisProcInst    历史实力哦
     * @return 变量
     */
    private Map<String, Object> collectVariables(HistoryService historyService,
                                                 HistoricProcessInstance hisProcInst,
												 HistoricVariableInstanceEntityManager historicVariableInstanceEntityManager) {
        Map<String, Object> varMap = new HashMap<>();
        // 1 查询历史流程变量
        List<HistoricVariableInstance> hisVariables = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(hisProcInst.getId())
                .executionId(hisProcInst.getId()).list();
        for (HistoricVariableInstance hisVariable : hisVariables) {
            // 2 收集变量
            varMap.put(hisVariable.getVariableName(), hisVariable.getValue());
            // 3 删除历史变量(防止历史数据存在2份-可选)
            historicVariableInstanceEntityManager.delete((HistoricVariableInstanceEntityImpl) hisVariable);
        }
        return varMap;
    }
}

2 、BpmRestartProcessInstanceService

调用核心代码,为必要的service实现

package com.flowable.core.service;

import com.flowable.core.cmd.RestartProcessInstanceCmd;
import jakarta.annotation.Resource;
import org.flowable.engine.ManagementService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BpmRestartProcessInstanceService {

    @Resource
    private ManagementService managementService;

    public void executeRestart(String processInstanceId, List<String> activityIds) {
        // 实例化流程复活command 类
        RestartProcessInstanceCmd restartProcessInstanceCmd = new RestartProcessInstanceCmd(processInstanceId, activityIds);
        // 通过 managementService 管理服务,执行流程复活command类
        managementService.executeCommand(restartProcessInstanceCmd);
    }
}

3 、ProcessInstanceServiceImpl

调用流程复活样例

package com.flowable.process.service.impl;

@Slf4j
@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Resource
    private TaskService taskService;

    @Resource
    private BpmRestartProcessInstanceService bpmRestartProcessInstanceService;

    @Override
    public void restartProcessInstance(Long userId, ProcessInstanceRestartParam param) {
        // 1 执行复活到指定节点
        bpmRestartProcessInstanceService.executeRestart(param.getId(), param.getTaskDefinitionKeys());

        // 2 添加任务复活评论
        // taskService.addComment(null, param.getId(), ProcessCommentTypeEnum.REACTIVATE.getType()
                , ProcessCommentTypeEnum.REACTIVATE.formatComment(user.getNickName(), param.getReason()));
    }
}

#-----------------------------------------------------------------------------
package com.flowable.process.entity.param;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.util.List;

/**
 * 管理后台 - 流程实例 请求参数
 */
@Data
public class ProcessInstanceRestartParam {

    /**
     * 任务编号
     */
    @NotEmpty(message = "流程实例编号不能为空")
    private String id;

    /**
     * 送办的节点 Key
     */
    @NotNull(message = "回退节点不能为空")
    private List<String> taskDefinitionKeys;

    /**
     * 送办原因
     */
    @NotEmpty(message = "复活原因不能为空")
    private String reason;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/965417.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

113,【5】 功防世界 web unseping

进入靶场 代码审计 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;方便开发者查看代码结构和内容 highlight_file(__FILE__);// 定义一个名为 ease 的类 class ease {// 私有属性 $method&#xff0c;用于存储要调用的方法名private $method;// 私有属性 $args&…

图解BWT(Burrows-Wheeler Transform) 算法

Burrows-Wheeler Transform (BWT) 是一种数据转换算法, 主要用于数据压缩领域. 它由 Michael Burrows 和 David Wheeler 在 1994 年提出, 广泛应用于无损数据压缩算法(如 bzip2)中. BWT 的核心思想是通过重新排列输入数据, 使得相同的字符更容易聚集在一起, 从而提高后续压缩算…

DeepSeek辅助学术写作【句子重写】效果如何?

句子重写(功能指数:★★★★★) 当我们想引用一篇文章中的一-些我们认为写得很好的句子时&#xff0c;如果直接将原文加人自己的文章&#xff0c;那么即使我们标注上了引用&#xff0c;也依旧会被查重软件计算在重复比例中。查重比例过高的话&#xff0c;会影响投稿或毕业答辩送…

【CAD】卸载清理注册表后安装失败/错误 1402无法打开主键:UNKNOWN Components DAFE。。。

这是因为注册表中的 CurrentVersion\Installer\UserData二级子目录中Components的文件缺少权限&#xff0c;只需要将权限添加为administors即可 一图流&#xff0c;就是把权限给到Administrators&#xff0c;然后就再以管理员权限安装就可以了&#xff0c;还有如果破解的软件可…

w192中国陕西民俗网的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

2.6日学习总结

题目一&#xff1a; AC代码&#xff1a; #include <stdio.h>// 宏 _for 用于简化 for 循环 #define _for(i, a, b) for (int i (a); i < (b); i)// 最大节点数 #define MAXN 1000010// 树节点结构体 typedef struct {int left;int right; } Node;// 树节点数组 Nod…

【数据结构】(4) 线性表 List

一、什么是线性表 线性表就是 n 个相同类型元素的有限序列&#xff0c;每一个元素只有一个前驱和后继&#xff08;除了第一个和最后一个元素&#xff09;。 数据结构中&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…

工控机的主要功能有那些?

工控机的主要功能包括&#xff1a;数据采集与处理&#xff0c;工控机可以连接多种传感器和输入设备&#xff0c;实时采集数据&#xff0c;并进行必要的处理和分析。其次 就是控制执行&#xff1a;在自动化生产线或机器人控制系统中&#xff0c;工控机根据预设的程序或实时数据执…

链式结构二叉树(递归暴力美学)

文章目录 1. 链式结构二叉树1.1 二叉树创建 2. 前中后序遍历2.1 遍历规则2.2 代码实现图文理解 3. 结点个数以及高度等二叉树结点个数正确做法&#xff1a; 4. 层序遍历5. 判断是否完全二叉树 1. 链式结构二叉树 完成了顺序结构二叉树的代码实现&#xff0c;可以知道其底层结构…

凝思60重置密码

凝思系统重置密码 - 赛博狗尾草 - 博客园 问题描述 凝思系统进入单用户模式&#xff0c;在此模式下&#xff0c;用户可以访问修复错误配置的文件。也可以在此模式下安装显卡驱动&#xff0c;解决和已加载驱动的冲突问题。 适用范围 linx-6.0.60 linx-6.0.80 linx-6.0.100…

MDPI的论文书写

一、作者信息 二、摘要 1、先写背景&#xff0c;将问题放到大背景里面&#xff0c;然后重点说明研究的目的。

2-kafka服务端之延时操作实现原理

文章目录 背景案例延时生产实现原理延时拉取实现原理 总结 背景 上篇我们说到了kafka时间轮是延时操作内部实现的重要数据结构&#xff0c;这篇我们来说下kafka内部的延时操作实现原理。这里我们以延时生产和延时拉取为例说明延时操作的实现原理。 案例 延时生产 我们知道如…

无心剑七绝《深度求索》

七绝深度求索 深研妙理定乾坤 度世玄机启智门 求路千难兼万险 索萦华夏自为尊 2025年2月1日 平水韵十三元平韵 无心剑七绝《深度求索》以平水韵十三元平韵写成&#xff0c;意境深远&#xff0c;气势磅礴。诗中“深研妙理定乾坤”开篇点题&#xff0c;展现出对深奥道理的钻研与探…

C++多级指针图解

AudioResample **pResample 指针的地址图解AudioResample **pResample; // pResample 存储 AudioResample* 的地址 AudioResample *ar *pResample; // ar 现在指向 AudioResample 结构体 pResample → 指向 AudioResample* 的地址 (0x2000)*pResample → 取出 AudioResample…

oracle基础语法

oracle基础语法 1、增删改查1.1查询语句1.2 修改语句1.3 删除表1.4 删除数据1.5 增加数据1.6 创建视图1.7 添加视图字段注释 1、增删改查 oracle与sql server语法上大致相同&#xff0c;但有些细微的不同&#xff0c;以下是我个人记录工作中常用到的一些语法句。 1.1查询语句…

数据库------------

一 mysql ----数据库就相当于一个端口 1. 三层结构 1&#xff09;数据库中 表的本质仍然是文件 1.1 mysql常用数据类型---&#xff08;即 mysql列类型&#xff09; 1&#xff09; 数值类型 2&#xff09; 文本类型 3&#xff09; 二进制数据类型 4&#xff09;日期类型 2. sq…

使用服务器部署DeepSeek-R1模型【详细版】

文章目录 引言deepseek-r1IDE或者终端工具算力平台体验deepseek-r1模型总结 引言 在现代的机器学习和深度学习应用中&#xff0c;模型部署和服务化是每个开发者面临的重要任务。无论是用于智能推荐、自然语言处理还是图像识别&#xff0c;如何高效、稳定地将深度学习模型部署到…

25/2/6 <机器人基础> 运动学中各连杆的变换矩阵求法

变换矩阵 机器人通常包含多个关节和连杆&#xff0c;每个关节和连杆都有自己的局部坐标系。变换矩阵能够将一个点或向量从一个坐标系转换到另一个坐标系&#xff0c;从而实现对机器人各个部件位置和姿态的统一描述 变换矩阵能够将复杂的运动分解为旋转和平移的组合。通过矩阵乘…

CS 与 BS 架构的差异

在数字化的今天&#xff0c;选择软件架构模式对系统的性能、维护、安全和成本都有很大影响。BS架构和CS架构是最常见的两种模式&#xff0c;了解它们的区别和特点对开发人员和企业决策者都很重要。 CS架构最早出现&#xff0c;当时用户直接从主机获取数据。随着客户端和服务端…

Vuex 解析:从 Vue 2 到 Vue 3 的演变与最佳实践

Vuex 是 Vue.js 中的状态管理模式&#xff0c;广泛应用于 Vue 2 和 Vue 3 中&#xff0c;其内部实现存在一些差异。 1. 什么是 Vuex &#xff1f; Vuex 是 Vue.js 官方提供的状态管理库&#xff0c;用于集中管理应用的所有组件的状态。主要是通过一种集中化的方式来管理共享状…