Activiti7流程操作详解

一、Activiti流程操作步骤

  • 定义流程,按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来

  • 部署流程,把画好的流程定义文件,加载到数据库中,生成表的数据

  • 启动流程,使用java代码来操作数据库表中的内容

  • 操作流程当中的各个任务

通过以上三步、就可以创建一个Activiti工作流、并且启动该流程。

二、常见流程符号

2.1 BPMN 2.0

BPMNBusiness Process Model AndNotation)- 业务流程模型和符号 是由BPMIBusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

BPMN 是目前被各 BPM 厂商广泛接受的 BPM 标准。

Activiti 就是使用 BPMN 2.0 进行流程建模、流程执行管理等。

BPMN的五类元素:

类别 对象
流对象 (Flow Objects ) 事件( Events)、活动 (Activities )、网关 ( Gateways )
数据 (Data) 数据对象 ( Data Objects )、数据输入 ( Data Inputs )、数据输出( Data Outputs )、数据存储 ( Data Stores )
连接对象 (Connecting Objects ) 顺序流 ( Sequence Flows )、消息流 ( Message Flows )、关联 ( Associations )、数据关联( Data Associations )
泳道(Swimlanes) 池子( Pools)、泳道 ( Lanes)
工件 ( Artifacts ) 组 (Group )、文字注释

2.2 常用流程符号

1)事件 Event

  • 开始事件

在这里插入图片描述

  • 中间事件

在这里插入图片描述
在这里插入图片描述

  • 结束事件

在这里插入图片描述

2)活动 Activity

活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:
在这里插入图片描述

3)网关 GateWay

网关用来处理决策,有几种常用网关需要了解:
在这里插入图片描述

  • 排他网关 (x)

    只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继续执行当前网关的输出流;

    如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。

    如果所有网关计算结果没有true,则引擎会抛出异常。

    排他网关需要和条件顺序流结合使用,default属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。

  • 并行网关 (+)

    所有路径会被同时选择

    拆分 —— 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。

    合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。

  • 包容网关 (+)

    可以同时执行多条线路,也可以在网关上设置条件

    拆分 —— 计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行

    合并 —— 所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。

  • 事件网关 (+)

    专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。

    当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。

4)流向 Flow

流是连接两个流程节点的连线。常见的流向包含以下几种:

在这里插入图片描述

三、安装Activiti BPMN visualizer插件

这个在idea2020版本开始,老的插件actiBPM已经在插件市场中搜索不到了。

虽然依旧可以在插件官网下载actiBPM到本地进行安装,但是没必要,新的插件更好使用一些。
在这里插入图片描述

四、流程设计器使用

4.1 新建BPMN 2.0文件

在这里插入图片描述
新建以后会得到一个XML文件,并没有出现画BPMN流程图的界面。
在这里插入图片描述

4.2 调出流程设计页面

在这里插入图片描述
在这里插入图片描述

4.3 绘制出差流程

1)绘制开始事件

右击流程设计页面,选择Start events
在这里插入图片描述

选择后注意下面三个区域即可:
在这里插入图片描述

2)绘制用户任务流程

  • 创建申请单
    在这里插入图片描述

绘制出用户任务之后,可以指定流程名称为创建出差申请,并且指定分配到任务的人为小王。
在这里插入图片描述

  • 部门经理审批

在这里插入图片描述

  • 总经理审批

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XblAWfMu-1683250543424)(assets\流程-部门经理审批.png)]
在这里插入图片描述

  • 财务审批
    在这里插入图片描述

3)绘制结束事件

在这里插入图片描述
在这里插入图片描述

4)绘制流向

1.选中流程节点

在这里插入图片描述

2.鼠标左键按住右上角箭头进行拖动

拖向需要指向的节点即可。
流向-拖动指向

3.最终流程图效果

在这里插入图片描述

五、流程操作

5.1 流程定义

流程定义是线下按照bpmn2.0标准去描述 业务流程,通常使用idea中的插件对业务流程进行建模。

使用idea下的designer设计器绘制流程,需要成两个文件:.bpmn.png

1)bpmn文件

已经按第三步的操作定义完成,在就是first.bpmn.xml文件。

2)png文件

直接在流程设计器中,右击后再菜单栏点击Save to PNG,然后选择文件位置,放到.bpmn文件的相同目录下即可。
在这里插入图片描述

5.2 流程部署

1)单文件部署方式

将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署。

直观的说就是把bpmn文件定义的流程和png文件存储到数据库当中。

这里使用一个单元测试来进行演示:

/**
  * 部署流程定义测试
  */
