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跟他相关的都会被清空


历史流程实例
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容