flowable工作流学习笔记

不同版本使用方式不一样,案例使用两个版本6.5.0及6.6.0,学习中6.5.0 版本是独立框架(服务单独部署)使用的, 6.6.0与springboot集成,

6.5.0版本如下:

下载flowable:

bash 复制代码
https://github.com/flowable/flowable-engine/releases

选择6.5.0版本

解压:

database中为所需的表,新建flowable数据库,导入mysql即可:


将war文件夹中的5个jar包复制到tomcat的webapps文件中

flowable-admin:后台管理
flow-idm:用户组权限管理
flow-modeler:流程定义管理
flowable-rest:流程引擎对外提供的API接口
flowable-task:用户任务管理

启动tomcat

修改每个项目的配置文件,将数据库配置修改为自己的地址:

其他均是flowable-default文件。

bash 复制代码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=XXXXX

每个项目的lib中需要加入mysql驱动包。

重新启动tomcat后登录:

bash 复制代码
 http://127.0.0.1:8080/flowable-idm   用户名 admin 密码 test 
 http://127.0.0.1:8080/flowable-admin   用户名 admin 密码 test
 http://127.0.0.1:8080/flowable-modeler   用户名 admin 密码 test 

需要先登录idm系统后再登录其他系统,否则会登录失败。

框架中引入

bash 复制代码
  <!-- 工作流flowable架包 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.5.0</version>
        </dependency>

6.6.0版本如下:

集成flowable模块

6.6.0版本集成到框架中即可:引入flowable-ui及modeler即可

bash 复制代码
 <!-- 工作流flowable架包 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.6.0</version>
        </dependency>
 <!--流程引擎的引用,单例唯一,可以通过它获得所有api的服务对象-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.6.0</version>
        </dependency>

   <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-ui-common</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!-- modeler绘制流程图 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-ui-modeler</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!-- idm依赖提供身份认证 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-ui-idm</artifactId>
            <version>6.6.0</version>
        </dependency>

application配置文件:

bash 复制代码
server:
  port: 8085
spring:
  application:
    name: flowable
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT&nullCatalogMeansCurrent=true
    username: root
    password: root
flowable:
  #关闭定时任务JOB
  async-executor-activate: false
  #将databaseSchemaUpdate设置为true。当flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
  database-schema-update: true
  activity-font-name: '楷体'
#flowable打印sql
logging:
  level:
    org.flowable.engine.impl.persistence.entity.*: debug
    org.flowable.task.service.impl.persistence.entity.*: debug
 

完整pom.xml

bash 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>flowable-master</artifactId>
    <properties>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.3.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!-- 工作流flowable架包 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!--流程引擎的引用,单例唯一,可以通过它获得所有api的服务对象-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!--flowable-ui-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-ui-common</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!-- modeler绘制流程图 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-ui-modeler</artifactId>
            <version>6.6.0</version>
        </dependency>
        <!-- idm依赖提供身份认证 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-ui-idm</artifactId>
            <version>6.6.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- thymeleaf模块引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

启动后访问localhost:8085

登录后画流程图:

成果图:

每个节点详情(主键id、参数等信息自定义即可):

新建模型:key自定义即可,用于标识流程。

开始:

申请:

申请流程:

主管审批:



排他网关:


老板审批:


保存后导出:

导出BPMN文件:

建好如下文件:

表说明:

大部分表是支持流程设计器的, 真正流程运行所需的表并不多。
表的功能一般可以通过第二个词语缩写来进行区分。

