目录
8.2.1使用Eclipse创建一个web项目(act_study)
8.2.8使用Mybatis代码生成器,生成Mapper和xml文件,并复制到相关包下
第一章了解工作流
在传统软件系统开发中,特别是一些辅助业务办公的大型OA系统,很多都会涉及到审批流程的相关功能,将线下流程放到线上审批,作为软件开发者来说,实现方式有很多,简单的流程还好,如果流程比较复杂或者流程的任务节点比较多,这个时候最好是采用一个比较成熟的流程引擎框架,通过流程引擎来实现流程的流转,减少逻辑代码的开发,网上有很多关于Activiti的相关博客,但是都是比较零散的知识点,并没有一个系统的使用教程。本教程适合新手快速入门,并没有记录太多底层原理,如果你是Activiti专家可以忽略,如果你是新手小白,这篇文章一定会对你有所帮助。
第二章Activiti的数据库设计
2.1Activiti数据库介绍
在使用流程引擎的过程中,必然会产生很多的流程数据,这些数据一定会有一些相关的数据库表来进行存储,不过这些不需要我们自己来考虑如果设计,Activiti已经为我们提供了一整套的数据库表,并且支持多种数据库类型,当我们在部署流程的时候,Activiti就会自动在数据库中创建出相关的表,所有的表名都是以“ACT_”开头,主要包括“流程部署表”、“流程定义表”、“流程实例表”、“用户及用户组表”、“流程任务表”等一共二十多张表,但实际上我们也不需要搞清楚每一张表每个字段的具体含义,Activiti自动生成的表是没有注释的,所以我们也很难弄清楚这二十多张表的依赖关系,但是不用担心,Activiti为我们提供了一整套的服务组件,我们只需要了解这些服务组件,通过调用这些组件来实现流程数据的操作即可,在实际使用过程中,尽可能的不要直接去操作数据库的表,避免造成不必要的麻烦。下面先来了解一下在Activit中一些比较关键的表。
2.2Activiti中的数据库表
2.2.1流程部署表
流程部署表(ACT_RE_DEPLOYMENT),当我们定义好一个流程的时候,第一件事就将定义好的流程文件进行部署,部署流程所产生的数据将会保存到ACT_RE_DEPLOYMENT表中,其主要字段包括“部署名称”、“部署时间”等。
2.2.2流程定义表
流程定义表(ACT_RE_PROCDEF)我们所绘制的流程图,最终都是以一个“.bpmn”的文件存储在我们的项目当中,当对流程文件进行部署的时候,Activiti就会解析流程文件中的内容,将解析出来的数据存储到流程定义表中,其主要字段包括“流程定义分类”,“流程定义名称”,“流程定义key”,“流程文件的路径”等。
2.2.3用户表
用户表(ACT_ID_USER),存储的是Activiti的用户信息,这个用户就是在流程中,谁发起了流程提交给了谁,由谁来处理,实际也就是对应我们各自业务系统的用户信息,其主要字段包括“用户名”、“密码”,“邮箱”等。
2.2.4用户分组及关系表
用户表(ACT_ID_GROUP),是针对用户的分组,我们可以将其理解为业务系统中的角色,每个用户都有属于自己的角色,其主要字段包括“分组类型”,“分组名称”等。既然有用户和角色,那就一定会有一个用户和角色的关系表(ACT_ID_MEMBERSHIP)。
2.2.5流程实例表
流程实例表(ACT_RU_EXECUTION),当流程启动后,会产生一条流程实例,流程实例的数据存在ACT_RU_EXECUTION表中,其主要字段包括“流程实例ID”,“指定流程关联的业务表主键”等。
2.2.6流程任务表
流程任务表(ACT_RU_TASK),流程在运行过程中所产生的任务数据,保存在ACT_RU_TASK表中,任务数据其实就是我们各业务系统中的代办数据,用户登录系统,会看到需要自己处理的代办数据,例如员工提交的申请单需要领导来进行审批,其主要字段包括“流程实例ID”,“流程定义ID”,“任务名称”,“任务描述”,“任务的拥有人”,“任务的代理人”等。
2.2.7流程参数表
流程参数表(ACT_RU_VARIABLE),用于存放流程中的参数,例如,在流程流转过程中,我们可以通过设置参数,来判断流程的分支走向,其主要字段包括“参数类型”,“参数名称”,“流程实例ID”,“任务ID”等。
第三章流程图的绘制
3.1安装流程设计器插件
想要进行流程图的绘制,首先我们要在我们的开发工具中安装流程设计器插件,下面我们以Eclipse为例,演示如何安装并使用流程设计器。
安装流程设计器插件有两种方式,一种是在有网络的情况下,可以直接在线安装,但是受网络因素影响,在线安装的速度一般会比较慢。另外一种方式也可以采用离线安装,直接将下载好的插件包放到指定目录下即可。
第一步:准备好下载的插件包,这里使用的是activiti-designer-5.18.0.zip,解压后会看到有“features”和“plugins”两个文件夹
另外还有一个jars文件夹,下面包含三个jar包
首先将jars文件夹下的三个jar包复制到eclipse安装目录的plugins目录下,并删除eclipse安装目录下的configuration文件夹里的org.eclipse.update文件夹,然后重启eclipse。
第二步:重新打开eclipse,选择Help->Install New Software,然后在弹出框中选择Add 。
点击Local选择activiti-designer-5.18.0所在目录
点击Next,等待安装。。。。。。
第三步:验证是否安装成功
重启Eclipse,选择File->New->Other,输入Activiti,出现如下选项,则说明安装成功
3.2流程设计器的使用
下面我们新建一个流程,File->New->Other->Activiti Diagram,例如新建一个MyProcess.bpmn文件,打开这个文件,会出现如下界面,右侧是流程设计器的一些元素,下面对元素的一些属性的配置。我们可以通过拖动右侧的各种元素来绘制满足我们业务需求的流程图。
下面了解一下比较常用的各个元素的含义和用途。
下面我们可以利用上面的元素来动手画一个简单的流程图
除了认识流程设计器中的各个元素,还需要了解各元素的一些属性配置,下面是几个常用的属性配置
第四章流程引擎的配置和创建
4.1流程引擎配置对象
在使用Activiti流程引擎时,首先需要通过配置一系列的参数来创建一个流程引擎配置对象ProcessEngineConfiguration,这些参数包括:
- 数据库的配置:配置使用数据库的类型,数据库的策略等。
- 事务的配置:通常会指向Spring中配置的事务管理器。
- 内置服务的配置:内置了一系列的服务接口,用来操作流程数据。
- 个性化的配置:加入自定义的个性化配置。
该对象代表一个流程引擎的全部配置,提供了一系列的创建流程引擎实例的方法,一般可以采用取读取默认配置文件或者通过读取输入流的方式来创建流程引擎配置对象,但是实际应用过程中,我们通常会将Activiti和Spring做集成,通过将在Spring中注入“bean”的方式来创建流程引擎配置对象,实例代码如下:
<!-- 配置流程引擎配置信息对象 -->
<bean id="processEngineConfiguration"
class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="databaseSchemaUpdate" value="true"/>
<property name="transactionManager"ref="transactionManager"/>
<property name="deploymentResources"value="classpath*:com/diagram/*.bpmn"/>
</bean>
- dataSource:指定数据源。
- transactionManager:指定spring配置的事务管理器。
- deploymentResources:指定流程文件所在的路径。
- databaseSchemaUpdate:流程引擎启动和关闭时数据库的执行策略,可以有以下三个值:
true:设置为true后,Activiti会对数据库中所有的表进行更新,如果不存在,则会自动创建表。
false:默认值为false,设置为false后,Activiti在启动后,会对比数据库表中保存的版本,如果版本不对或者没有表则会抛出异常。
craete-drop:设置值为create-drop,则在Activit启动时会自动创建表,在Activiti关闭时会自动删除表。
4.2流程引擎对象的创建
通过上面注入到Spring中的流程引擎配置对象,可以创建一个流程引擎实例,在Activiti内部,ProcessEngineConfiguration提供了一个buildPorcessEngine的方法用来创建流程引擎实例processEngine,根据流程引擎的相关配置,buildPorcessEngine会初始化相关引擎的服务和对象,包括数据源、服务组件、事务、拦截器等,这个流程引擎的初始化过程实际也是流程配置的一个检查过程。
和ProcessEngineConfiguration一样,我们在实际应用中,通常也是通过在Spring中注入bean的形式来初始化流程引擎对象,实例代码如下:
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration"/>
</bean>
其中 processEngineConfiguration为引入流程引擎的配置对象
4.3服务组件
流程引擎对象中提供了一系列的服务接口组件,这些组件提供了大部分的操作流程引擎数据的方法,使用这些组件,就像在WEB应用中注入的接口一样,直接调用其中的方法即可,processEngine主要包含如下组件:
- RepositoryService:主要提供定义流程和部署流程的接口。
- RuntimeService:主要提供管理流程运行中的流程实例和数据的接口。
- TaskService:提供一系列管理流程任务的接口。
- HistoryService:主要提供操作流程历史数据的接口。
- IdentityService:主要提供操作流程用户和用户组的接口,例如用户和用户组的增加修改。
- ManagementService:提供管理和维护流程引擎的接口。
- DynarnicBpmnService :在不重新部署流程模型的情况下对流程模型进行修改。
还是和流程引擎配置对象和流程实例一样,我们在实际应用中依然是通过在Spring中注入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="formService" factory-bean="processEngine" factory-method="getFormService"/>
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService"/>
第五章用户和用户组
每个系统都会有属于自己的角色和用户,Activiti中的用户和用户组就可以对应自己业务系统的用户和角色,比如我们在系统中发送一个请假申请单给总经理这个角色的时候,我们可以选择给所有拥有总经理角色的用户,也可以指定发送给具体是哪个总经理用户。所以,在使用Activiti时候,需要保持Activiti中的用户和用户组和业务系统中的用户和角色数据保持一致。操作用户和用户组的服务组件是identityService
5.1用户
所有用户数据都保存在ACT_ID_USER表中,在Activiti中有与其映射的一个实体类User对象,主要属性包括
- ID:用户的id
- FirstName:用户姓
- LastName:用户名
- Email:用户邮箱
- Password:用户密码
- Revesion:用户数据的版本
下面直接看示例代码
添加和修改用户都调用saveUser方法
User user = identityService.newUser(UUID.randomUUID().toString());
user.setLastName("员工甲");
user.setFirstName("员工甲");
user.setPassword("123456");
identityService.saveUser(user);
删除数据用户
String uuid = UUID.randomUUID().toString();
User user = identityService.newUser(uuid);
user.setLastName("员工乙");
user.setFirstName("员工已");
user.setPassword("123456");
//保存用户
identityService.saveUser(user);
//删除用户
identityService.deleteUser(uuid);
用户数据查询
Activit为用户数据的查询提供了一个UserQuery查询对象,该对象包括了一系列的查询方法,
部分代码示例如下:
//创建用户查询对象
UserQuery userQuery = identityService.createUserQuery();
//升序查询结果集
List<User> users1 = userQuery.asc().list();
//查询总记录数
long l = userQuery.count();
//降序查询结果集
List<User> users2 = userQuery.desc().list();
//分页查询结果集
List<User> users3 = userQuery.listPage(0, 10);
//根据Email查询用户
User user1 = userQuery.userEmail("1234@qq.com").singleResult();
//根据Email模糊查询用户
List<User> users4 = userQuery.userEmailLike("1234").list();
//根据姓名查询用户
User user2 = userQuery.userFirstName("zhang").singleResult();
//根据姓名模糊查询用户
List<User> users5 = userQuery.userFirstNameLike("zhang").list();
//根据用户ID查询用户数据
User user3 = userQuery.userId("ae372ec9f3c14074bcf49141cc1ad19a").singleResult();
//根据用户组ID查询属于该用户组下的所有用户数据
List<User> users6 = userQuery.memberOfGroup("ab918f98f669482f99725ac2c84fddcb").list();
//根据用户ID进行排序
List<User> users7 = userQuery.userLastNameLike("zhang").orderByUserId().list();
5.2用户组
所有用户组数据都保存在ACT_ID_GROUP表中,在Activiti中有与其映射的一个实体类Group对象,主要属性包括
Id:用户组主键
Name:用户组名称
Type:用户组类型
Revision:用户组数据版本
操作用户组数据的示例代码如下
添加和修改用户组都调用saveGroup方法,用法与操作用户类似。
Group g1 = identityService.newGroup(UUID.randomUUID().toString());
g1.setName("人事组");
g1.setType("HR");
identityService.saveGroup(g1);
删除用户组数据
Group g1 = identityService.newGroup(UUID.randomUUID().toString());
g1.setName("人事组");
g1.setType("HR");
identityService.saveGroup(g1);
identityService.deleteGroup(g1.getId());
用户组数据查询
//创建用户组查询对象
GroupQuery groupQuery = identityService.createGroupQuery();
//升序查询结果集
List<Group> groups = groupQuery.asc().list();
//查询总记录数
long l = groupQuery.count();
//降序查询结果集
List<Group> groups2 = groupQuery.desc().list();
//分页查询结果集
List<Group> users3 = groupQuery.listPage(0, 10);
//根据分组类型查询分组对象
Group group = groupQuery.groupType("HR").singleResult();
//根据分组名称模糊查询分组信息
List<Group> groups4 = groupQuery.groupNameLike("HR").list();
//根据用户组ID查询用户组数据
Group group3 = groupQuery.groupId("ae372ec9f3c14074bcf49141cc1ad19a").singleResult();
//根据用户组类型进行排序
List<Group> group7 = groupQuery.groupNameLike("HR").orderByGroupType().list();
5.3用户和用户组关系
所有的用户和用户组关系数据保存在ACT_ID_MEMERSHIP表中,但是和用户与用户组不同,Activiti中没有对应的映射实体类。
绑定用户和用户组的关系
//新增用户组
Group g1 = identityService.newGroup(groupId);
g1.setName(groupName);
g1.setType(gouptype);
identityService.saveGroup(g1);
//新增用户
User u1 = identityService.newUser(userId);
u1.setLastName(userName);
u1.setFirstName(userName);
u1.setPassword(passd);
identityService.saveUser(u1);
//绑定用户和用户组的关系
identityService.createMembership(u1.getId(), g1.getId());
解除绑定
//新增用户组
Group g1 = identityService.newGroup(groupId);
g1.setName(groupName);
g1.setType(gouptype);
identityService.saveGroup(g1);
//新增用户
User u1 = identityService.newUser(userId);
u1.setLastName(userName);
u1.setFirstName(userName);
u1.setPassword(passd);
identityService.saveUser(u1);
//绑定用户和用户组的关系
identityService.createMembership(u1.getId(), g1.getId());
//删除绑定关系
identityService.deleteMembership(u1.getId(), g1.getId());
查询用户组下的用户
//查询用户组下的用户
identityService.createUserQuery().memberOfGroup("用户组ID").list();
//查询用户所属用户组
List<Group> list = identityService.createGroupQuery().groupMember("用户ID").list();
第六章流程任务管理
在使用Activit的过程中,流程的部署和发布方式比较固定,我们只需要记住其中一种固定的方式即可,相比较而言,流程的任务管理相对比较多样化,任务管理是流程引擎中比较重要的一部分,整个流程的流转过程,实际就是任务数据的流转过程,所以在使用Activiti之前必须先对任务管理的基本概念有所了解。
所有的任务数据保存在ACT_RU_TASK表中,Activiti也提供了对应的服务接口来实现任务数据的增加修改删除,用法和用户与用户组的增删改类似,但我们实际应用过程中,大部分情况下不需要手动的去添加任务数据,根据流程图的配置,Activiti会自动添加流程任务数据,对于我们来说,最重要的是知道如何查询任务数据,也就是在业务系统中的代办任务列表。
6.1任务候选用户
候选用户,指的是一个用户群体,这个群体中所有人都有执行该任务的权限,但是最终实际处理任务的人只能有一个,候选用户可以在流程图中进行配置,也可以通过taskService接口来设置候选用户,候选用户与任务是多对多的关系。
taskService.addCandidateUser("任务ID", "用户ID");
根据候选用户查询所有任务
//根据候选用户查询任务
List<Task> list = taskService.createTaskQuery().taskCandidateUser("用户ID").list();
6.2任务候选用户组
与候选用户类似,区别是针对的是用户组,也就是业务系统的角色,候选用户组也可以在流程图中设置,也可以通过taskService接口来进行设置
taskService.addCandidateGroup("任务ID", "用户组ID");
根据用户组查询所有任务
//根据候选用户组查询任务
List<Task> list2 = taskService.createTaskQuery().taskCandidateGroup("用户组ID").list();
6.3任务持有人
在流程任务表ACT_RU_TASK中有一个OWNER_字段,用于存储任务的持有人。持有人是指当前任务的执行人,实际持有该任务的人,持有人和任务是一对多的关系,一个人可以持有多个任务,但是一个任务只能有一个持有人。
//设置任务的持有人
taskService.setOwner("任务ID", "用户ID");
通过任务持有人字段查询任务
//查询持有人的任务
List<Task> list3 = taskService.createTaskQuery().taskOwner("用户ID").list();
6.4任务代理人
除了任务持有人,在流程任务表ACT_RU_TASK中还中有一个ASSIGNEE_字段,用于存储流程代理人,当任务的持有人因为某些原因无法处理任务的时候,可以将任务指派给另一个人,被指派的人即为代理人。一个人可以代理多个任务,一个任务只能有一个代理人,因此代理人和任务的关系是一对多的关系。
//设置任务代理人
taskService.setAssignee("任务ID", "用户ID");
通过任务代理人查询代理任务
//通过任务代理人查询代理任务
List<Task> list = taskService.createTaskQuery().taskAssignee("用户ID").list();
除了调用setAssignee方法设置代理人外,还可以通过claim方法设置代理人,区别是,setAssignee方法可以随意更改代理人,但是一旦调用claim方法,当再次调用该方法将同一个任务分配给其他人的时候,就会抛出异常。
taskService.claim("任务ID", "用户ID");
6.5删除任务权限数据
taskService中也提供了删除任务权限的方法
//根据任务ID和候选用户组ID删除任务
taskService.deleteCandidateGroup("任务ID", "用户组ID");
//根据任务ID和候选用户ID删除任务
taskService.deleteCandidateUser("任务ID", "用户ID");
//删除持有人持有的任务
taskService.deleteUserIdentityLink("任务ID", "用户ID", "OWNER");
//删除代理人代理的任务
taskService.deleteUserIdentityLink("任务ID", "用户ID", "ASSIGNEE");
6.6设置任务参数
Activiti提供可以设置任务参数的方法,在实际应用中,通常会把业务主键(例如请假申请单的主键ID),判断流程分支走向的参数(例如pass=0表示同意,pass=1表示驳回),设置到任务参数中,这些参数可以贯穿整个流程,通过任务参数将任务和“业务数据”关联到一起。
//定义任务参数
Map<String, Object> var = new HashMap<String,Object>();
//放入参数值
var.put("businessID", "请假申请单ID");
var.put("pass", "1");
//设置任务参数
taskService.setVariables("任务ID", var);
//获取任务参数
Map<String, Object> map = taskService.getVariables("businessID");
6.7添加任务的评论
在实际应用场景中,通常某个用户在处理某一个代办任务的时候,一般都会填写一些个人意见
//添加评论
taskService.addComment("任务ID", "流程实例ID", "审批意见");
//查询评论
List<Comment> comments = taskService.getTaskComments("任务ID");
6.8任务的完成
当想要完成当前任务,进入到下一个任务节点的时候,只需要调用complete方法即可。
流程就会按照流程图中的分支走向下一个节点
taskService.complete("任务ID","任务参数");
第七章案例应用
需求分析:现在我们根据上面学习的基础知识,来实际实现一个请假的流程,参与请假流程的角色为“员工”“项目经理”“部门主任”“人事”。
员工的请假流程为:员工提交--->项目经理审批-->部门主任审批--->人事审批。
项目经理的请假流程为:项目经理提交-->部门主任审批-->人事审批。
部门主任的请假流程为:部门主任提交-->人事审批。
所有处理人均可以退回给发起人。
设计分析: