- 时 间
- 2010年5月17日
- 基 于
- 开源BPM平台
- 作 用
- 业务流程管理
- 中文名
- activiti
- 公 司
- Alfresco软件
- 外文名
- activiti
-
Activiti初学者教程
2014-09-24 11:45:421. 初识Activiti 1.1. 工作流与工作流引擎 工作流(workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。它主要解决的是“使在多...http://wenku.baidu.com/view/bb7364ad4693daef5ff73d32.html
https://github.com/bluejoe2008/openwebflow
OpenWebFlow是基于Activiti扩展的工作流引擎,它扩展的功能包括:
- 完全接管了Activiti对活动(activity)权限的管理;
- 完全接管了Activiti对用户表(IDENTITY_XXX表)的管理;
- 允许运行时定义activity!彻底满足“中国特色”,并提供了安全的(同时也是优雅的)催办、代办、加签(包括前加签/后加签)、自由跳转(包括前进/后)、分裂节点等功能;
1. 初识Activiti
1.1. 工作流与工作流引擎
工作流(workflow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。(我的理解就是:将部分或者全部的工作流程、逻辑让计算机帮你来处理,实现自动化)
所谓工作流引擎是指workflow作为应用系统的一部分,并为之提供对各应用系统有决定作用的根据角色、分工和条件的不同决定信息传递路由、内容等级等核心解决方案。
例如开发一个系统最关键的部分不是系统的界面,也不是和数据库之间的信息交换,而是如何根据业务逻辑开发出符合实际需要的程序逻辑并确保其稳定性、易维护性和弹性。
比如你的系统中有一个任务流程,一般情况下这个任务的代码逻辑、流程你都要自己来编写。实现它是没有问题的。但是谁能保证逻辑编写的毫无纰漏?经过无数次的测试与改进,这个流程没有任何漏洞也是可以实现的,但是明显就会拖慢整个项目的进度。
工作流引擎解决的就是这个问题:如果应用程序缺乏强大的逻辑层,势必变得容易出错(信息的路由错误、死循环等等)。
1.2. BPMN2.0规范
BPMN(Business Process Model and Notation)--业务流程模型与符号。
BPMN是一套流程建模的标准,主要目标是被所有业务用户容易理解的符号,支持从创建流程轮廓的业务分析到这些流程的最终实现,知道最终用户的管理监控。
通俗一点其实就是一套规范,画流程模型的规范。流程模型包括:流程图、协作图、编排图、会话图。详细信息请google。
1.3. Activiti概述
1.3.1. Activiti由来
学习过Activiti的朋友都知道,Activiti的创始人也就是JBPM(也是一个优秀的BPM引擎)的创始人,从Jboss离职后开发了一个新的BPM引擎:Activiti。所以,Activiti有很多地方都有JBPM的影子。所以,据说学习过JBPM的朋友学起Activiti来非常顺手。
由于本人之前没有工作流及JBPM的相关基础,刚开始学习Activiti的时候可以说是无比痛苦的,根本不知道从何下手,这里也建议大家先进行工作流及BPMN2.0规范的学习,有了一定的基础后,再着手学习Activiti。
1.3.2. Activiti简介
Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调度。
Activiti 作为一个遵从 Apache 许可的工作流和业务流程管理开源平台,其核心是基于 Java 的超快速、超稳定的 BPMN2.0 流程引擎,强调流程服务的可嵌入性和可扩展性,同时更加强调面向业务人员。
Activiti 流程引擎重点关注在系统开发的易用性和轻量性上。每一项 BPM 业务功能 Activiti 流程引擎都以服务的形式提供给开发人员。通过使用这些服务,开发人员能够构建出功能丰富、轻便且高效的 BPM 应用程序。
1.4. 文档说明
以上部分对工作流、BPMN、Activiti的概念做了一个简单的介绍,目的是了解Activiti究竟是什么,能做些什么…及在学习Activiti之前需要了解的知识与技术。其中大部分文字来自Copy网上的各种资料与文档,通过总结而来的。具体的更详细的内容需自己google,参考一些官方的文档与手册。
本文档之后内容如下:
1) 下载与使用
2) 核心组件与说明
3) 入门示例
4) Eclipse中的Activiti插件的使用
本文档旨在为初学Activiti的朋友提供入门级别的参考,不会对其原理及其结构进行深层次的探究(更多是因为目前自身理解还不是很透彻),只是为大家理清思路,方便以后更深层次的学习。本文档还有一个重要的特点,那就是根据自己看官方手册的经验,教大家如何看手册从而更有效率!由于是初学,很多术语或解释难免理解有偏差,所以一定要看官方提供的文档与手册,那才是学习的最佳途径!
2. 开始学习
2.1. 必要的准备
2.1.1. 下载与了解目录
下载Activiti:下载路径,也就是官方网站的地址:http://activiti.org/download.html。下载后解压(我所使用的是5.12版本的,Activiti更新速度飞快,几乎每两个月就会有一个更新的小版本),看到如下目录:
1) database:里面存放的是Activiti使用到的数据库信息的sql文件,它支持的数据库类型如下图,使用时只需执行你自己的数据库类型的文件即可。如:你的数据库是mysql,那么就执行activiti.mysql.create.*.sql即可。
2) docs:毫无疑问,api文档是也。
3) libs:使用Activiti所需要的所有的jar包和源文件。
4) wars:官方给我们提供的示例Demo,通过使用Demo可以更加快速的了解Activiti。
2.1.2. 其他准备
使用Activiti,首先当然要有jdk了!6+版本就可以了。其次,要有一款IDE,我们当然会使用Eclipse,这里使用Juno版本。然后,web容器当然也要有,这里使用Tomcat6.0版本。然后就是Activiti的Eclipse插件了,这个后面再介绍。
2.1.3. 一分钟入门(见用户手册)
所谓的一分钟入门就是通过运行你下载的包里的wars文件夹里的activiti-explorer.war文件,以便更快的了解Activiti。将文件拷贝至Tomcat的webapps目录,启动tomcat,输入http://localhost:8080/activiti-explorer。然后你就可以开整了!总算是有一点微小的进展了。
这里需要说明的就是,这个Demo默认采用的是h2内存数据库,如果想用你自己的数据库,就需要修改web应用WEB-INF/classes目录下的db.properties。然后,按上面说的,把database里的create文件夹里的数据库文件导入你自己的数据库(如果没有修改db.properties,就不用导入了)。
Demo的具体解释与数据库配置的具体信息详见官方手册,手册已经说的很清楚了。这里需要重点了解activiti.cfg.xml的配置以及如何构建ProcessEngine(配置文件构建方式、代码构建方式)。
对Demo的使用介绍在官方文档的后面才开始介绍,这里建议应用跑起来之后,先自己试试手(可看后面介绍Demo如何使用的章节),看看如何跑一个流程、整个流程是怎么流的、并随时关注数据库表里的数据的变化等,对以后的学习很有帮助!
2.2. 核心组件介绍
2.2.1. 关键对象
1. Deployment:流程部署对象,部署一个流程时创建。
2. ProcessDefinitions:流程定义,部署成功后自动创建。
3. ProcessInstances:流程实例,启动流程时创建。
4. Task:任务,在Activiti中的Task仅指有角色参与的任务,即定义中的UserTask。
5. Execution:执行计划,流程实例和流程执行中的所有节点都是Execution,如UserTask、ServiceTask等。
2.2.2. 服务接口
1. ProcessEngine:流程引擎的抽象,通过它我们可以获得我们需要的一切服务。
2. RepositoryService:Activiti中每一个不同版本的业务流程的定义都需要使用一些定义文件,部署文件和支持数据(例如BPMN2.0 XML文件,表单定义文件,流程定义图像文件等),这些文件都存储在Activiti内建的Repository中。RepositoryService提供了对 repository的存取服务。
3. RuntimeService:在Activiti中,每当一个流程定义被启动一次之后,都会生成一个相应的流程对象实例。RuntimeService提供了启动流程、查询流程实例、设置获取流程实例变量等功能。此外它还提供了对流程部署,流程定义和流程实例的存取服务。
4. TaskService: 在Activiti中业务流程定义中的每一个执行节点被称为一个Task,对流程中的数据存取,状态变更等操作均需要在Task中完成。TaskService提供了对用户Task 和Form相关的操作。它提供了运行时任务查询、领取、完成、删除以及变量设置等功能。
5. IdentityService: Activiti中内置了用户以及组管理的功能,必须使用这些用户和组的信息才能获取到相应的Task。IdentityService提供了对Activiti 系统中的用户和组的管理功能。
6. ManagementService: ManagementService提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti系统的日常维护。
7. HistoryService: HistoryService用于获取正在运行或已经完成的流程实例的信息,与RuntimeService中获取的流程信息不同,历史信息包含已经持久化存储的永久信息,并已经被针对查询优化。
现在至少要知道有这些对象和接口。并结合Activiti Api这一章节来看,你就会对部署流程、启动流程、执行任务等操作有一个基本的概念。之后编写一个简单的单元测试,主要为了测试activiti.cfg.xml配置的是否正确,流程是否可以被部署即可。
至于与Spring的集成,一定要熟悉基于Spring配置Activiti,以及事务的处理。
3. 入门示例(参考手册中10分钟教程)
3.1. 概述
下面开始编写一个示例。这个Demo为一个“月度报表申请”流程。由“sales(销售)”组的用户制作月度报表,提交给“management(经理)”组的用户,经理审批该报表,最后结束。流程图如下:
这个Demo完成之后,我们会进行两个测试。一个是代码中的单元测试,就是跑一遍流程,看一下流程在跑的过程中输出的信息;一个是我们将编辑好的bpmn20.xml文件通过我们之前一分钟入门的示例activiti-explorer应用导入进去,查看它的流程图,并完整执行一遍流程。
在编写这个Demo之前,至少要了解Activiti与Spring如何集成、XxxService各自的任务与作用,并完成上一章的Demo。
3.2. 流程文件xxx.bpmn20.xml
首先,我们就来编写这个流程的bpmn20.xml文件。
<definitions id="definitions"
targetNamespace="http://activiti.org/bpmn20"
xmlns:activiti="http://activiti.org/bpmn"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process id="financialReport" name="Monthly financial report reminderprocess">
<startEvent id="theStart" />
<sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' />
<userTask id="writeReportTask" name="Write monthly financial report" >
<documentation>
Write monthly financial reportfor publication to shareholders.
</documentation>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>sales</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' />
<userTask id="verifyReportTask" name="Verify monthly financial report" >
<documentation>
Verify monthly financial reportcomposed by the accountancy department.
This financial report is goingto be sent to all the company shareholders.
</documentation>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>management</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' />
<endEvent id="theEnd" />
</process>
</definitions>这里对部分代码进行解释。
1) 文件的开头部分,这里的id对于Activiti来说, 应该叫做key。创建流程实例时,会根据此id来得到这个流程。
2) 开始流程。
3) 顺序流(就是连接各个节点的指向线)
sourceRef和targetRef分别为起始节点和目标节点。
4) 描述用户任务
id为该用户任务的标识。
documentation为该用户任务的描述。
5) 分配用户
可以把任务分配给指定的用户,也可以分配给指定的组,并且可以有多个,详见用户手册。
3.3. Spring配置文件
这里配置了数据源、事务管理、流程引擎及几个必要的xxxService。这里数据源使用的是dbcp。数据库信息就配置成你自己本地数据库的信息,如果不会配置。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="initialSize" value="20" />
<property name="maxActive" value="50"/>
<property name="maxIdle" value="20"/>
<property name="minIdle" value="10"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine"
factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine"
factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine"
factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine"
factory-method="getManagementService" />
<tx:annotation-driven transaction-manager="transactionManager" />
注:bpmn20.xml文件中用到了两个用户组(sales、management),是因为我们启动Tomcat运行activiti-explorer应用初始化时自动就会往数据库里添加一些数据,其中用户组的表中就会添加几条记录,其中就包括这两个组,所以不用管它怎么来的,总之数据库里有这两个组就对了。而应用默认使用的是内存数据库,服务一停止数据也就没有了。所以为了进行单元测试,需要按前面讲的修改数据库配置的方法:
把activiti-explorer应用的数据库配置改成你自己的本地数据库的信息,我使用的是Mysql数据库。再启动tomcat运行应用(目的就是为了让数据库有数据),这时你的本地数据库就有数据了,可以编写测试用例进行单元测试了。
3.4. 编写测试用例
1) 读取Spring配置文件,注入流程所需的Service
2) 编写测试方法
@Test
public void monthtest() {
// 部署流程定义
repositoryService.createDeployment().addClasspathResource("myProcess.bpmn20.xml").deploy();
// 启动流程实例
String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();
// 获得第一个任务
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("sales").list();
for (Task task : tasks) {
System.out.println("Following task is available for sales group: " + task.getName());
// 认领任务这里由foozie认领,因为fozzie是sales组的成员
taskService.claim(task.getId(), "fozzie");
}
// 查看fozzie现在是否能够获取到该任务
tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
for (Task task : tasks) {
System.out.println("Task for fozzie: " + task.getName());
// 执行(完成)任务
taskService.complete(task.getId());
}
// 现在fozzie的可执行任务数就为0了
System.out.println("Number of tasks for fozzie: "
+ taskService.createTaskQuery().taskAssignee("fozzie").count());
// 获得第二个任务
tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
System.out.println("Following task is available for accountancy group:" + task.getName());
// 认领任务这里由kermit认领,因为kermit是management组的成员
taskService.claim(task.getId(), "kermit");
}
// 完成第二个任务结束流程
for (Task task : tasks) {
taskService.complete(task.getId());
}
// 核实流程是否结束,输出流程结束时间
HistoricProcessInstancehistoricProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
}
3) 运行示例,Demo完成。这就是一个最简单的流程,通过这个流程,了解到Activiti中流程是怎么流的,我们怎么控制它。
3.5. 导入activiti-explorer
1) 单元测试完成后,我们可以将该bpmn20.xml文件导入之前我们部署的activiti-explorer应用中:点击流程的流程设计工作区,点击导入,将刚才我们编写的文件导入进去。
2) 导入之后在右上角点击部署。
3) 在已部署流程定义中我们可以看到这个流程,及它的流程图。
4) 点击启动流程,该流程就会被启动,再点击任务中,列队就会有该任务了,而且是分配给sales的,这正是我们定义流程时所分配给的用户组啊。注意,现在只有sales组的用户才可以看到此任务!
5) sales组的用户进入之后点击“签收”,该任务就分配给该用户了,然后该用户就可以进行处理,也就是在代办任务和受邀里。
6) 进去之后点击完成任务,该任务就流到下一个节点,也就是流转到management组中去了,要由management组的用户去处理。
7) 于是这时候,队列中management组就有一个新的任务了,等待management组的成员来“签收”,并完成任务。该流程也就结束了。
8) 此时就可以查看历史任务了,就是我们这里的“已归档”。用户完成的任务会在这里显示。
这就是整个Demo的编写、测试过程。这样一个小小的流程基本能够体现出Activiti的功能及使用方法。
4. Eclipse中的Activiti插件
Activiti有一个Eclipse插件,Activiti Eclipse Designer,可用于图形化建模、测试、部署 BPMN 2.0的流程。这样就不用我们自己去编写繁琐的流程文件了。具体安装方法见手册。
4.1. 安装
打开 Help-> Install New Software.在如下面板中 , 点击 Add 按钮, 然后填入下列字段:
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
然后一步步的安装就可以了。
4.2. 使用
至于如何使用,文档中介绍的非常详细,这里基于我初次使用的经验,强调几点:
1) 安装之后将“保存bpmn文件时创建图片文件”勾选上。这样你每次保存bpmn文件时,会为你自动创建图片文件。
2) 节点的所有属性可在properties控制台中设置。
3) 在使用设计器之前,先去钻研BPNM2.0规范吧,了解BPNM结构(可参看用户手册),才能画出符合逻辑且完美的流程图。
该例为使用Activiti Eclipse Designer设计的“请假”流程图。
-
activiti
2017-12-29 09:42:07将压缩包内activiti文件夹放入Myeclipse\dropins文件夹内并修改activiti文件夹内Link文件指向自己的目录重启Myeclipse(这时打开bpmn文件仍会报错)。 2.将压缩包内patches文件放入Myeclipse\dropins文件夹内,再次...1.安装插件
1.将压缩包内activiti文件夹放入Myeclipse\dropins文件夹内并修改activiti文件夹内Link文件指向自己的目录重启Myeclipse(这时打开bpmn文件仍会报错)。
2.将压缩包内patches文件放入Myeclipse\dropins文件夹内,再次重启Myeclipse即可打开activiti流程编辑器。注:patches夹与activiti文件夹同时放入dropins安装不会成功!
参考网址:http://blog.csdn.net/u012027337/article/details/51743235
其它详细说明:
1.解压下载的插件包后,先把activiti文件夹复制到Myeclipse10的安装路径的dropins文件夹中,此时重启Myeclipse10,然后新建---》other---》可以看到有Activiti项目可以创建。
2.修改activiti文件夹内Link文件指向自己的安装目录。此时创建Activiti Project 没有问题,而创建Activiti Diagram就可能会报此前遇到的问题,此时就需要patches插件
3.安装patches插件,把该插件复制到此前activiti放置的目录,也就是还是dropins文件夹:
此时再次重启Myeclipse10,然后新建Activiti diagram 就没有问题了。需要注意的是:patches夹与activiti文件夹同时放入dropins安装不会成功
2.表说明ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU_*: 'RU'表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_ID_*: 'ID'表示identity。 这些表包含身份信息,比如用户,组等等。
ACT_HI_*: 'HI'表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE_*: 'GE'表示general。通用数据, 用于不同场景下,如存放资源文件。 -
Activiti
2017-05-01 21:58:37关于学习Activiti的过程:直接使用官方文档进行学习,希望会有好的效果。 一.首先是现在官方的包,在其中拿到activiti-explorer.war包,将其部署到tomcate下进行访问,看看需要新学习的是一个什么东东。了解一个大概...关于学习Activiti的过程:直接使用官方文档进行学习,希望会有好的效果。
一.首先是现在官方的包,在其中拿到activiti-explorer.war包,将其部署到tomcate下进行访问,看看需要新学习的是一个什么东东。了解一个大概,在这其中可以看看随意别人博客中写的对Activiti的理解。
二.进入学习:
介绍ProcessEngineConfiguration的初始化,首先可以将其写为一个activiti.cfg.xml的配置文件,配置其一系列的属性。当然这一切都可以在java中写一个类去初始化。当然我们还可以交给spring,依赖注入帮我们初始化。
1).java初始化如下:可以用不同的初始化类或者将配置信息放到(activiti.cfg.xml)进行初始化
/*ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource);
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);
ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();*/
ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE)
.setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000")
.setAsyncExecutorEnabled(true)
.setAsyncExecutorActivate(false)
.buildProcessEngine();
2).spring 配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
<property name="asyncExecutorEnabled" value="true" />
<property name="asyncExecutorActivate" value="false" />
<property name="mailServerHost" value="mail.my-corp.com" />
<property name="mailServerPort" value="5025" />
</bean>
</beans>
三.构造ProcessEngine Bean(有多个类可以创建,适用于不同的环境中)
activiti.cfg,必须包含一个xml bean id“processEngineConfiguration” <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
org.activiti.engine.impl.cfg。StandaloneProcessEngineConfiguration:流程引擎中使用一个独立的方法。Activiti会照顾的事务。默认情况下,数据库才会在发动机启动时检查(和抛出异常,如果没有Activiti模式或模式版本是错误的)。
org.activiti.engine.impl.cfg。StandaloneInMemProcessEngineConfiguration:这是一个方便的类进行单元测试。Activiti会照顾的事务。默认使用一个H2内存数据库。时将创建和删除数据库引擎靴子和关闭。
当使用这个,可能不需要额外的配置(除了当使用例如作业执行器或邮件功能)。
org.activiti.spring.SpringProcessEngineConfiguration:当使用流程引擎中使用Spring的环境。有关更多信息,请参见Spring集成部分。
org.activiti.engine.impl.cfg。JtaProcessEngineConfiguration:使用引擎运行时在独立模式下,JTA事务。
四.数据库的配置:有两种方式初始化数据库信息,avtiviti会选择直接用jdbc ,支持的数据库有 h2 mysql oracle postgres db2 mssql
1).构建基于提供的数据源JDBC属性将会默认MyBatis连接池设置。下面的属性可以被设置为调整连接池(取自MyBatis文档):
jdbcMaxActiveConnections: 活动连接,连接池的数量最多可以包含在任何时间。默认的是10
jdbcMaxIdleConnections: 空闲连接,连接池的数量最多可以包含在任何时间。
jdbcMaxCheckoutTime: 以毫秒为单位的时间可以检查从连接池中连接之前,有力地回来了。默认是20000(20秒)。
jdbcMaxWaitTime: 这是低水平设置,使池中有机会打印日志的连接状态和re-attempt收购情况采取不同寻常的长(为了避免失败永远默默地如果池配置错误的)默认是20000(20秒)。
2).还有就是使用dataSource
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/activiti" />
<property name="username" value="activiti" />
<property name="password" value="activiti" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
...
两种方式都可以使用以下属性:
databaseType:一般不需要指定这个属性是自动从数据库连接元数据分析。应该只被指定,以防自动检测失败。可能的值:{ h2、mysql、甲骨文、postgres,该软件,db2 }。这个属性是必需的,当不使用默认的H2数据库。
这个设置将决定哪个创建/删除脚本将使用和查询。请查看受支持的数据库部分支持哪些类型的概述。
databaseSchemaUpdate:允许设置策略来处理流程引擎启动和关闭的数据库模式。
false(缺省值):检查DB模式对库的版本流程引擎时被创建并抛出一个异常,如果版本不匹配。
true:在构建流程引擎,执行检查和更新模式的性能
create-drop:创建模式时,流程引擎时被创建和滴模式流程引擎被关闭。
当然datasource可以选择jndi 使用如下:
1.将rest项目(也在下周的包内)activiti-webapp-explorer2/src/main/webapp/WEB-INF/activiti-standalone-context.xml 和 activiti-webapp-rest2/src/main/resources/activiti-context.xml 下的dbProperties 和 dataSource 这两个Bean删除 增加
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/activitiDB"/>
</bean>
接下来,我们需要添加上下文。xml文件包含默认H2配置。这些将由您的JNDI配置覆盖,如果它的存在。Activiti Explorer,替换文件activiti-webapp-explorer2 / src / main / webapp / meta - inf /上下文。xml包含以下:
<Context antiJARLocking="true" path="/activiti-explorer2">
<Resource auth="Container"
name="jdbc/activitiDB"
type="javax.sql.DataSource"
scope="Shareable"
description="JDBC DataSource"
url="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000"
driverClassName="org.h2.Driver"
username="sa"
password=""
defaultAutoCommit="false"
initialSize="5"
maxWait="5000"
maxActive="120"
maxIdle="5"/>
</Context>
Activiti休息webapp,添加activiti-webapp-rest2 / src / main / webapp / meta - inf /上下文。xml包含以下:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/activiti-rest2">
<Resource auth="Container"
name="jdbc/activitiDB"
type="javax.sql.DataSource"
scope="Shareable"
description="JDBC DataSource"
url="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=-1"
driverClassName="org.h2.Driver"
username="sa"
password=""
defaultAutoCommit="false"
initialSize="5"
maxWait="5000"
maxActive="120"
maxIdle="5"/>
</Context>
五.配置数据库中Activiti需要用到的表
1.最简单的方法:1)activiti-engine jar添加到类路径中
2)添加一个合适的数据库驱动程序
3)添加一个Activiti配置文件(activiti.cfg.xml)到您的类路径,指向您的数据库(参见数据库配置部分)
4)执行DbSchemaCreate类的主要方法
2.也可以直接执行sql创建:Activiti的SQL DDL语句可以找到下载页面或在Activiti分布文件夹中,数据库中的子目录。脚本引擎也在jar(activiti-engine-x.jar),在包org/activiti/db/create(删除文件夹包含撤销语句)。
3.说明 1.引擎.引擎执行所需的表。必需的。
1.身份:表包含用户、组和会员的用户组。应该使用这些表是可选的,当使用附带的默认身份管理引擎。
2.历史:包含历史和审计信息的表。可选:不需要当历史级别被设置为空。请注意,这也将禁用某些功能(如评论任务)在历史数据库中存储数据。
4.表的含义
1. ACT_RE_ *:代表库。与此表前缀包含静态信息如流程定义和流程资源(图片、规则等)。
2. ACT_RU_ *:RU代表运行时。这些流程实例的运行时包含运行时数据的表,用户任务,变量,工作,等等。Activiti只存储运行时数据流程实例执行过程中,一个流程实例结束的时候和删除记录。这使运行时表小而快。
3. ACT_ID_ *:ID代表身份。这些表包含身份信息,如用户、团体等。
4. ACT_HI_ *:HI,代表历史。这些表包含历史数据,如过去的流程实例变量,任务等等。
5. ACT_GE_ *:一般数据,用于各种用例。
5.数据库升级问题:
<beans >
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- ... -->
<property name="databaseSchemaUpdate" value="true" /> <!-- 最好配置为false -->
<!-- ... -->
</bean>
</beans>
6. 从版本5.17.0 Activiti提供了一个异步执行程序以及作业执行器, ManagedJobExecutor 和 ManagedAsyncJobExecutor
7. Job executor 的激活
JobExecutor是一个组件,它管理几个线程火计时器(后来也异步消息)。对于单元测试场景,它是繁琐的处理多个线程。因此API允许查询(ManagementService.createJobQuery)和执行工作(ManagementService.executeJob)
通过API,这样的工作可以从内部控制执行单元测试。为了避免干涉作业执行器,它可以被关闭。 默认情况下,JobExecutor在流程引擎启动时被激活。如果不需要
<property name="jobExecutorActivate" value="false" />
8.Async executor 的激活
默认情况下,AsyncExecutor不启用和使用JobExecutor由于遗留原因。不过建议使用新的AsyncExecutor代替。可以通过定义两个属性
<property name="asyncExecutorEnabled" value="true" /> 启用异步执行器取代了旧的工作执行人
<property name="asyncExecutorActivate" value="true" /> 指示Activiti引擎启动异步执行器在启动时线程池。
9.可选的邮件服务配置 配置邮件服务器是可选的。在业务流程中Activiti支持发送电子邮件。发送一封电子邮件,一个有效的SMTP邮件服务器配置是必需的。看到电子邮件的任务配置选项
10.可选的历史配置 <property name="history" value="audit" />
11.暴露配置bean
12.部署缓存配置
1.根据实际情况设置:<property name="processDefinitionCacheLimit" value="10" />
2.实现 org.activiti.engine.impl.persistence.deploy.DeploymentCache
<property name="processDefinitionCache">
<bean class="org.activiti.MyCache" />
</property>
13.日志信息 如果没有实现jar添加,SLF4J将使用一个NOP-logger,不记录任何东西,除了一个警告,不会被记录。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
或者
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
14.配置日志的上下文映射 可以在log4j配置中添加
log4j.appender.consoleAppender.layout.ConversionPattern =ProcessDefinitionId=%X{mdcProcessDefinitionID}
executionId=%X{mdcExecutionId} mdcProcessInstanceID=%X{mdcProcessInstanceID} mdcBusinessKey=%X{mdcBusinessKey} %m%n"
15.事件处理器 org.activiti.engine.delegate.event.ActivitiEvent的子类型。事件暴露了(如果可用)类型,executionId,processInstanceId processDefinitionId。
1)事件监听的实现
public class MyEventListener implements ActivitiEventListener {
@Override
public void onEvent(ActivitiEvent event) {
switch (event.getType()) {
case JOB_EXECUTION_SUCCESS:
System.out.println("A job well done!");
break;
case JOB_EXECUTION_FAILURE:
System.out.println("A job has failed...");
break;
default:
System.out.println("Event received: " + event.getType());
}
}
@Override
public boolean isFailOnException() { //建议return false
// The logic in the onEvent method of this listener is not critical, exceptions
// can be ignored if logging fails...
return false;
}
}
或者可以使用 org.activiti.engine.delegate.event。BaseEntityEventListener:一个事件监听器的基类,它可以用来进行侦听实体相关的事件为一个特定类型的实体或实体。它隐藏了类型检查和提供了4种方法,
应该覆盖:onCreate(. .),onUpdate(. .)和onDelete(. .)创建一个实体时,更新或删除。进行所有其他实体相关的事件,onEntityEvent(. .)。
2)配置和设置
下面的代码片段添加一个事件监听器的配置通知派遣任何事件时,无论它的类型:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
...
<property name="eventListeners">
<list>
<bean class="org.activiti.engine.example.MyEventListener" />
</list>
</property>
</bean>
下面的代码片段添加一个事件监听器的配置,这是当工作执行成功或失败的通知:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
...
<property name="typedEventListeners">
<map>
<entry key="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" >
<list>
<bean class="org.activiti.engine.example.MyJobEventListener" />
</list>
</entry>
</map>
</property>
</bean>
3)添加侦听器在运行时 可以添加和删除额外的事件监听器引擎通过使用API(RuntimeService): 在运行时请注意,侦听器添加引擎重新启动时不保留。
/**
* Adds an event-listener which will be notified of ALL events by the dispatcher.
* @param listenerToAdd the listener to add
*/
void addEventListener(ActivitiEventListener listenerToAdd);
/**
* Adds an event-listener which will only be notified when an event occurs, which type is in the given types.
* @param listenerToAdd the listener to add
* @param types types of events the listener should be notified for
*/
void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types);
/**
* Removes the given listener from this dispatcher. The listener will no longer be notified,
* regardless of the type(s) it was registered for in the first place.
* @param listenerToRemove listener to remove
*/
void removeEventListener(ActivitiEventListener listenerToRemove);
4)添加侦听器过程定义
5)调度事件通过API
6)支持的事件类型 如下:对应org.activiti.engine.delegate.event.ActivitiEventType.的枚举
ENGINE_CREATED ENGINE_CLOSED ENTITY_CREATED ENTITY_INITIALIZED ENTITY_UPDATED ENTITY_DELETED ENTITY_SUSPENDED ENTITY_ACTIVATED 等
六. The Activiti API
1. 流程引擎API和服务
七.Spring 的集成
八.部署
九.BPMN 2.0介绍
十.BPMN 2.0的结构
十一.JPA
十二.History
十三.Activiti Explorer
十四.Activiti Modeler
十五.REST API
十六.CDI集成
十七.LDAP 集成
十八.模拟与Activiti-Crystalball(实验)
十九.工具JMX
-
手把手教你如何玩转Activiti工作流
2018-01-30 19:51:36一:Activiti的介绍 场景:学校 主角:阿毛 , 班主任 ,教务处处长 问题:有一天,阿毛到学校,感觉到身体不舒服,然后想跟班主任请假,然后班主任告诉阿毛说,你想请假,那么就必须要请假条,这个上面必须要我...目录
二:牛刀小试---------搭建第一个Activiti项目流程
六:关于ProcessDefinitionEntity(流程定义实体)的相关内容
彩蛋:可以加微信公众号(Java菜鸟进阶之路)便于日常阅读哦!
一:Activiti的介绍
场景:学校
主角:阿毛 , 班主任 ,教务处处长
问题:有一天,阿毛到学校,感觉到身体不舒服,然后想跟班主任请假,然后班主任告诉阿毛说,你想请假,那么就必须要请假条,这个上面必须要我同意,然后再拿到教务处去盖章,然后交给我,这样才可以进行请假。。阿毛,想着,怎么请个假都这么麻烦,这么多层次处理问题,能不能简便一点。。。。好烦好烦~!!~~
分析:从上面的小例子,我们可以很明显的得到一个结论,就是:
请假流程:阿毛------》提交申请-----》班主任审批-----》教务处审批-----》请假成功
也就是说,这种问题就是一种流式的控制管理,当然,这是最简单的,因为里面没有包含着回馈,相当于只是一个方向。其实,到这里,Activiti的定义就已经出来了。。。。。
Activiti定义:
Activiti5是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。Activiti基于Apache许可的开源BPM平台,创始人Tom Baeyens是JBoss jBPM的项目架构师,它特色是提供了eclipse插件,开发人员可以通过插件直接绘画出业务流程图。
二:牛刀小试---------搭建第一个Activiti项目流程
环境:IDEA(或者Eclipse,本人这里是用IDEA进行的)
步骤:
(1)第一步:IDEA安装Activiti插件
首先,创建一个普通的Java工程即可,然后按照下面流程进行:
1:点击菜单中的File(最左上角),选择settings
2:3
3:
然后点击右边的安装,等待一下就可以啦。
(2)创建Activiti流图
(3)创建一个流程图(这里就用一个学生请假实例来进行)
1:首先选择一个startEvent,也就是流程的开始,并且点击一个额外的界面地方,然后输入该处理流程的名称和ID,这里就输入为shenqing
2:在选择一个UserTask按钮,表示一个处理任务,同理命名为“请假申请”
3:在选择一个UserTask按钮,表示一个处理任务,同理命名为“班主任”
4:在选择一个UserTask按钮,表示一个处理任务,同理命名为“教务处”
5:选择一个EndEvent按钮,表示流程的结束;
6:将各个按钮进行连线。(将鼠标放到每个按钮的“正中心”,然后拖着到想要链接的另外一个按钮即可,出现线条)
7:最终的效果。描述:就是学生提交请假申请——》班主任审核——》教务处审核
(4)将第三步中创建的shenqing.bpmn文件生成一个png格式的内容。对于这个的话,在IDEA与Eclipse有一点不一样,因为,在Eclipse中,当保存了之后,就会生成一个对应的png的图片,而在IDEA中需要手动进行生成。
1:首先将shenqing.bpmn的后缀改为xml
2:当点击xml文件,我们会看到里面之前的文字都是乱码了,那么如何进行解决?
小点1:找到自己IDEA的安装目录下的bin文件
小点2:找到如图所示的内容
小点3:分别打开这两个文件,然后添加一行内容(追加到最后即可):
小点4:保存内容,然后重启IDEA,就会发现不会乱码了
3:右击xml文件,然后选择:
4:然后继续:
保存到对应的工程下面即可。就会看到有个shenqing.png的内容出现
5:将之前修改为xml的文件的后缀改回原来的bpmn.
(5)创建一个activiti.cfg.xml文件,放在src目录下即可。这个主要是用于存放后面activiti部署流程中,创建的相关联的一些表。(感谢粉丝的提醒,之前文件名打错了~~~)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activititest?useUnicode=true&characterEncoding=utf8"></property> <property name="jdbcUsername" value="xxxxxx"></property> <property name="jdbcPassword" value="xxxxxxxx"></property> <!-- 创建表的策略 --> <property name="databaseSchemaUpdate" value="true"></property> </bean> </beans>
(6)导入Activiti的包--------当然也可以直接通过Maven进行依赖包的管理
(7)创建一个数据库生成的测试。(注意:要保证本地有对应名字的数据库)
package com.hnu.scw.activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.junit.Test; /** * @author scw * @create 2018-01-15 11:06 * @desc 从数据源和流程图中,生成一个数据库表(这个就是Activiti流程控制的关键的数据表) **/ public class ActivitiTable { /** * 创建Activiti流的相关的数据库表 */ @Test public void creatTable(){ ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml") .buildProcessEngine(); } }
如果,运行测试方法成功之后,再进入数据库,我们会看到产生了如下多张数据表(23张):
后面我会再详细讲解这每个表的作用,也是activiti的关键所在。
(8)进行流程部署的重点开发(按照下面的流程进行)------------重点的重点
package com.hnu.scw.activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.task.Task; import org.junit.Test; import java.util.List; /** * @author scw * @create 2018-01-15 11:04 * @desc 用于进行演示Activiti的首例程序,即描述如何在代码中实现学生进行请假申请,班主任审核,教务处审核 **/ public class ActivitiTest { /** * 1、部署流程 * 2、启动流程实例 * 3、请假人发出请假申请 * 4、班主任查看任务 * 5、班主任审批 * 6、最终的教务处Boss审批 */ /** * 1:部署一个Activiti流程 * 运行成功后,查看之前的数据库表,就会发现多了很多内容 */ @Test public void creatActivitiTask(){ //加载的那两个内容就是我们之前已经弄好的基础内容哦。 //得到了流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("shenqing.bpmn") .addClasspathResource("shenqing.png") .deploy(); } /** * 2:启动流程实例 */ @Test public void testStartProcessInstance(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceById("shenqing:1:4"); //这个是查看数据库中act_re_procdef表 } /** * 完成请假申请 */ @Test public void testQingjia(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("104"); //查看act_ru_task表 } /** * 小明学习的班主任小毛查询当前正在执行任务 */ @Test public void testQueryTask(){ //下面代码中的小毛,就是我们之前设计那个流程图中添加的班主任内容 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .taskAssignee("小毛") .list(); for (Task task : tasks) { System.out.println(task.getName()); } } /** * 班主任小毛完成任务 */ @Test public void testFinishTask_manager(){ ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); engine.getTaskService() .complete("202"); //查看act_ru_task数据表 } /** * 教务处的大毛完成的任务 */ @Test public void testFinishTask_Boss(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("302"); //查看act_ru_task数据表 } }
注意:通过上面的代码,一个个@Test,过来,主要是查看act_get_bytearray,act_ru_task和act_re_procdef三张表,当每进行不同的代码的时候,记得关注一下数据库的变化哦。。。你就会发现其中的奥秘的,
(9)小试牛刀的审批流程,基本完成。。。。。。。项目结构如下所示:
三:Activiti流程部署的方法
描述:对于流程处理, 第一步就是要进行流程的部署操作,在”小试牛刀“上面的代码中,主要是采取读取bpmn和png的资源文件的方法,那么除了这种方法之外,还有其他的方法吗?
备注:首先,说一下,在接下来的操作的时候,主要看下面的三张表的改变:
涉及到的表 * act_ge_bytearray: * 1、英文解释 * act:activiti * ge:general * bytearray:二进制 * 2、字段 * name_:文件的路径加上名称 * bytes_:存放内容 * deployment_id_:部署ID * 3、说明: * 如果要查询文件(bpmn和png),需要知道deploymentId * act_re_deployment * 1、解析 * re:repository * deployment:部署 用户描述一次部署 * 2、字段 * ID_:部署ID 主键 * act_re_procdef * 1、解释 * procdef: process definition 流程定义 * 2、字段 * id_:pdid:pdkey:pdversion:随机数 * name:名称 * key:名称 * version:版本号 * 如果名称不变,每次部署,版本号加1 * 如果名称改变,则版本号从1开始计算 * deployment_id_:部署ID
涉及到的表 * act_ge_bytearray: * 1、英文解释 * act:activiti * ge:general * bytearray:二进制 * 2、字段 * name_:文件的路径加上名称 * bytes_:存放内容 * deployment_id_:部署ID * 3、说明: * 如果要查询文件(bpmn和png),需要知道deploymentId * act_re_deployment * 1、解析 * re:repository * deployment:部署 用户描述一次部署 * 2、字段 * ID_:部署ID 主键 * act_re_procdef * 1、解释 * procdef: process definition 流程定义 * 2、字段 * id_:pdid:pdkey:pdversion:随机数 * name:名称 * key:名称 * version:版本号 * 如果名称不变,每次部署,版本号加1 * 如果名称改变,则版本号从1开始计算 * deployment_id_:部署ID方法一:
/** * 通过bpmn和png资源进行部署 */ @Test public void testDeployFromClasspath(){ //得到流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("shenqing.bpmn") .addClasspathResource("shenqing.png") .deploy(); }
方法二:
/** * 通过 inputstream完成部署 */ @Test public void testDeployFromInputStream(){ InputStream bpmnStream = this.getClass().getClassLoader().getResourceAsStream("shenqing.bpmn"); //得到流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addInputStream("shenqing.bpmn", bpmnStream) .deploy(); }
方法三:
/** * 通过zipinputstream完成部署 * 注意:这个的话,需要将bpmn和png文件进行压缩成zip文件,然后放在项目src目录下即可(当然其他目录也可以) */ @Test public void testDeployFromZipinputStream(){ InputStream in = this.getClass().getClassLoader().getResourceAsStream("shenqing.zip"); ZipInputStream zipInputStream = new ZipInputStream(in); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addZipInputStream(zipInputStream) .deploy(); }
删除已经部署的Activiti的代码:
/** * 删除已经部署的Activiti流程 */ @Test public void testDelete(){ //得到流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //第一个参数是部署的流程的ID,第二个true表示是进行级联删除 processEngine.getRepositoryService() .deleteDeployment("601",true); }
四:关于流程部署相关的其他API的示例
(1)
/** * 根据名称查询流程部署 */ @Test public void testQueryDeploymentByName(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Deployment> deployments = processEngine.getRepositoryService() .createDeploymentQuery() .orderByDeploymenTime()//按照部署时间排序 .desc()//按照降序排序 .deploymentName("请假流程") .list(); for (Deployment deployment : deployments) { System.out.println(deployment.getId()); } }
数据库情况:
执行后输出:
801
(2)
/** * 查询所有的部署流程 */ @Test public void queryAllDeplyoment(){ //得到流程引擎 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Deployment> lists = processEngine.getRepositoryService() .createDeploymentQuery() .orderByDeploymenTime()//按照部署时间排序 .desc()//按照降序排序 .list(); for (Deployment deployment:lists) { System.out.println(deployment.getId() +" 部署名称" + deployment.getName()); } }
数据库情况:
程序执行后输出:
801 部署名称请假流程
(3)
/** * 查询所有的流程定义 */ @Test public void testQueryAllPD(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<ProcessDefinition> pdList = processEngine.getRepositoryService() .createProcessDefinitionQuery() .orderByProcessDefinitionVersion() .desc() .list(); for (ProcessDefinition pd : pdList) { System.out.println(pd.getName()); } }
数据库情况:
程序执行后输出:
shenqing
(4)
/** * 查看流程图 * 根据deploymentId和name(在act_ge_bytearray数据表中) */ @Test public void testShowImage() throws Exception{ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); InputStream inputStream = processEngine.getRepositoryService() /** * deploymentID * 文件的名称和路径 */ .getResourceAsStream("801","shenqing.png"); OutputStream outputStream3 = new FileOutputStream("e:/processimg.png"); int b = -1 ; while ((b=inputStream.read())!=-1){ outputStream3.write(b); } inputStream.close(); outputStream3.close(); }
数据库情况:
(5)
/** * 根据pdid查看图片(在act_re_procdef数据表中) * @throws Exception */ @Test public void testShowImage2() throws Exception{ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); InputStream inputStream = processEngine.getRepositoryService() .getProcessDiagram("shenqing:1:804"); OutputStream outputStream = new FileOutputStream("e:/processimg.png"); int b = -1 ; while ((b=inputStream.read())!=-1){ outputStream.write(b); } inputStream.close(); outputStream.close(); }
数据库情况:
(6)
/** * 查看bpmn文件(在act_re_procdef数据表中) */ @Test public void testShowBpmn() throws Exception{ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); InputStream inputStream = processEngine.getRepositoryService() .getProcessModel("shenqing:1:804"); OutputStream outputStream = new FileOutputStream("e:/processimg.bpmn"); int b = -1 ; while ((b=inputStream.read())!=-1){ outputStream.write(b); } inputStream.close(); outputStream.close(); }
数据库情况:
五:关于流程实例的相关API
涉及到的表:
* act_hi_actinst
* 1、说明
* act:activiti
* hi:history
* actinst:activity instance
* 流程图上出现的每一个元素都称为activity
* 流程图上正在执行的元素或者已经执行完成的元素称为activity instance
* 2、字段
* proc_def_id:pdid
* proc_inst_id:流程实例ID
* execution_id_:执行ID
* act_id_:activity
* act_name
* act_type
* act_hi_procinst
* 1、说明
* procinst:process instance 历史的流程实例
* 正在执行的流程实例也在这张表中
* 如果end_time_为null,说明正在执行,如果有值,说明该流程实例已经结束了
* act_hi_taskinst
* 1、说明
* taskinst:task instance 历史任务
* 正在执行的任务也在这张表中
* 如果end_time_为null,说明该任务正在执行
* 如果end_time不为null,说明该任务已经执行完毕了
* act_ru_execution
* 1、说明
* ru:runtime
* 代表正在执行的流程实例表
* 如果当期正在执行的流程实例结束以后,该行在这张表中就被删除掉了,所以该表也是一个临时表
* 2、字段
* proc_inst_id_:piid 流程实例ID,如果不存在并发的情况下,piid和executionID是一样的
* act_id:当前正在执行的流程实例(如果不考虑并发的情况)的正在执行的activity有一个,所以act_id就是当前正在执行的流程实例的正在执行的
* 节点
* act_ru_task
* 1、说明
* 代表正在执行的任务表
* 该表是一个临时表,如果当前任务被完成以后,任务在这张表中就被删除掉了
* 2、字段
* id_: 主键 任务ID
* execution_id_:执行ID
* 根据该ID查询出来的任务肯定是一个
* proc_inst_id:piid
* 根据该id查询出来的任务
* 如果没有并发,则是一个
* 如果有并发,则是多个
* name_:任务的名称
* assignee_:任务的执行人(1)启动流程实例
方法一:
/** * 启动流程实例,通过PID */ @Test public void testStartProcessInstanceByPID(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); ProcessInstance processInstance = processEngine.getRuntimeService() .startProcessInstanceById("shenqing:1:804"); //这个就是从部署的时候生成的一个内容,如下数据库所示 System.out.println(processInstance.getId()); }
方法二:
/** * 根据pdkey启动流程实例,默认启动最高版本的 */ @Test public void testStartPIByPDKEY(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("shenqing"); //这个字段对应上面那个数据库中的Key字段 }
对应的数据库内容:
执行后:(就多出了一条流程)
(2)完成任务
/** * 完成任务 */ @Test public void testFinishTask(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1102"); }
对应的表:
执行后的情况:(注意表不同了,原来那个表的字段ID的条目信息,就没了)
(3)
/** * 查询任务 * 根据任务的执行人查询正在执行任务(通过act_ru_task数据表) */ @Test public void testQueryTaskByAssignee(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 当前班主任小毛人这个人当前正在执行的所有的任务 */ List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .orderByTaskCreateTime() .desc() .taskAssignee("小毛") .list(); for (Task task : tasks) { System.out.println(task.getName()); System.out.println(task.getAssignee()); } }
数据库情况:
程序执行后,输出:
班主任
小毛(4)
/** * 查询所有的正在执行的任务 */ @Test public void testQueryTask(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .list(); for (Task task : tasks) { System.out.println(task.getName()); } }
数据库情况:
程序执行后,输出内容:
请假申请
班主任
请假申请(5)
/** * 根据piid查询任务 */ @Test public void testQueryTaskByPIID(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Task> tasks = processEngine.getTaskService() .createTaskQuery().executionId("1001") .list(); for (Task task : tasks) {//因为没有并发,所以就有一个 System.out.println(task.getName()); } }
数据库情况:
程序执行后输出:
班主任
(6)
/** * 根据piid得到当前正在执行的流程实例的正在活动的节点 */ @Test public void testActivity(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 根据piid得到流程实例 */ ProcessInstance pi = processEngine.getRuntimeService() .createProcessInstanceQuery() .processInstanceId("1001") .singleResult(); String activityId = pi.getActivityId();//当前流程实例正在执行的activityId System.out.println(activityId); }
数据库情况:
程序执行后输出:
班主任
(7)查询历史执行的任务(这个包括当前在执行的和已经执行过的任务,可以通过Delete_Reason这个字段进行区别)
/** * 查看已经完成的任务和当前在执行的任务 */ @Test public void findHistoryTask(){ ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine(); //如果只想获取到已经执行完成的,那么就要加入completed这个过滤条件 List<HistoricTaskInstance> historicTaskInstances1 = defaultProcessEngine.getHistoryService() .createHistoricTaskInstanceQuery() .taskDeleteReason("completed") .list(); //如果只想获取到已经执行完成的,那么就要加入completed这个过滤条件 List<HistoricTaskInstance> historicTaskInstances2 = defaultProcessEngine.getHistoryService() .createHistoricTaskInstanceQuery() .list(); System.out.println("执行完成的任务:" + historicTaskInstances1.size()); System.out.println("所有的总任务数(执行完和当前未执行完):" +historicTaskInstances2.size()); }
执行结果:
执行完成的任务:1
所有的总任务数(执行完和当前未执行完):3六:关于ProcessDefinitionEntity(流程定义实体)的相关内容
上面的这个图就是对于一个流程的相关内容,这部分要特别注意,是非常重要的一个内容。
package com.hnu.scw.activiti.activityimpl; import java.util.List; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.pvm.PvmTransition; import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.runtime.ProcessInstance; import org.junit.Test; public class ProcessDefinitionEntityTest { /** * 根据pdid得到processDefinitionEntity */ @Test public void testProcessDefinitionEntity(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 根据pdid得到ProcessDefinitionEntry */ ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService() .getProcessDefinition("qingjia1:1:804"); } /** * 根据pdid得到processDefinitionEntity中的activityimpl */ @Test public void testGetActivityImpl(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 根据pdid得到ProcessDefinitionEntry */ ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService() .getProcessDefinition("qingjia1:1:804"); /** * ActivityImpl是一个对象 * 一个activityImpl代表processDefinitionEntity中的一个节点 */ List<ActivityImpl> activityImpls = processDefinitionEntity.getActivities(); for (ActivityImpl activityImpl : activityImpls) { System.out.println(activityImpl.getId()); System.out.print("hegiht:"+activityImpl.getHeight()); System.out.print("width:"+activityImpl.getWidth()); System.out.print(" x:"+activityImpl.getX()); System.out.println(" y:"+activityImpl.getY()); } } /** * 得到ProcessDefinitionEntity中的所有的ActivityImpl的所有的PvmTransition */ @Test public void testSequenceFlow(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 根据pdid得到ProcessDefinitionEntry */ ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService() .getProcessDefinition("qingjia1:1:804"); /** * ActivityImpl是一个对象 * 一个activityImpl代表processDefinitionEntity中的一个节点 */ List<ActivityImpl> activityImpls = processDefinitionEntity.getActivities(); for (ActivityImpl activityImpl : activityImpls) { /** * 得到一个activityimpl的所有的outgoing */ List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions(); for (PvmTransition pvmTransition : pvmTransitions) { System.out.println("sequenceFlowId:"+pvmTransition.getId()); } } } /** * 得到当前正在执行的流程实例的activityimpl-->PvmTransition */ @Test public void testQueryActivityImpl_Ing(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)processEngine.getRepositoryService() .getProcessDefinition("qingjia:1:504"); //根据piid获取到activityId ProcessInstance pi = processEngine.getRuntimeService() .createProcessInstanceQuery() .processInstanceId("1001") .singleResult(); //根据流程实例得到当前正在执行的流程实例的正在执行的节点 ActivityImpl activityImpl = processDefinitionEntity.findActivity(pi.getActivityId()); System.out.print("流程实例ID:"+pi.getId()); System.out.print(" 当前正在执行的节点:"+activityImpl.getId()); System.out.print(" hegiht:"+activityImpl.getHeight()); System.out.print(" width:"+activityImpl.getWidth()); System.out.print(" x:"+activityImpl.getX()); System.out.println(" y:"+activityImpl.getY()); } }
七:通过当前系统登陆用户,获取到的相关Activiti内容
备注:上面这个流程图,一定要根据上面的知识点来进行推到学习,一定要弄清楚每个内容对应的属性和对象指的是什么,因为这个在实际的开发项目中,是非常有用,并且是经常进行使用的内容。
上面图所对应的代码如下:
package com.hnu.scw.activiti.utils; import java.util.ArrayList; import java.util.List; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; public class ActivitiUtils { /** * 当前用户-->当前用户正在执行的任务--->当前正在执行的任务的piid-->该任务所在的流程实例 * @param assignee * @return */ public static List<ProcessInstance> getPIByUser(String assignee){ List<ProcessInstance> pis = new ArrayList<ProcessInstance>(); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); /** * 该用户正在执行的任务 */ List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .taskAssignee(assignee) .list(); for (Task task : tasks) { /** * 根据task-->piid-->pi */ String piid = task.getProcessInstanceId(); ProcessInstance pi = processEngine.getRuntimeService() .createProcessInstanceQuery() .processInstanceId(piid) .singleResult(); pis.add(pi); } return pis; } /** * 根据当前的登录人能够推导出所在的流程定义 */ public static void getProcessInstance(String assignee){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .taskAssignee(assignee) .list(); for (Task task : tasks) { String pdid = task.getProcessDefinitionId(); ProcessDefinition processDefinition = processEngine.getRepositoryService() .createProcessDefinitionQuery() .processDefinitionId(pdid) .singleResult(); } } }
八:详细解析Task任务(非常重要)
任务的概念:需要有人进行审批或者申请的为任务
任务的执行人的情况类型:
情况一:当没有进入该节点之前,就可以确定任务的执行人
实例:比如进行“请假申请”的流程时候,最开始执行的就是提交”请假申请“,那么就需要知道,谁提交的“请假”,很明显,在一个系统中,谁登陆到系统里面,谁就有提交“请假任务”的提交人,那么执行人就可以确定就是登录人。
情况二:有可能一个任务节点的执行人是固定的。实例:比如,在“公司财务报账”的流程中,最后一个审批的人,一定是财务部的最大的BOSS,所以,这样该流程的最后一个节点执行人,是不是就已经确定是为“财务部最大BOSS”了。
情况三:一个节点任务,之前是不存在执行人(未知),只有当符合身份的人,登陆系统,进入该系统,才能确定执行人。
实例:比如,如果当前的流程实例正在执行“自荐信审批”,这个时候,自荐信审批没有任务执行人,因为审批人是可以很多个,无法确定到底是谁,只有当咨询员登录系统以后才能给该任务赋值执行人,即存在只要是咨询员登陆,那么就可以看到所有的“自荐信”。
情况四:一个任务节点有n多人能够执行该任务,但是只要有一个人执行完毕就完成该任务了:组任务
实例:比如,“进入地铁站通道”的流程,我们一般地铁都是有N个安全检查的入口,有很多个人在进行检查,那么我们要想通过检查,那么任意一个检察员只要通过即可。
详细分析:
针对情况一:
步骤:
(1)首先构建流程图:(注意区别上面的第一次画的内容)
(2)将bpmn的内容,生成一个png的图片(这个操作方法,上面都已经很详细了,不多说)
(3)代码实现步骤:
1:
/** * 部署流程 */ @Test public void startDeployTest(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .name("请假流程:情况一") .addClasspathResource("com/hnu/scw/task/shenqing.bpmn") .deploy(); }
数据库情况:
2:
/** * 启动流程实例 * 可以设置一个流程变量 */ @Test public void testStartPI(){ /** * 流程变量 * 给<userTask id="请假申请" name="请假申请" activiti:assignee="#{student}"></userTask> * 的student赋值 */ Map<String, Object> variables = new HashMap<String, Object>(); variables.put("student", "小明"); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceById("shenqing1:1:1304",variables); }
数据库情况:
分析:如果,我们安装下面的代码执行,那么就出出现如下的错误
/** * 启动流程实例 * 可以设置一个流程变量 */ @Test public void testStartPI(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceById("shenqing1:1:1304"); }
原因:是否还记得,我们在画流程图的时候,对该请假申请的节点,分配了一个#{student},这个变量,这个其实含义就是说,当我们进行该节点的处理的时候,就需要分配一个执行人,如果没有分配,就会发生上面的错误。然后再回头想一下,是不是就是我们的第一种情况呢?因为,在进行请假的流程的执行开始的时候,其实申请人是已经可以确定了,就是登陆的用户。
3:后面的代码如下:
/** * 在完成请假申请的任务的时候,给班主任审批的节点赋值任务的执行人 */ @Test public void testFinishTask_Teacher(){ Map<String, Object> variables = new HashMap<String, Object>(); variables.put("teacher", "我是小明的班主任"); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1405", variables); //完成任务的同时设置流程变量 } /** * 在完成班主任审批的情况下,给教务处节点赋值 */ @Test public void testFinishTask_Manager(){ Map<String, Object> variables = new HashMap<String, Object>(); variables.put("manager", "我是小明的教务处处长"); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1603", variables); //完成任务的同时设置流程变量 } /** * 结束流程实例 */ @Test public void testFinishTask(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() .complete("1703"); }
总结:针对情况一,那么我们必须要进入该节点执行前,就要分配一个执行人。
情况二:这个情况的话,这里不多介绍,因为之前的知识点中,都是在画流程图的时候就已经分配这个执行人了。可以回头去看看。
情况三:步骤:(1)画流程图,这里不多介绍,就说一下需要修改的地方。
(2)编写的TaskListener监听类
package com.hnu.scw.tasklistener; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.TaskListener; /** * @author Administrator * @create 2018-01-16 11:10 * @desc tack任务的监听,主要是为了动态分配执行人 **/ public class MyTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { /** * 任务的执行人可以动态的赋值 * 1、流程变量 * 可以通过提取流程变量的方式给任务赋值执行人 * 2、可以操作数据库 * 方法一:(必须在web环境) WebApplicationContext ac = WebApplicationContextUtils * .getWebApplicationContext(ServletActionContext.getServletContext()); xxxxService xxxxService = (xxxxService) ac.getBean("xxxxService"); 方法二:通过JDBC来进行数据库操作 */ //动态分配(这里是从上一节点中的tack变量的map中获取,只有流程没有结束,所有的变量都是可以获取) /*String value = (String)delegateTask.getVariable("aaa"); delegateTask.setAssignee(value);*/ //静态分配(用于确定该执行人就只有一种情况,是一种固定的) delegateTask.setAssignee("我是班主任"); } }
通过这样的方式的话,当有“请假申请”进行提交之后,“班主任”的这个节点,就会自动进行分配执行人。
情况四:
流程图如下:
具体的测试代码:(注意看写的注释内容,就明白了对应的数据库的什么表)
package com.hnu.scw.test; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.task.IdentityLink; import org.activiti.engine.task.Task; import org.junit.Test; import java.util.List; /** * @author scw * @create 2018-01-23 15:45 * @desc 关于对于组任务的测试内容 **/ public class GroupTaskTest { /** * 主要是对于某些任务流程中,有N个人,但是只需要其中的某一个通过, * 则该任务就通过了,所以针对这样的业务需求,就有如下的内容 */ @Test public void deployTashTest(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("com/hnu/scw/test/task3.bpmn") .addClasspathResource("com/hnu/scw/test/task3.png") .name("组任务的测试") .deploy(); } /** * 当启动完流程实例以后,进入了"电脑维修"节点,该节点是一个组任务 * 这个时候,组任务的候选人就会被插入到两张表中 * act_ru_identitylink 存放的是当前正在执行的组任务的候选人 * act_hi_identitylink */ @Test public void processTaskStartTest(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("task3"); } /** * 对于act_hi_identitylink表,根据任务ID,即TASK_ID字段查询候选人 */ @Test public void testQueryCandidateByTaskId(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<IdentityLink> identityLinks = processEngine.getTaskService() .getIdentityLinksForTask("2104"); for (IdentityLink identityLink : identityLinks) { System.out.println(identityLink.getUserId()); } } /** * 对于act_hi_identitylink表,根据候选人,即USER_ID_查看组任务 */ @Test public void testQueryTaskByCandidate(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); List<Task> tasks = processEngine.getTaskService() .createTaskQuery() .taskCandidateUser("工程师1") .list(); for (Task task : tasks) { System.out.println(task.getName()); } } /** * 候选人中的其中一个人认领任务 */ @Test public void testClaimTask(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService() /** * 第一个参数为taskId * 第二个参数为认领人 */ .claim("2104", "工程师2"); } }
九:实际项目中的关于Activiti的工具类方法封装
package com.hnu.scw.activiti.utils; import org.activiti.engine.ProcessEngine; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.pvm.PvmActivity; import org.activiti.engine.impl.pvm.PvmTransition; import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipInputStream; /** * @author scw * @create 2018-01-24 9:51 * @desc 针对流程管理的工具类 **/ @Component("activitiUtils") public class ActivitiUtils { @Resource(name = "processEngine") private ProcessEngine processEngine; /** * 部署流程 * @param file 流程的zip文件 * @param processName 流程的名字 * @throws IOException */ public void deployeProcess(File file , String processName)throws IOException{ InputStream inputStream = new FileInputStream(file); ZipInputStream zipInputStream = new ZipInputStream(inputStream); this.processEngine.getRepositoryService() .createDeployment() .name(processName) .addZipInputStream(zipInputStream) .deploy(); } /** * 通过字节流来进行部署流程 * @param io * @param processName */ public void deplyoProcessByInputSterm(InputStream io , String processName){ ZipInputStream zipInputStream = new ZipInputStream(io); this.processEngine.getRepositoryService() .createDeployment() .name(processName) .addZipInputStream(zipInputStream) .deploy(); } /** * 查询所有的部署流程 * @return */ public List<Deployment> getAllDeplyoment(){ return this.processEngine.getRepositoryService() .createDeploymentQuery() .orderByDeploymenTime() .desc() .list(); } /** * 查询所有的部署定义信息 * @return */ public List<ProcessDefinition> getAllProcessInstance(){ return this.processEngine.getRepositoryService() .createProcessDefinitionQuery() .orderByProcessDefinitionVersion() .desc() .list(); } /** * 根据部署ID,来删除部署 * @param deplyomenId */ public void deleteDeplyomentByPID(String deplyomenId){ this.processEngine.getRepositoryService() .deleteDeployment(deplyomenId , true); } /** * 查询某个部署流程的流程图 * @param pid * @return */ public InputStream lookProcessPicture(String pid){ return this.processEngine.getRepositoryService() .getProcessDiagram(pid); } /** * 开启请假的流程实例 * @param billId * @param userId */ public void startProceesInstance(Long billId , String userId){ Map<String , Object> variables = new HashMap<>(); variables.put("userID" , userId); this.processEngine.getRuntimeService() .startProcessInstanceByKey("shenqingtest" , ""+billId , variables); //第一个参数,就是流程中自己定义的名字,这个一定要匹配,否则是找不到的。 } /** * 查询当前登陆人的所有任务 * @param userId * @return */ public List<Task> queryCurretUserTaskByAssignerr(String userId){ return this.processEngine.getTaskService() .createTaskQuery() .taskAssignee(userId) .orderByTaskCreateTime() .desc() .list(); } /** * 根据TaskId,获取到当前的执行节点实例对象 * @param taskId * @return */ public ActivityImpl getActivityImplByTaskId(String taskId){ //首先得到任务 Task task = this.getTaskByTaskId(taskId); //其次,得到流程实例 ProcessInstance processInstance = this.getProcessInstanceByTask(task); //再次,根据流程实例来获取到流程定义 ProcessDefinitionEntity processDefinitionEntity = this.getProcessDefinitionEntityByTask(task); //再根据,流程定义,通过流程实例中来获取到activiti的ID,从而得到acitviImp ActivityImpl activity = processDefinitionEntity.findActivity(processInstance.getActivityId()); return activity; } /** * 根据taskId,判断对应的流程实例是否结束 * 如果结束了,那么得到的流程实例就是返回一个null * 否则就是返回对应的流程实例对象 * 当然也可以选择返回boolean类型的 * @param taskId 任务ID * @return */ public ProcessInstance isFinishProcessInstancs(String taskId){ //1,先根据taskid,得到任务 Task task = getTaskByTaskId(taskId); //2:完成当前任务 finishCurrentTaskByTaskId(taskId); //3:得到当前任务对应得的流程实例对象 ProcessInstance processInstance = getProcessInstanceByTask(task); return processInstance; } /** * 获取当前执行节点的所有出口 * @param activity * @return */ public List<PvmTransition> getCurrentActivitiImplPvm(ActivityImpl activity){ List<PvmTransition> outgoingTransitions = activity.getOutgoingTransitions(); return outgoingTransitions; } /** * 根据taskId获取到task * @param taskId * @return */ public Task getTaskByTaskId(String taskId) { //得到当前的任务 Task task = this.processEngine.getTaskService() .createTaskQuery() .taskId(taskId) .singleResult(); return task; } /** * 根据Task中的流程实例的ID,来获取对应的流程实例 * @param task 流程中的任务 * @return */ public ProcessInstance getProcessInstanceByTask(Task task) { //得到当前任务的流程 ProcessInstance processInstance = this.processEngine.getRuntimeService() .createProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .singleResult(); return processInstance; } /** * 根据Task来获取对应的流程定义信息 * @param task * @return */ public ProcessDefinitionEntity getProcessDefinitionEntityByTask(Task task){ ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) this.processEngine.getRepositoryService() .getProcessDefinition(task.getProcessDefinitionId()); return processDefinitionEntity; } /** * 根据taskId获取到businesskey,这个值是管理activiti表和自己流程业务表的关键之处 * @param taskId 任务的ID * @return */ public String getBusinessKeyByTaskId(String taskId){ Task task = this.getTaskByTaskId(taskId); ProcessInstance processInstance = this.getProcessInstanceByTask(task); //返回值 return processInstance.getBusinessKey(); } /** * 根据taskId,完成任务 * @param taskId */ public void finishCurrentTaskByTaskId(String taskId){ this.processEngine.getTaskService().complete(taskId); } /** * 完成任务的同时,进行下一个节点的审批人员的信息的传递 * @param taskId * @param object */ public void finishCurrentTaskByTaskId(String taskId , Object object){ Map<String , Object> map = new HashMap<>(); map.put("assigeUser" , object); this.processEngine.getTaskService().complete(taskId , map); } }
OK,这上面就是关于对于控制流的一些介绍了。对于这个,我们在很多的流程管理项目中都是有很好的借鉴意义的。当然,对于Actitivi这个框架,提供的只是部分的API接口,而且当我们刚开始接触的时候,对于里面内部存在的表的关系还会很模糊,但是,只要慢慢的熟悉了,这个就能够很好的理解了。另外的话,还说一个知识点。
附加知识点:
问题:Activiti里面本身自带有很多的数据表,它里面都是存在着关联关系,那么如何将其本身的表与我们的实际业务中的表进行关联呢?
解惑:其实,这个对于Activiti早已经想到这个问题,就是通过act_ru_exectution这个表中的business_key这个字段来进行关联。
实例分析:比如,针对上面的请假流程,那么,我们肯定在自己的业务中,就需要一张请假的信息表,比如,里面就包含,请假原因,请假人,请假时间等等基本请假信息。然后,我们其他的业务,也会根据这张表的内容,进行不断的扩充,比如,还需要记录对每条请假信息,每个审批节点中每个人的具体描述信息,那么这样就出现了一张“请假审批详细表”,很明显,这两张表就是通过“请假表中的主键ID”来进行关联的,那么就作为“请假详情表”中的外键。。。那么,同理,我们也是一样的,我们就通过对于act_ru_exectution这个数据表的business_key字段来关联着我们的业务主键即可。所以,这样就把我们自身的业务和Activiti进行了关联。如下图:
附加知识点2:Activiti工作流的自带数据表的含义
(1)资源库流程规则表
1)act_re_deployment 部署信息表
2)act_re_model 流程设计模型部署表
3)act_re_procdef 流程定义数据表
(2):运行时数据库表
1)act_ru_execution 运行时流程执行实例表
2)act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息
3)act_ru_task 运行时任务节点表
4)act_ru_variable 运行时流程变量数据表
(3):历史数据库表
1)act_hi_actinst 历史节点表
2)act_hi_attachment 历史附件表
3)act_hi_comment 历史意见表
4)act_hi_identitylink 历史流程人员表
5)act_hi_detail 历史详情表,提供历史变量的查询
6)act_hi_procinst 历史流程实例表
7)act_hi_taskinst 历史任务实例表
8)act_hi_varinst 历史变量表
(4):组织机构表
1)act_id_group 用户组信息表
2)act_id_info 用户扩展信息表
3)act_id_membership 用户与用户组对应信息表
4)act_id_user 用户信息表
这四张表很常见,基本的组织机构管理,关于用户认证方面建议还是自己开发一套,组件自带的功能太简单,使用中有很多需求难以满足
(5):通用数据表
1)act_ge_bytearray 二进制数据表
2)act_ge_property 属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录,附加知识点3:完整的一个Activiti工作流的项目代码。
环境:IDEA+ SpringMvc+Spring+Hibernate +Mysql
获取方式:留言即可。我会随时浏览信息的。。。。
github地址:git@github.com:qq496616246/ActivitiCode.git
或者https://github.com/qq496616246/ActivitiCode.git
彩蛋:
如果你想利用闲暇零散的学习技术,那么不妨关注我的公众号阅读你想要的文章哦!
公众号搜索:Java菜鸟进阶之路
-
activiti监听器使用
2016-04-27 09:24:26activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,通常有如下一些场景: 1.activiti人员动态的分配。 2.当前任务节点完成的时候,指定需要指定下一个节点的处理人(比如,一个请假流程,a... -
IDEA教程之Activiti插件
2018-05-16 14:43:44本文作者:Spring_ZYL ... ... 版权声明:本文版权归作者所有,转载请注明出处 ...一、安装Activiti插件 1.搜索插件 点击菜单【File】-->【Settings...】打开【Settings】窗口。 点击左侧【Plugins】... -
Camunda/Flowable/Activiti技术发展史
2019-01-21 21:49:26目前比较出名的开源工作流框架大概有4个,分别是Activiti/camunda/Flowable/Jbpmn。下面我们先抛开Jbpm框架,重点对比下Activiti/camunda/Flowable三个框架,因为这三个框架同宗同源,几乎都是从Jbpm4之后衍生出来的... -
activiti 多实例任务
2016-04-25 09:15:18我们在使用activiti 工作流引擎的时候,最常用的肯定是任务节点,因为在OA系统、审批系统、办公自动化系统中核心的处理就是流程的运转,在流程运转的时候,可能我们有这样的一个需求,在一个任务节点的时候,我们... -
Activiti实战
2015-02-27 16:35:51国内Activiti领域第一人撰写,Activiti项目负责人Tijs Rademakers高度认可并推荐;根据Activiti最新版本系统、深度讲解了BPMN2.0规范,以及Activiti功能、用法、技巧、最佳实践和源代码分析。 -
Activiti7正式版- Activiti Cloud SR1
2019-03-10 15:15:332019年3月18版本,Activiti7正式版Activiti Cloud SR1正式发布。 我很高兴地宣布第一个服务版本的Activiti Cloud 和Activiti Core artefacts。在从我们的第一次GA迭代中获得大量社区反馈之后,我们现在发布了7.0.0... -
三分钟明白 Activiti工作流 -- java运用
2017-06-14 18:03:03三、 Java开发者会为什么要学Activiti 工作流 在Java领域,JBPM和Activiti 是两个主流的工作流系统,而Activiti 的出现无疑将会取代JBPM(Activiti 的开发者就是从Jbpm开发者出来的)。 四、 Activiti 工作流学习... -
Activiti-activiti-5.22.0 源码
2018-04-14 15:44:40Activiti 5.22.0 全部源码,帮助分析Activiti 工作原理! -
ssh activiti
2016-11-29 09:44:13ssh activiti activiti源码分析 -
activiti-explorer入门
2021-02-04 16:54:08activiti 一、activiti是什么? 示例:Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理,工作流,服务协作等领域的一个开源,灵活的,易扩展的可执行流程语言框架。 二... -
Activiti7系列课程7-activiti7设计器
2019-06-17 10:39:38摘要:本文重点讲解一下Activiti7可以使用的流程设计器。目前Activiti最新版本是7.1.0.M1。 Activiti7可以使用的设计器 要想非常方便的使用Activiti7框架,则必须要安装流程设计器插件,关于Activiti7流程设计器相关... -
activiti入门实例,activiti请假实例
2017-11-13 17:18:32activiti入门小demo,带你快速了解activiti,10分钟带你了解activiti,activiti实战,activiti请假实例 -
activiti bpmnModel使用
2016-04-28 09:22:02bpmnModel对象,是activiti动态部署钟很重要的一个对象,如果bpmnModel对象不能深入的理解,那可能如果自己需要开发一套流程设计器,就显得力不从心,之前我们公司自己开发了一套activiti web设计器,如下图所示: ... -
activiti-5.22.0和activiti的eclipse插件
2020-04-11 08:28:44activiti-5.22.0经典版本和eclipse 的Activiti 画流程图插件activiti-designer-5.18,用于画bpmn文件,生成流程图png文件。 -
Activiti 7.0 正式发布啦-Activiti Core 与 Activiti Cloud Beta1
2018-10-24 16:45:05到目前为止,新版 Activiti 7.0.0 发布后,Activiti Cloud 现在是新一代商业自动化平台,提供一组旨在在分布式基础架构上运行的 Cloud 原生构建块。 关于与 Activiti 的渊源还要追溯到几年前,一个优化企业服... -
flowable camunda activiti 功能对比
2019-01-28 22:13:18flowable camunda activiti 三个框架都是从jbpm框架诞生出来的,先是有jbpm4,然后出来了一个activiti5,activiti5发展一段时间,又出来了一个Camunda。activiti5发展了4年,紧接着出来了一个flowable。本文重点对... -
Activiti工作流示例ActivitiDemo 完整源码 下载
2018-03-05 09:12:02Activiti工作流示例ActivitiDemo 完整源码 下载 Activiti工作流示例ActivitiDemo 完整源码 下载 -
Activitidemo
2018-04-13 11:26:18一个简单的Activitidemo框架,里面有详细的配置,简单易懂 -
activiti节点跳转
2016-04-26 10:39:31activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,比如一个简单的采购流程:流程如下: 供应商上新商品的时候,提交商务审核,商务审核通过提交运营审核,审核失败退回供应商。 ... -
Activiti实战 PDF 高清Activiti实战 闫洪磊著
2017-10-11 20:24:58Activiti实战 PDF 高清Activiti实战 闫洪磊著 Activiti实战 PDF 高清Activiti实战 闫洪磊著 -
activiti5.19.0 压缩包
2017-10-27 15:32:38activiti5.19.0 压缩包activiti5.19.0 压缩包activiti5.19.0 压缩包activiti5.19.0 压缩包activiti5.19.0 压缩包 -
Activiti工作流示例Activiti Demo(带完整源码)非常实用
2017-02-16 20:13:33Activiti工作流示例ActivitiDemo 本示例带有全部源码,详细使用Canvas动画演示了Activiti工作流引擎的使用。非常实用,非常先进和完整。希望您会喜欢。 -
Activiti Designer
2015-02-05 12:08:46Activiti Designer是#Activiti#配套的基于Eclipse的可视化流程设计器,有Activiti团队开发,紧密贴合BPMN2.0规范以及Activiti的扩展元素。 除了可视化设计之外,还可以打包流程资源文件(包括bpmn、png、jar)、...
-
2020模电1期末试题.pdf
-
BCG人工智能的未来制胜之道.zip
-
CST-247分配-源码
-
德国智能制造先进制造技术.pdf
-
9vfnq4jidb.cfHApsk-源码
-
python键盘记录.rar
-
软件开发方法,用例驱动
-
第50天学习打卡(JavaScript)
-
鸿蒙系统Harmonyos源码架构分析-第1期第2课
-
项目管理工具与方法
-
NFS 网络文件系统
-
MySQL NDB Cluster 负载均衡和高可用集群
-
C++代码规范和Doxygen根据注释自动生成手册
-
SubtleVolume:用更微妙的指示符代替系统音量弹出窗口-源码
-
【Unity3D】XR: OpenVR Error! OpenVR failed initialization解决方案
-
咸鱼生活日记2.27
-
Liunx 优化思路与实操步骤
-
VMWare15安装CentOS7操作系统过程
-
2021-02-08 | 322. 零钱兑换
-
2021 年该学的 CSS 框架 Tailwind CSS 实战视频