xml 复制代码
ACT_RE_*
'RE'表示repository(存储)。RepositoryService接口操作的表。带此前缀的表包含的是静态信息,如流程定义、流程的资源(图片,规则等)。
ACT_RU_*
'RU'表示runtime。这是运行时的表存储着流程变量,用户任务、变量、职责(job)等运行时的数据。flowable只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
ACT_ID_*
'ID'表示identity(组织机构)。这些表包含标识的信息,如用户、用户组等。
一般在正式系统中, 会将这些表用业务系统的组织机构, 角色表进行替换。
ACT_HI_*
'HI'表示history。就是这些表包含着历史的相关数据,如结束的流程实例、变量、任务等。
ACT_GE_*
普通数据,各种情况都使用的数据。
表名 表说明
一般数据对应表 --
ACT_GE_BYTEARRAY 通用的流程定义和流程资源
ACT_GE_PROPERTY 系统相关属性
流程历史对应表 --
ACT_HI_ACTINST 历史的流程实例详情
ACT_HI_ATTACHMENT 历史的流程附件
ACT_HI_COMMENT 历史的说明性信息
ACT_HI_DETAIL 历史的流程运行中的细节信息
ACT_HI_IDENTITYLINK 历史的流程运行过程中用户关系
ACT_HI_PROCINST 历史的流程实例
ACT_HI_TASKINST 历史的任务实例
ACT_HI_VARINST 历史的流程运行中的变量信息
用户用户组对应表 --
ACT_ID_BYTEARRAY 二进制数据表
ACT_ID_GROUP 用户组信息表
ACT_ID_INFO 用户信息详情表
ACT_ID_MEMBERSHIP 人与组关系表
ACT_ID_PRIV 权限表
ACT_ID_PRIV_MAPPING 用户或组权限关系表
ACT_ID_PROPERTY 属性表
ACT_ID_TOKEN 系统登录日志表
ACT_ID_USER 用户表
流程定义表 --
ACT_RE_MODEL 模型信息
ACT_RE_DEPLOYMENT 部署单元信息
ACT_RE_PROCDEF 已部署的流程定义
运行实例表 --
ACT_RU_DEADLETTER_JOB 正在运行的任务表
ACT_RU_EVENT_SUBSCR 运行时事件
ACT_RU_EXECUTION 运行时流程执行实例
ACT_RU_HISTORY_JOB 历史作业表
ACT_RU_IDENTITYLINK 运行时用户关系信息
ACT_RU_JOB 运行时作业表
ACT_RU_SUSPENDED_JOB 暂停作业表
ACT_RU_TASK 运行时任务表
ACT_RU_TIMER_JOB 定时作业表
ACT_RU_VARIABLE 运行时变量表
其他表 --
ACT_EVT_LOG 事件日志表
ACT_PROCDEF_INFO 流程定义信息

互斥网关(Exclusive Gateway),又称排他网关,他有且仅有一个有效出口,可以理解为if...else if... else if...else,就和我们平时写代码的一样。

并行网关(Parallel Gateway),他的所有出口都会被执行,可以理解为开多线程同时执行多个任务。

包容性网关(Inclusive Gateway),只要满足条件的出口都会执行,可以理解为 if(...) do, if (...) do, if (...) do,所有的条件判断都是同级别的。

工作流测方法总结:

bash 复制代码
import com.tt.flowable.FlowerApplication;
import com.tt.flowable.pojo.ResponseBean;
import com.tt.flowable.pojo.leave.leaveInfo;
import org.apache.commons.collections.CollectionUtils;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.User;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

@SpringBootTest(classes = FlowerApplication.class)
public class flowtest {
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    RepositoryService repositoryService;


    /**
     * 查询用户
     */
    @Test
    public void queryUser(){

        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//通过IdentityService完成相关的用户和组的管理
        IdentityService identityService = processEngine.getIdentityService();
       //使用userId
        User u=identityService.createUserQuery().userId("用户01").singleResult();
        List<User> list=identityService.createUserQuery().list();
        for(User user:list){
            System.out.println(user.getId()+";"+user.getFirstName());
        }

    }
    /**
     * 新增用户act_id_user
     */
    @Test
    public void createUser(){
             ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//通过IdentityService完成相关的用户和组的管理
             IdentityService identityService = processEngine.getIdentityService();
             User user = identityService.newUser("0001");
             user.setFirstName("QQ");
             //user.setLastName("01");
             user.setEmail("6666666@qq.com");
             identityService.saveUser(user);

    }

    /**
     * 创建用户组 ACT_ID_GROUP
     */
    @Test
    public void createGroup(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        IdentityService identityService = processEngine.getIdentityService(); // 创建Group对象并指定相关的信息
        Group group = identityService.newGroup("G1");
        group.setName("研发部");
        group.setType("T1"); // 创建Group对应的表结构数据
        identityService.saveGroup(group);
    }

    /**
     * 将用户分配给对应的Group
     * ACT_ID_MEMBERSHIP
     * 用户和组是一个多对多的关联关联
     */
    @Test
    public void userGroup(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        IdentityService identityService = processEngine.getIdentityService(); // 根据组的编号找到对应的Group对象
        Group group = identityService.createGroupQuery().groupId("G1").singleResult();//查询用户组
        List<User> list = identityService.createUserQuery().list();
        for (User user : list) { // 将用户分配给对应的组
            if("用".equals(user.getFirstName())){
                identityService.createMembership(user.getId(),group.getId());
            }
         }
    }

    /**
     * 状态
     */
    @Test
    void testStatus(){
        boolean status=taskService.createTaskQuery().taskId("taskId").singleResult().isSuspended();//Suspended=2中止,=1正常
        if (status){//是否挂起(中止)

        }
    }

    /**
     * 终止删除流程、挂起(中止)
     */
   @Test
    void deletetask(){

       Map<String,Object> map=new HashMap<>();
       String processInstanceId="";
       List<Task> procInsId = taskService.createNativeTaskQuery().
               sql("select * from ACT_HI_TASKINST where PROC_INST_ID_ = #{procInsId} ORDER BY START_TIME_ desc")
               .parameter("procInsId", processInstanceId).list();
       if (!procInsId.get(1).getAssignee().equals("revokeVo.getUserCode()")){
           map.put("result",false);
           map.put("message","当前流程已不在下级节点!");
       }
       //作废-删除流程,删除流程历史记录
       runtimeService.deleteProcessInstance(processInstanceId,"不请了删除流程实例");//删除任务,无processInstanceId任务时会报错。
       historyService.deleteHistoricProcessInstance(processInstanceId);

       runtimeService.suspendProcessInstanceById(processInstanceId);//挂起
       runtimeService.activateProcessInstanceById(processInstanceId);;//激活
    }


    /**
     * 获取下一节点信息(bpmn20.xml中的节点信息)
     * @param taskId
     */
    void getNextWork(String taskId){

        Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult(); // 获取当前任务
        List<SequenceFlow> outgoingFlows =((FlowNode)(repositoryService.getBpmnModel(currentTask.getProcessDefinitionId())
                .getMainProcess()).getFlowElement(currentTask.getTaskDefinitionKey())).
                getOutgoingFlows();
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            String nextTaskId = outgoingFlow.getTargetRef();//节点的id,非任务
            String nextTaskName = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess().getFlowElement(nextTaskId).getName();
            System.out.println("Next task ID: " + nextTaskId + ", Next task name: " + nextTaskName);
           // taskService.setAssignee(nextTaskId,"三三");
            // 下一个审批节点
            FlowElement targetFlow = outgoingFlow.getTargetFlowElement();
            //如果是用户任务则获取待办人或组
            if (targetFlow instanceof UserTask)
            {
                // 如果下个审批节点为userTask
                UserTask userTask = (UserTask) targetFlow;
                userTask.setAssignee("主管审批");
            }
        }
    }
    // 删除流程 :(data:删除原因)
    @Test
    public void deleteProcessInstanceId(){
        try{
            String processInstanceId="a4b311ec-9e13-11ee-ac38-00ffb3ed8746";
            //需要添加这段代码,否则审批意见表ACT_HI_COMMENT审批的userid是空的
           // Authentication.setAuthenticatedUserId("老总");
            //审核意见
            //taskService.addComment("2122f113-9d70-11ee-bd37-00ffb3ed8746","5145648b-9b13-11ee-8409-00ffb3ed8746","批准");

            runtimeService.deleteProcessInstance(processInstanceId,"删除原因");//删除运行中流程
            historyService.deleteHistoricProcessInstance(processInstanceId);//删除历史流程
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //提交申请
    @Test
    public void sub() {
        // 员工提交请假申请
        Map<String, Object> map = new HashMap<>();
        map.put("days", 4);
        map.put("name", "QQ");
        map.put("reason","调价");
        //map.put("userIds", "laozhao,laowang,lisi");//1、赋值审批分配人key
       try{
           //启动流程
           ProcessInstance a1=runtimeService.startProcessInstanceByKey("leaveprocessmore",map.get("name").toString(),map);
           Task task = taskService.createTaskQuery().processInstanceId(a1.getId()).singleResult();
           /* Map<String,Object> m=new HashMap<>();
            m.put("userIds", "老王,老赵");//2、赋值审批分配人key同1
            taskService.setVariables(task.getId(),m);*/
           //处理任务
           taskService.complete(task.getId());
       }catch (Exception e){
           e.printStackTrace();
       }
    }

    /**
     * 候选人为多个时需要先认领任务在处理,认领后其他人不能再操作此任务
     */
    @Test
    void taskClaim(){
        //认领
        List<Task> list = taskService.createTaskQuery().taskCandidateUser("QQ").list();
        for (Task task : list) {
            taskService.claim(task.getId(), "QQ");
        }
        //处理审批任务
        list = taskService.createTaskQuery().taskAssignee("QQ").list();
        for (Task task : list) {
            Map<String, Object> leaves = new HashMap<>();
            leaves.put("approved", true);
             Authentication.setAuthenticatedUserId("QQ");
            //审核意见
            taskService.addComment("54981962-9e15-11ee-8123-00ffb3ed8746","54122175-9e15-11ee-8123-00ffb3ed8746","主管批准");
            taskService.complete(task.getId(), leaves);
        }
    }

    /**
     * 认领任务
     */
    @Test
    void taskClaimUser() {
        //认领
        List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();
        for (Task task : list) {
            taskService.claim(task.getId(), "老赵");
        }
    }
    /**
     * 放弃、取消已认领任务
     */
    @Test
    public void taskUnClaim() {
        List<Task> list = taskService.createTaskQuery().taskAssignee("老赵").list();
        //根据任务id查询
        //Task task= taskService.createTaskQuery().taskId("").singleResult();
        for (Task task : list) {
            taskService.unclaim(task.getId());//放弃认领
        }
    }

    /**
     * 更换认领人
     */
    @Test
    public void taskClaimUserChange() {
        //根据任务id查询
        Task task= taskService.createTaskQuery().taskId("").singleResult();
        taskService.setAssignee(task.getId(), "老王");
    }

    /**
     * 附件应用-保存
     * attachmenttype附件类型
     * attachmentdescription附件说明
     */
    @Test
    void saveAttavhment(){
        Attachment attachment=taskService.createAttachment("bpmn20.xml","0b0ffc62-9e16-11ee-8e0c-00ffb3ed8746",
                "54122175-9e15-11ee-8123-00ffb3ed8746","流程文件","记录流程文件",
                "E:\\springcloud\\flowable-master\\target\\classes\\processes\\请假more.bpmn20.xml");
        taskService.saveAttachment(attachment);
    }

    /**
     * 附件应用-查询
     */
    @Test
    void taskAttavhment(){
        Attachment attachment=taskService.getAttachment("a3a30f3a-9e18-11ee-aef3-00ffb3ed8746");//获取附件对象
        InputStream inputStream= taskService.getAttachmentContent("a3a30f3a-9e18-11ee-aef3-00ffb3ed8746");
        List<Attachment> attachmentList=taskService.getProcessInstanceAttachments("54122175-9e15-11ee-8123-00ffb3ed8746");
        for(Attachment att:attachmentList){
            System.out.println("url:"+att.getUrl()+";description:"+att.getDescription());
        }
    }
    /**
     * 老板审批(批量)
     */
    @Test
    void approveMore(){
        //查询所属任务
        List<Task> list = taskService.createTaskQuery().taskCandidateGroup("boss").list();
        Map<String,Object> leaves=new HashMap<>();
        leaves.put("bossapproved", true);
        for (Task task : list) {
            taskService.complete(task.getId(), leaves);
            if (true) {
                //如果是同意,还需要继续走一步
                Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
                if (t == null) {
                    System.out.println("工作流已完结!!!!");
                    break;
                }
                taskService.complete(t.getId());

            }
        }
    }
    /**
     * 老板审批(单任务)、记录审批意见
     */
    @Test
    void approveSingle(){
        //查询所属任务
        Task task = taskService.createTaskQuery().processInstanceId("5145648b-9b13-11ee-8409-00ffb3ed8746").singleResult();
        Map<String,Object> leaves=new HashMap<>();
        leaves.put("bossapproved", true);
        //需要添加这段代码,否则审批意见表ACT_HI_COMMENT审批的userid是空的
        Authentication.setAuthenticatedUserId("老总");
        //审核意见
        taskService.addComment("2122f113-9d70-11ee-bd37-00ffb3ed8746","5145648b-9b13-11ee-8409-00ffb3ed8746","批准");

        taskService.complete(task.getId(), leaves);
            if (true) {
                //如果是同意,还需要继续走一步
                Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
                if (t == null) {
                    System.out.println("工作流已完结!!!!");
                    return;
                }
                taskService.complete(t.getId());
            }
    }

    /**
     *查询审批意见
     */
    @Test
    void queryTaskComment(){
        List<Comment> taskComments = taskService.getTaskComments("2122f113-9d70-11ee-bd37-00ffb3ed8746");
        for (Comment taskComment : taskComments) {
            System.out.println("审批人:" + taskComment.getUserId());
            System.out.println("审批意见:" + taskComment.getFullMessage());
        }
    }


    /**
     * 设置候选人
     */
    @Test
    void setCandidate(){
        Map<String, Object> userIds = new HashMap<>();
        userIds.put("userIds", "老赵,老王,老李");
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("leaveprocessmore",userIds);
        System.out.println("id:"+pi.getId()+";activityId:"+pi.getActivityId());

    }

    /**
     * 追加候选人
     */
    @Test
    void addCandidate(){
        List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();
        List<Task> listt = taskService.createTaskQuery().taskId("taskId").list();
        for (Task task : list)
            taskService.addCandidateUser(task.getId(),"老李");
    }
    /**
     * 删除候选人
     */
    @Test
    void delCandidate() {
        List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();
        for (Task task : list) {
            taskService.deleteCandidateUser(task.getId(), "老李");
        }
    }

    /**
     * 查询流程历史参与人记录
     */
    @Test
    void queryHistoryUser() {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
        for (HistoricProcessInstance historicProcessInstance : list) {
            List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForProcessInstance(historicProcessInstance.getId());
            for (HistoricIdentityLink link : links)
                System.out.println("userId:,taskId:,type:,processInstanceId:"+ link.getUserId()+";"+
                        link.getTaskId()+";"+ link.getType()+";"+ link.getProcessInstanceId());
        }
    }

    /**
     * 查询一个 Task的历史候选人与处理人
     */
    void queryHistoryTaskUser() {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
        for (HistoricTaskInstance historicTaskInstance : list) {
            List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(historicTaskInstance.getId());
            for (HistoricIdentityLink link : links) {
                System.out.println("userId:,taskId:,type:,processInstanceId:" + link.getUserId() + ";" +
                        link.getTaskId() + ";" + link.getType() + ";" + link.getProcessInstanceId());
            }
        }
    }
    /**
     * 查询分配人或候选用户人的任务
     */
    @Test
    void querytask(){

        //候选用户及多个候选用户时
        List<Task> task = taskService.createTaskQuery().taskCandidateUser("李思思").list();
        //分配人
        List<Task> tasks = taskService.createTaskQuery().taskAssignee("李思思").list();
        //候选用户或分配人
        List<Task> task2 =taskService.createTaskQuery().taskCandidateOrAssigned("lisi").list();

        System.out.println(task.size()+"=="+tasks.size());
    }

    /**
     * 获取历史流程
     */
    @Test
    void queryHistory() {
        List<ProcessInstance> processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();
        if (CollectionUtils.isEmpty(processInstance)) {
            System.out.println("------------------------------------------");
        }
        // 获取最近的一个流程
        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstance.get(0).getId())
                // 只查询已经完成的活动
                .finished()
                // 按照结束时间排序
                .orderByHistoricActivityInstanceEndTime().desc()
                .list();
        List<String> collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());
        for (String s : collect) {
            System.out.println(s);
        }
    }

    /**
     * 请假列表(已完结、未完结)
     */
    @Test
    public void searchResult() {
        String name = "李思思";
        List<leaveInfo> leaveInfos = new ArrayList<>();
        //已完结的
       // List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list();
        //未完结的
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().
                processInstanceBusinessKey(name).unfinished().orderByProcessInstanceStartTime().list();

        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            leaveInfo leaveInfo = new leaveInfo();
            Date startTime = historicProcessInstance.getStartTime();
            Date endTime = historicProcessInstance.getEndTime();
            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(historicProcessInstance.getId())
                    .list();
            for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
                String leaveName = historicVariableInstance.getVariableName();
                Object value = historicVariableInstance.getValue();
                if ("reason".equals(leaveName)) {
                    leaveInfo.setReason((String) value);
                } else if ("days".equals(leaveName)) {
                    leaveInfo.setDays(Integer.parseInt(value.toString()));
                } else if ("approved".equals(leaveName)) {
                    leaveInfo.setStatus((Boolean) value);
                } else if ("name".equals(leaveName)) {
                    leaveInfo.setName((String) value);
                }
            }
            leaveInfo.setStartTime(startTime);
            leaveInfo.setEndTime(endTime);
            leaveInfos.add(leaveInfo);
        }
        System.out.println(leaveInfos.size());
    }

}

