Activiti快速入门(三)—— 部署启动审批流程

1. 部署流程

部署流程的2大前提:

  • 流程引擎
  • 流程定义文件
    流程引擎就不再多说了,无论是前面的哪种方式获取的,在部署上是一致的。
    本篇的相关源码:GitHub

流程定义文件需要通过IDEA的、eclipse 中的 Activiti designer插件或者Web 编辑器Activiti modeler来的设计,且得到一个以.bpmn为后缀的流程定义文件
安装完成插件后在Eclipse 右键new—Other—Activiti Diagram即可新建流程定义文件
Activiti designer插件设计界面如下

eclipse activiti modeler.PNG

IDEA 安装完成插件后在任意目录下右键new BpmnFile 即可新建流程定义文件
actibpm 插件设计界面如下

IDEA actiBPM.PNG

这里如果遇到actiBPM插件中文乱码则需要在IDEA安装目录下找到idea.exe.vmoptions和idea64.exe.vmoptions这两个文件,且在最后一行添加如下配置

-Dfile.encoding=UTF-8

设计器中有很多元素可以供我们选择,但是在入门阶段,我认为只需要关注StartEvent UserTask EndEvent 这3个元素即可,简单场景下,也只会用到这3个。

设计器元素

在我们定义好了流程定义文件后,就可以使用流程引擎部署它了,前提是你已经搭建好了Activiti的项目环境,这里可以参考快速入门(二)

@Test
    public void deployMyProcess() throws FileNotFoundException {
        //几乎所有需要操作到流程文件,或者读取流程相关信息的,都是使用仓库服务repositoryService
        //部署流程 这里跟构建流程引擎的方式差不多,都是使用了构建者模式
        //1. 首先创建DeploymentBuilder对象,通过这个对象,我们可以指定要加载的流程定义文件,以及一些其他属性。
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        //2.1 加载流程文件,从类路径加载
//        deploymentBuilder.addClasspathResource("AskForLeave.bpmn");
        //2.使用InputStream加载 (替换自己的文件路径)
        FileInputStream fileInputStream = new FileInputStream("E:\\DemoProjects\\ActivitiExample\\ActivitiFirstDeploy\\src\\main\\resources\\AskForLeave.bpmn");
        deploymentBuilder.addInputStream("AskForLeave.bpmn", fileInputStream);
        deploymentBuilder.name("测试部署请假流程");
        //还有将多个bpmn文件打包批量,以及字符串这两种部署方式(这里暂时不介绍这两种方式)
        //执行部署,直到调用deploy()方法才是真正的部署到引擎中了
        //同时会在act_ge_bytearray ,act_re_deployment ,act_re_procdef这3个表中插入相关信息
        //与以前的教程不一样的是,别的教程中会经常将流程图片和流程bpmn文件一起部署,但我认为有点多余了,因为在部署bpmn的时候会自动生成流程图片
        Deployment deploy = deploymentBuilder.deploy();
        //这个是创建一个部署查询对象查询,查询的是act_re_deployment表这个表记录的就是deploymentBuilder对象所附加的属性
        long count = repositoryService.createDeploymentQuery().count();
        System.out.println("部署时间:"+deploy.getDeploymentTime());
        Assert.assertEquals(1, count);
    }

在流程部署完成后,我们来看一看数据库的变化,学习Activiti引擎就必须要对这20多张表有一个比较深刻的了解。
部署一个流程,数据库中有act_ge_bytearray ,act_re_deployment ,act_re_procdef这3个表会发生变化

  • act_re_deployment 中插入了deploymentBuilder构建时所附带的信息,比如name,key,category等但是这几个信息都是非必填项,目前即便是为空也不影响,主要是为了定义和区分不同的部署信息。


    act_re_deployment
  • act_re_procdef 这个表中插入的是比较重要的信息了,这里面的Name,Key,Category 等等都是流程引擎从我们自己设计的AskForLeave.bpmn这个流程定义文件中解析出来的属性,在实际业务中最常关联的也就是这个表中的属性。


    act_re_procdef
  • act_ge_bytearray在第一节中有提到过ge系列的是通用数据,可以看成是系统将我们的bpmn文件以及它解析这个文件生成的流程图片以二进制的形式保存到这个表中了


    act_ge_bytearray

当流程部署完成后,我们就可以调用Api启动这个请假流程

2. 启动流程

启动流程的方法有很多重载最主要的就是下面展示的两种方式

    @Test
    public void startAskLeaveProcess() {
        //1.通过流程定义ID启动,这个ID就是act_re_procdef的主键ID 例如Leave:1:4
        //runtimeService.startProcessInstanceById();
        //2.通过流程定义的Key启动,这个Key是在我们画流程图的时候输入的ID
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(LEAVEKEY);
        Assert.assertEquals(LEAVEKEY, processInstance.getProcessDefinitionKey());
        System.out.println("启动时间:"+processInstance.getStartTime());
    }

启动流程成功后,我们来看看数据库之中的变化

  • act_ru_task RU当前任务表 是Runtime的缩写
    证明这个表中存放的是当前系统中的任务(也就是设计流程时的Task类型的节点)其中最重要的属性有如下:

ID:当前任务的ID(可以使用TaskService查询)
EXECUTION_ID_: 执行ID
PROC_INST_ID_: 流程实例ID 这个属性非常重要,它贯穿了流程实例的整个生命周期,从启动到结束到历史记录查询都非常依赖于它,它是流程实例的唯一标识
Name: 当前任务节点的名称(自己定义的)
TASK_DEF_KEY_: 任务定义Key 用户定义的任务Key

act_ru_task当前任务表
  • act_ru_execution 当前执行表
    这个表可以看做是比Task记录的节点更详细的表,因为act_ru_task仅仅会保存Task属性的节点,而这个表会记录所有的节点,而且它主要用于子流程,或者并行流程复杂用法的时候才需要特别关心,现在暂时不用详细了解
act_ru_execution 当前执行表

除了RU系列运行时查询的表,还会有对应的历史记录保存表

  • act_hi_taskinst 历史任务流程实例表 保存的内容大体跟ru_task一致


    历史任务表
  • act_hi_procinst历史流程实例表
    这个表存储的信息也比较重要,主要用于查询流程的详细信息,比如启动时间,启动人结束时间等等


    流程实例表
  • act_hi_actinst流程活动实例表 这个表从存储的内容来说和act_ru_execution比较像,它会把流程所有已经流转过的节点都记录下来


    流程活动实例表

    从上述数据库变化中大家应该可以初步熟悉流程表的核心了,启动任何一个流程上面的5张表都会产生新记录,也可以说Activiti的核心功能是重度依赖这5张表的

Activiti中有不少的Query查询对象,他们是通过各个Service获取的,目的也主要是查询数据库表,我们就以上面的act_hi_procinst为例

    @Test
    public void listAllProcess(){
        //创建Query查询对象 可以在这个对象后面调用各种方法实现条件查询,排序等
        HistoricProcessInstanceQuery hisProcInstanceQuery = historyService.createHistoricProcessInstanceQuery();
        //不加查询条件
        List<HistoricProcessInstance> processInstances = hisProcInstanceQuery.list();
        for (HistoricProcessInstance processInstance : processInstances) {
            System.out.println(processInstance.getId());
            System.out.println(processInstance.getDeploymentId());
            System.out.println(processInstance.getProcessDefinitionName());
        }
        //根据流程定义名称查询
        List<HistoricProcessInstance> processInstances1 = hisProcInstanceQuery.processDefinitionName("我的流程").list();
        //根据流程实例ID查询
        HistoricProcessInstance processInstance = hisProcInstanceQuery.processInstanceId("2501").singleResult();
        //查询未结束的流程
        List<HistoricProcessInstance> processInstances2 = hisProcInstanceQuery.unfinished().list();
        Assert.assertEquals(1, processInstances1.size());
        Assert.assertNotNull(processInstance);
        Assert.assertEquals(1, processInstances2.size());
    }

办理任务

流程启动之后肯定是需要办理任务的,act_ru_task中的每一行就是我们需要处理的任务,首先熟悉一下如何查询他们,同上依然先创建Query对象

    @Test
    public void getTask() {
        //如果你的数据库中只有一条流程那么也是可以将list()改为singleResult()的
        List<HistoricProcessInstance> processInstances = historyService
                .createHistoricProcessInstanceQuery().unfinished().list();
        List<Task> tasks = new ArrayList<>();
        for (HistoricProcessInstance processInstance : processInstances) {
            String processInstanceId = processInstance.getId();
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
            tasks.add(task);
        }
        tasks.forEach(task -> System.out.println(task.getName()));

        for (Task task : tasks) {
            //TaskService可以完成某个任务的审批,使流程流转到下一节点,比如用户申请审批完成后到达领导审批节点
            taskService.complete(task.getId());
            Task result = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            System.out.println(result.getName());
        }
    }

控制台输出如下

用户申请
领导审批

再看数据库之前的那一行ID为2505的用户申请已经没有了,新增了5002的领导审批记录,这些都是流程引擎自动为我们完成的


当前任务

我们在历史活动和历史任务中都会插入一条新记录(新的节点或者任务),且更新之前办理的任务的END_TIME用来表示他们的处理时间


历史活动实例表
历史任务实例表

在我们最终调用TaskService.complete()完成最后一个任务的审批后,会更新历史流程实例的完成时间,这就代表这个流程已经结束了所有的ru跟他相关的都会被清空


历史流程实例

推荐阅读更多精彩内容