@Test
public void DeployTest() {
    
    
    // 1. 创建ProcessEngine流程引擎对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2、得到RepositoryService实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3、使用RepositoryService进行部署,定义一个流程名字,把bpmn和png部署到数据库中
    Deployment deployment = repositoryService.createDeployment()    // 创建一个部署流程
        .name("出差申请流程")                                 	// 给部署流程命名
        .addClasspathResource("a-bpmn/first.bpmn20.xml")    	// 添加类路径下的资源文件
        .addClasspathResource("a-bpmn/first.png")
        .deploy();												// 执行流程部署
    // 4、输出部署信息
    System.out.println("流程部署id:" + deployment.getId());
    System.out.println("流程部署名称:" + deployment.getName());
}

运行之后,activiti主要操作了4张表:

  • act_re_deployment :流程部署表,每部署一个流程就会多一条数据
    在这里插入图片描述

  • act_re_procdef:流程定义表
    在这里插入图片描述

    这里面比较重要的字段:

    • KTY_:也就是之前流程定义时的ID属性
    • RESOURCE_NAME_bpmn文件的资源路径
    • DGRM_RESOURCE_NAME_png文件的资源路径
  • act_ge_bytearray :通用的流程资源表
    在这里插入图片描述

    • 查看图像的方法:用Navicat的话选择开始事务旁边的下拉框为图像,再选中想看的png文件,下方就能显示了
      在这里插入图片描述

    • 查看bpmn文件的方法
      在这里插入图片描述

    其实这就是一个xml文件即可。

  • act_ge_property:系统相关属性
    在这里插入图片描述

完成上述操作后Activiti会将上边代码中指定的bpmn文件和图片文件保存在Activiti数据库中的相关表中。

小结:

act_re_deploymentact_re_procdef一对多关系.

一次部署在流程部署表生成一条记录,但一次部署可以部署多个流程定义,每个流程定义在流程定义表生成一条记录。

每一个流程定义在act_ge_bytearray会存在两个资源记录,bpmnpng

建议:

一次部署一个流程,这样部署表和流程定义表是一对一有关系,方便读取流程部署及流程定义信息。

2)压缩包部署方式

这种方式需要把bpmn文件和png文件打成一个.zip的压缩包。

/**
  * 部署流程--压缩包部署方式
  */
@Test
public void deployByZipTest() {
    
    
    // 1. 创建ProcessEngine流程引擎对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.定义字节输入流
    InputStream inputStream = this.getClass()
        .getClassLoader()
        .getResourceAsStream("bpmn/evection.zip");
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    // 3.获取repositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 4.流程部署
    Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
    // 5、输出部署信息
    System.out.println("流程部署id:" + deployment.getId());
    System.out.println("流程部署名称:" + deployment.getName());
}

当有很多流程的时候,很多时候都是使用这种压缩包的形式来进行流程的部署

前端直接把这个压缩包上传上来,后端拿到后,进行流程部署即可。

5.3 启动流程实例

/**
  * 启动流程实例
  */
@Test
public void startProcessTest(){
    
    
    // 1、创建ProcessEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2、获取RunTimeService
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 3、根据流程定义Id启动流程,并获取流程实例
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myEvection");
    // 输出内容
    System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
    System.out.println("流程实例id:" + processInstance.getId());
    System.out.println("当前活动Id:" + processInstance.getActivityId());
}

所操作数据表如下:

  • act_hi_actinst :流程实例执行历史
    在这里插入图片描述

  • act_hi_identitylink :流程的参与用户历史信息
    在这里插入图片描述

  • act_hi_procinst :流程实例历史信息
    在这里插入图片描述

  • act_hi_taskinst : 流程任务历史信息
    在这里插入图片描述

  • act_ru_execution:流程执行信息
    在这里插入图片描述

  • act_ru_identitylink :流程的参与用户信息,记录当前用户需要做的流程实例的关联关系
    在这里插入图片描述

  • act_ru_task :任务信息
    在这里插入图片描述

5.4 流程任务查询

一个流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。

/**
  * 查询当前个人待执行的任务
  */
@Test
public void findTaskListTest() {
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取TaskService
    TaskService taskService = processEngine.getTaskService();
    // 3.根据流程key 和 任务负责人 查询任务
    String assignee = "小王";
    List<Task> list = taskService.createTaskQuery()
        .processDefinitionKey("myEvection")     	//流程Key
        .taskAssignee(assignee)                 	//只查询当前任务负责人的任务
        .list();

    list.forEach(task -> {
    
    
        System.out.printf("-----------------------------------------");
        System.out.println("流程实例id:" + task.getProcessInstanceId());
        System.out.println("任务id:" + task.getId());
        System.out.println("任务负责人:" + task.getAssignee());
        System.out.println("任务名称:" + task.getName());
    });
}