主管审批使用监听器动态设置候选人:


MyUserTaskListener代码:

bash 复制代码
package com.tt.flowable.lister;


import org.flowable.engine.RuntimeService;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Map;

/**
 * 委派任务
 * 监听器类型:任务监听器
 * 主管审批动态分配候选人
 */
public class MyUserTaskListener implements TaskListener {


    @Override
    public void notify(DelegateTask delegateTask) {

        delegateTask.addCandidateUser("老赵");
        delegateTask.addCandidateUser("老王");
        System.out.println("任务监听器:"+delegateTask.getId()+";"+delegateTask.getName());
    }
}

主要表信息:

串行任务撤回(主动):

    1、先判断是否可撤回:上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;
    2、撤回步骤:保存并删除BpmnModel当前流程节点输出流;
    3、把当前流程节点输出流方向改成上一个节点;
    4、撤回任务完成后,再次恢复当前节点的原有输出流;

service

bash 复制代码
  @Autowired
    RepositoryService repositoryService;

    //串行任务【主动撤回】
    public void proBack(String instanceId,String canBackTaskId,String userId){
        /*
        1、先判断是否可撤回:上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;
        2、撤回步骤:保存并删除BpmnModel当前流程节点输出流;
        3、把当前流程节点输出流方向改成上一个节点;
        4、撤回任务完成后,再次恢复当前节点的原有输出流;
        */
        Map<String,Object> result = canBack(instanceId, canBackTaskId, userId);
        Boolean canBack=(Boolean)result.get("canBack");
        HistoricTaskInstance currentTask=(HistoricTaskInstance)result.get("currentTask");
        if(!canBack){
            System.out.println("流程:"+instanceId+",任务:"+canBackTaskId+"已经处理,不可撤回");
            return;
        }

        //流程撤回
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();//历史的【流程】实例
        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());//ProcessDefinitionId:leaveprocessmore:11:20f8063c-9641-11ee-b051-00ffb3ed8746
        //上一流程
        HistoricTaskInstance canBackTask = historyService.createHistoricTaskInstanceQuery().taskId(canBackTaskId).taskAssignee(userId).singleResult();

        if(canBackTask==null){
            System.out.println("未找到要回退的任务");
            return;
        }

        FlowNode canBackNode = (FlowNode)bpmnModel.getFlowElement(canBackTask.getTaskDefinitionKey());
        FlowNode currentNode=(FlowNode)bpmnModel.getFlowElement(currentTask.getTaskDefinitionKey());

        //记录原始输出流程
        List<SequenceFlow> sourceOuts =new ArrayList<>();
        sourceOuts.addAll(currentNode.getOutgoingFlows());

        //清理活动方向
        currentNode.getOutgoingFlows().clear();

        //创建新流
        List<SequenceFlow> newFlows=new ArrayList<>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentNode);//bossApproval老总
        newSequenceFlow.setTargetFlowElement(canBackNode);//上一环节targetRef=supApproval(主管)
        newFlows.add(newSequenceFlow);
        currentNode.setOutgoingFlows(newFlows);

        //记录备注信息
        Authentication.setAuthenticatedUserId(userId);
        taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), "撤回");

        //完成撤回任务
        taskService.complete(currentTask.getId());
        //恢复原方向
        currentNode.setOutgoingFlows(sourceOuts);

        //删除当前任务历史记录,根据业务需要是否删除
        historyService.deleteHistoricTaskInstance(currentTask.getId());
    }

    /**
     * 判断流程是否能够撤回
     * 上一个节点是当前用户审核,并且下一节点审批人还未处理时,可撤回到上一节点
     * @return
     */
    public Map<String,Object> canBack(String instanceId,String canBackTaskId,String userId){
        boolean canBack=false;
        Map<String,Object> map=new HashMap<>();
        //查看流程历史节点
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instanceId)
                .orderByHistoricTaskInstanceStartTime()
                .asc()
                .list();
        boolean hasUserTask=false;
        HistoricTaskInstance currentTask=null;
        for(HistoricTaskInstance task:list) {
            if(task.getId().equalsIgnoreCase(canBackTaskId)
                    &&task.getAssignee().equalsIgnoreCase(userId)){
                //找到了处理人处理的任务,查看下一个任务是否已经处理
                hasUserTask=true;
                continue;
            }
            if(hasUserTask){
                //上一个任务是当前人处理的,查看当前任务是否已经被处理
                hasUserTask=false;
                if(null==task.getEndTime()){
                    canBack=true;
                    currentTask=task;
                    break;
                }
            }
        }
        if(canBack){
            System.out.println("未处理的流程可撤回,任务ID:"+currentTask.getId()+",任务接收人:"+currentTask.getAssignee());
        }
        map.put("canBack",canBack);
        map.put("currentTask",currentTask);
        //方法返回值为:是否可回退、当前任务
        return map;
    }

controller

bash 复制代码
 /**
     * 撤回
     * @param instanceId 流程id ACT_HI_PROCINST.PROC_INST_ID_
     * @param canBackTaskId 想撤回到节点的id  act_hi_taskinst.ID_
     * @param userId 撤回人
     */
    @GetMapping("/proBack")
    public void proBack(String instanceId,String canBackTaskId,String userId) {
        leaveService.proBack(instanceId,canBackTaskId,userId);
    }
复制代码
相关推荐
Somnus陳5 分钟前
软考架构师笔记-计算机系统组成-1
笔记·系统架构
中草药z5 分钟前
【Spring】深入解析 Spring 原理:Bean 的多方面剖析(源码阅读)
java·数据库·spring boot·spring·bean·源码阅读
信徒_13 分钟前
常用设计模式
java·单例模式·设计模式
神仙别闹19 分钟前
基于C#实现的(WinForm)模拟操作系统文件管理系统
java·git·ffmpeg
小爬虫程序猿19 分钟前
利用Java爬虫速卖通按关键字搜索AliExpress商品
java·开发语言·爬虫
组合缺一25 分钟前
Solon v3.0.5 发布!(Spring 可以退休了吗?)
java·后端·spring·solon
程序猿零零漆27 分钟前
SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法
java·spring cloud·mybatis-plus
猿来入此小猿29 分钟前
基于SpringBoot在线音乐系统平台功能实现十二
java·spring boot·后端·毕业设计·音乐系统·音乐平台·毕业源码
愤怒的代码42 分钟前
Spring Boot对访问密钥加解密——HMAC-SHA256
java·spring boot·后端
带多刺的玫瑰43 分钟前
Leecode刷题C语言之切蛋糕的最小总开销①
java·数据结构·算法