工作流(Workflow),指业务过程的部分或整体在计算机应用环境下的自动化。是对工作流程及其各操作步骤之间业务规则的抽象。
大白话就是比如说我要请假,整个流程谁提起,第一步谁审批,第二步又是谁审批,这样的一个过程。
在没有专门的工作流引擎之前,程序员为了实现流程控制,通常的做法就是采用状态字段的值来跟踪流程的变化情况。这样不同角色的用户,通过状态字段的取值来决定记录是否显示。
针对有权限可以查看的记录,当前用户根据自己的角色来决定审批是否合格的操作。如果合格将状态字段设置一个值,来代表合格;当然如果不合格也需要设置一个值来代表不合格的情况。
但这样实现的话,缺点其实有很多:
所以这个时候就迫切的需要一个专门用来管理工作流的框架,它可以做到即使我们的流程发生变化,程序也不用改变。所以Activiti应运而生。
Activiti是一个工作流引擎, Activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行.
Activiti实现了系统的流程由其进行管理**,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。**
BPMN2.0BPMN(Business Process Model AndNotation): 业务流程模型和符号
由
BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

规定由Start Event,End Event结束,期间由很多其它事件及网关,彼此关系由连线维系。

这些图标有着分类:
Event(事件):BPMN规定,流程起点为StartEvent,由EndEvent结束Task(任务):任务之间通过连线形成整个的工作流Geteway(构建复杂流程):网关如下为一个完整的流程:

简单的流程用activiti提供的按钮及连线便可确定,但是复杂的流程如:请假未超三天组长批,超过总监批这种流程则需要网关来处理
排他网关(解析条件,IF else逻辑)

并行网关(互不影响,并行网关结束时需要聚合)

包含网关(使用最多,和并行网关一样,有分散聚合的功能)
包含网关可以看做是排他网关和并行网关的结合体。
和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。

Activiti
Activiti是一个工作流引擎(其实就是一堆jar包API),将工作流环境与业务系统的环境集成。
使用
Activiti流程建模工具(activity-designer)画业务流程(.bpmn文件,实质就是一堆xml文件) 。bpmn文件就是业务流程定义文件,通过xml定义业务流程。
使用
Activiti提供的API把流程定义内容存储,在Activiti执行过程汇总可查询定义内容。Activiti通过数据库来存储业务流程
流程实例也叫:
ProcessInstance启动一个流程实例表示开始一次业务流程的运行。多个流程互不影响。
Task)因为现在系统的业务流程已经交给
Activiti管理,通过Activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些Activiti帮我们管理了,而不需要开发人员自己编写在sql语句查询。
用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,就可由
Activiti将工作流流程往后推动
当任务办理完成没有下一个任务结点了,这个流程实例就完成了。

activiti.cfg.xml:activiti的引擎配置文件,包括:ProcessEngineConfiguration的定义、数据源定义、事务管理器等,此文件其实就是一个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>
流程引擎的配置类(ProcessEngineConfiguration),通过ProcessEngineConfiguration可以创建工作流引擎ProceccEngine
ProcessEngineConfiguration类读取我们创建的activiti.cfg.xml文件,,源码如下:
工作流引擎(ProcessEngine),相当于一个门面接口,通过ProcessEngineConfiguration创建processEngine,通过ProcessEngine创建各个service接口。
ProcessEngine)创建
ProcessEngine时会自动创建表
有两种方式:
默认创建
//使用classpath下的activiti.cfg.xml中的配置创建processEngine
ProcessEngine processEngine01 = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine01);
一般创建方式
直接使用
activiti提供的工具类ProcessEngines,会默认读取classpath下的activiti.cfg.xml文件,读取其中的数据库配置,创建ProcessEngine
@Test
public void test(){
// 引擎配置
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 获取流程引擎对象,会创建数据库
ProcessEngine processEngine=pec.buildProcessEngine();
}
数据库由代码动态生成25张表:
act_re开头代表Repository,存储流程图的相关信息和数据。act_hi开头,history 历史记录,存储各种历史记录act_ru开头,存储runtime 运行时的数据(临时数据),流程跑完自动删除
Servcie服务接口ProcessEngine 源码中已有各个service:

所以直接用get方法创建各个service:


RepositoryService:是activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作RuntimeService:Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息TaskService:Activiti的任务管理类。可以从这个类中获取任务的信息。HistoryService:Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。此服务主要通过查询功能来获得这些数据。ManagementService:Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti 系统的日常维护。
在idea中画好对应的流程图,流程id为act_re_procdef表的key,用来标识流程的唯一性,生成.bpmn,是指就是xml文件