执行上述代码后,就可以查询出来小王的所有待办任务了:
在这里插入图片描述

5.5 流程任务处理

任务负责人查询属于自己的待办任务,选择相应的任务进行处理,并且去完成自己的代办任务。

/**
  * 完成个人代办任务
  */
@Test
public void completTaskTest(){
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2. 获取TaskService实例
    TaskService taskService = processEngine.getTaskService();
    // 3.根据流程key 和 任务的负责人 查询任务
    String assignee = "小王";
    Task task = taskService.createTaskQuery()
        .processDefinitionKey("myEvection")     //流程Key
        .taskAssignee(assignee)                 //只查询当前任务负责人的任务
        .singleResult();
    // 4.完成待办任务,参数:任务id
    taskService.complete(task.getId());
}

执行上述代码之前,act_ru_task表中的内容还是第一步创建出差申请单:
在这里插入图片描述

执行上述代码之前,act_ru_task表中的内容已经替换为了当前流程:
在这里插入图片描述

而之前的流程就放到了act_hi_taskinst历史的任务实例当中去了:
在这里插入图片描述

5.6 流程定义信息查询

查询流程相关信息也是经常要用到的操作,通常包含对流程定义,流程部署,流程定义版本的查询。

/**
  * 查询流程定义
  */
@Test
public void findProcessDefinitionTest(){
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取repositoryService资源管理实例
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3.通过repositoryService获取ProcessDefinitionQuery对象
    ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
    // 4.查询出当前所有的流程定义
    List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("myEvection")  // 根据流程key(id)进行查询
        .orderByProcessDefinitionVersion()                 // 按照版本排序
        .desc()                                            // 倒序
        .list();
    // 5.输出流程定义信息
    definitionList.forEach(processDefinition -> {
    
    
        System.out.printf("----------------------------------------");
        System.out.println("流程定义 id="+processDefinition.getId());
        System.out.println("流程定义 name="+processDefinition.getName());
        System.out.println("流程定义 key="+processDefinition.getKey());
        System.out.println("流程定义 Version="+processDefinition.getVersion());
        System.out.println("流程部署ID ="+processDefinition.getDeploymentId());
    });
}

5.7 流程删除

如果某个流程不想要了,可以选择删除该流程。

/**
  * 流程删除(它影响的表和流程定义时的表)
  */
@Test
public void deleteDeploymentTest() {
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.通过流程引擎获取repositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3.删除流程定义,如果该流程定义已有流程实例启动,那么删除时就会报错
    String deploymentId = "1";       // 流程部署id
    repositoryService.deleteDeployment(deploymentId);
    // 4.设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除
    //   设置为false非级别删除方式,如果该流程定义已有流程实例启动,那么删除时就会报错
    //repositoryService.deleteDeployment(deploymentId, true);
}

注意:

  • 使用repositoryService删除流程定义,历史表信息不会被删除

  • 如果该流程定义下没有正在运行的流程,则可以用普通删除

  • 项目开发中级联删除操作一般只开放给超级管理员使用.

5.8 流程资源下载

虽然jdbc也可以把blob类型的数据读取出来,但是这里主要还是用Activiti提供的API来进行操作。

1)引入commons-io的Maven依赖

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

2)代码示例

/**
  * 下载资源文件 
  * @throws IOException
  */
@Test
public void  findBpmnFileTest() throws IOException {
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.通过流程引擎获取repositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3、得到流程定义查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("myEvection")
        .singleResult();
    // 4、通过流程定义信息,得到部署ID
    String deploymentId = processDefinition.getDeploymentId();
    // 5、通过repositoryService的方法,实现读取图片信息和bpmn信息
    //  5.1 获取png图片的流
    InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
    //  5.2 获取bpmn文件的流
    InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
    //  6、构造OutputStream流
    File file_png = new File("d:/first.png");
    File file_bpmn = new File("d:/first.bpmn");
    FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
    FileOutputStream pngOut = new FileOutputStream(file_png);
    //  7、输入流,输出流的转换
    IOUtils.copy(pngInput, pngOut);
    IOUtils.copy(bpmnInput, bpmnOut);
    //  8、关闭相关流
    pngOut.close();
    bpmnOut.close();
    pngInput.close();
    bpmnInput.close();
}

这只是在本地是这么操作的,是比较原生的写法,更有利于理解。

如果是在web项目中,就直接按文件下载的方式传输数据就可以了。

5.9 流程历史信息的查看

/**
  * 查看历史信息
  */
@Test
public void findHistoryInfoTest(){
    
    
    // 1.创建流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取HistoryService
    HistoryService historyService = processEngine.getHistoryService();
    // 3.获取 actinst表的查询对象
    HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
        // 查询 actinst表,条件:根据 InstanceId 查询(等价于: instanceQuery.processInstanceId("2501");  根据 InstanceId 查询)
    instanceQuery.processDefinitionId("myEvection:1:4");
    // 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
    instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
    // 查询所有内容
    List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
    // 输出查询结果
    activityInstanceList.forEach(hi -> {
    
    
        System.out.println("<==========================>");
        System.out.println(hi.getActivityId());
        System.out.println(hi.getActivityName());
        System.out.println(hi.getProcessDefinitionId());
        System.out.println(hi.getProcessInstanceId());
    });
}

5.10 流程实例的挂起

在一些情况下可能由于流程变更需要将当前运行的流程暂停(挂起)而不是直接删除,流程暂停后将不会继续执行。

比如每月的最后一天不处理出差申请呀,财务审批流程改变需要暂停已经发起的审批等等。

流程实例的挂起也有下面两种方式:

1)将单个流程实例的挂起和激活

/**
  * 挂起、激活单个流程实例
  */
@Test
public void suspendSingleProcessInstance(){
    
    
    // 1. 创建ProcessEngine流程引擎对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2、获取RuntimeService
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 3、通过RuntimeService获取流程实例对象
    ProcessInstance instance = runtimeService.createProcessInstanceQuery()
        .processInstanceId("27501")
        .singleResult();
    // 4、得到当前流程实例的暂停状态,true-已暂停  false -激活
    boolean suspended = instance.isSuspended();
    // 5、获取流程实例id
    String instanceId = instance.getId();
    // 6、判断是否已经暂停,如果已经暂停,就执行激活操作
    if(suspended){
    
    
        // 如果已经暂停,就执行激活
        runtimeService.activateProcessInstanceById(instanceId);
        System.out.println("流程实例id:"+instanceId+"已经激活");
    }else {
    
    
        // 7、如果是激活状态,就执行暂停操作
        runtimeService.suspendProcessInstanceById(instanceId);
        System.out.println("流程实例id:"+instanceId+"已经暂停");
    }
}

2)将全部流程实例的挂起和激活

/**
  * 全部流程实例的挂起和激活
  * suspend 暂停
  */
@Test
public void suspendAllProcessInstance(){
    
    
    // 1. 创建ProcessEngine流程引擎对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2、获取Repositoryservice
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3、根据流程定义Key查询流程定义,获取流程定义的查询对象
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                                                           .processDefinitionKey("myEvection")
                                                           .singleResult();
    // 4、获取当前流程定义的实例是否都是挂起状态
    boolean suspended = processDefinition.isSuspended();
    // 5、获取流程定义的id
    String definitionId = processDefinition.getId();
    // 6、如果是挂起状态,改为激活状态
    if(suspended){
    
    
        // 如果是挂起,可以执行激活的操作,参数1:流程定义id 参数2:是否激活,参数3:激活时间
        repositoryService.activateProcessDefinitionById(definitionId, true, null);
        System.out.println("流程定义id:"+definitionId+",已经被激活");
    }else {
    
    
        // 7、如果是激活状态,改为挂起状态,参数1:流程定义id 参数2:是否暂停 参数3 :暂停的时间
        repositoryService.suspendProcessDefinitionById(definitionId, true, null);
        System.out.println("流程定义id:"+definitionId+",已经被挂起");
    }
}

这两者的区别也就是一个是查询实例,拎一个查询的是流程定义罢了

六、小结

上面已经演示了常见的BPMN操作和流程操作,更偏向于新手理解相关API

但是在绘制流程图的时候,暂时都是把责任人等信息写死了。这显然在实际的业务中是难以适应业务的变化的。

并且流程审批的时候,下一个审批人根本不知道审批的具体内容是啥。

比如上面的出差申请,具体要出差去哪,出差几天,都是不知道的。

因为这些数据在业务系统当中,而不是在Activiti中。

只有两者联动起来才能完成实际的业务开发需求,这个是需要注意的。

下一篇博客会讲诉Activiti的中流程实例、用户任务、流程变量以及网关的使用,更加贴合实际开发。

猜你喜欢

转载自blog.csdn.net/qq_44749491/article/details/130499960