RepositoryService进行流程部署:分为两种方式:
通过调用activiti的API将流程定义的bpmn和png两个文件一个一个添加部署到activiti中
public class ActivitiDemo {
/**
* 部署流程定义
*/
@Test
public void testDeployment(){
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/evection.bpmn") // 添加bpmn资源
.addClasspathResource("bpmn/evection.png") // 添加png资源
.name("出差申请流程")
.deploy();
// 4、输出部署信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
}
压缩包部署方式
@Test
public void deployProcessByZip() {
// 定义zip输入流
InputStream inputStream = this
.getClass()
.getClassLoader()
.getResourceAsStream(
"bpmn/evection.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
执行此操作后
activiti会将上边代码中指定的bpm文件和图片文件保存在activiti数据库。
流程定义部署后操作activiti的3张表如下:
act_re_deployment 流程定义部署表,每部署一次增加一条记录act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录act_ge_bytearray 流程资源表runtimeService 启动流程实例可获取流程基本信息
/**
* 启动流程实例
*/
@Test
public void testStartProcess(){
// 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 任务信息TaskService 进行任务操作可获取任务基本信息
流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。
/**
* 查询当前个人待执行的任务
*/
@Test
public void testFindPersonalTaskList() {
// 任务负责人
String assignee = "zhangsan";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("myEvection") //流程Key
.taskAssignee(assignee)//只查询该任务负责人的任务
.list();
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
处理任务
任务负责人查询待办任务,选择任务进行处理,完成任务。
// 完成任务
@Test
public void completTask(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务的负责人 查询任务
// 返回一个任务对象
Task task = taskService.createTaskQuery()
.processDefinitionKey("myEvection") //流程Key
.taskAssignee("zhangsan") //要查询的负责人
.singleResult();
// 完成任务,参数:任务id
taskService.complete(task.getId());
}
RepositoryService流程定义信息查询查询流程相关信息,包含流程定义,流程部署,流程定义版本
/**
* 查询流程定义
*/
@Test
public void queryProcessDefinition(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 得到ProcessDefinitionQuery 对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// 查询出当前所有的流程定义
// 条件:processDefinitionKey =evection
// orderByProcessDefinitionVersion 按照版本排序
// desc倒叙
// list 返回集合
List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("myEvection")
.orderByProcessDefinitionVersion()
.desc()
.list();
// 输出流程定义信息
for (ProcessDefinition processDefinition : definitionList) {
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());
}
}
RepositoryService流程定义信息查询public void deleteDeployment() {
// 流程部署id
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
//删除流程定义,如果该流程定义已有流程实例启动则删除时出错
repositoryService.deleteDeployment(deploymentId);
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
//repositoryService.deleteDeployment(deploymentId, true);
}
HistoryService 即使流程定义已经删除了,流程执行的历史信息通过前面的分析,依然保存在activiti的
act_hi_*相关的表中。所以我们还是可以查询流程执行的历史信息,可以通过HistoryService来查看相关的历史记录。
/**
* 查看历史信息
*/
@Test
public void findHistoryInfo(){
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取HistoryService
HistoryService historyService = processEngine.getHistoryService();
// 获取 actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
// 查询 actinst表,条件:根据 InstanceId 查询
// instanceQuery.processInstanceId("2501");
// 查询 actinst表,条件:根据 DefinitionId 查询
instanceQuery.processDefinitionId("myEvection:1:4");
// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// 查询所有内容
List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
}
流程定义部署在
activiti后,就可以在系统中通过activiti去管理该流程的执行,执行流程表示流程的一次执行。当多实例启动的时候,指定
businesskey作为业务标识,就会在act_ru_execution流程实例的执行表中存储businesskey,其与流程实例一一对应
businesskey作为业务标识可以是出差单id或者采购单id等由业务系统发来的数据同activi数据产生联系
/**
* 启动流程实例,添加businessKey
*/
@Test
public void addBusinessKey(){
// 1、得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、启动流程实例,同时还要指定业务标识businessKey,也就是出差申请单id,这里是1001
ProcessInstance processInstance = runtimeService.
startProcessInstanceByKey("myEvection","1001");
// 4、输出processInstance相关属性
System.out.println("业务id=="+processInstance.getBusinessKey());
}
流程在运行过程中可以查询流程实例的状态,当前运行结点等信息。
@Test
public void queryProcessInstance() {
// 流程定义key
String processDefinitionKey = "evection";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> list = runtimeService
.createProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)//
.list();
for (ProcessInstance processInstance : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:"
+ processInstance.getProcessInstanceId());
System.out.println("所属流程定义id:"
+ processInstance.getProcessDefinitionId());
System.out.println("是否执行完成:" + processInstance.isEnded());
System.out.println("是否暂停:" + processInstance.isSuspended());
System.out.println("当前活动标识:" + processInstance.getActivityId());
}
}
注意:
当系统查询流程实例时若是要同时显示比如出差天数,报销金额等处于业务系统数据库中的数据,则可以通过获取activiti数据中的businessKey,并以此当作主键,在业务表中进行查询