Activiti工作流学习总结

来源:互联网 发布:android chroot linux 编辑:程序博客网 时间:2024/05/16 08:00

一、工作流的初步认识

在我初步学习完工作流的理解中,工作流就是将开发中由代码控制的业务流程状态抽取出来然后进行统一控制的机制!比如在实际开发中,我们需要表明一个状态的改变,可以通过字段status来进行转化,常见的业务请假流程四个环节的状态有:'待提交审核','主管审核'、'经理审核'、'审核完成',当我们在实现这几个状态的改变时,是通过硬编码实现的,执行待提交审核状态,就一定会到达主管审核,以此类推!如果这时业务需求发生了改变,流程只需要三个环节:待提交审核','主管审核'、'审核完成',这时我们就得去更改代码了,需要在进行'主管审核'后,直接将状态更改为;审核完成'状态,看起来这样好像也不麻烦,就更改一处!!但是,这只是一个小小的流程而已,如果业务再复杂一点呢,一旦更改流程,是不是代码需要大改了?这样着实不方便啊!而工作流,恰恰帮我们解决了这个问题,工作流就适用于业务复杂且需求经常性变更的流程。

二、工作流的学习内容

1、加载工作流插件

在eclipse中加载插件工作流插件http://www.activiti.org/designer/update/

2、两种工作流环境的搭建:

a、activiti单独运行StandaloneProcessEngineConfiguration
b、activiti与spring整合SpringProcessEngineConfiguration
搭建时需要五种activity表处理策略,即databaseSchemaUpdate配置的五种值
false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。
true:构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建。(常用)
create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。
drop-create:先删除表再创建表。(常用,使用完成后改为true)
create: 构建流程引擎时创建数据库表, 关闭流程引擎时不删除这些表。

3、设计流程定义

a、加载完eclipse的工作流插件后,我们可以在新建中找到Activiti下的Activiti Diagram进行bpmn文件的创建
b、可以在eclipse-->window-->preferences-->Activiti-->save action下勾选 create process definition image when saving the diagram 大概意思就是当保存bpmn文件时,就会自动生成png文件,这两个文件是流程部署需要用到的文件,也是我们设计流程定义时的文件
c、通过eclipse的Activiti  Diagram Editor打开bpmn文件
c1、有一个空白的面板是我们需要操作设计的地方
c2、Palette面板是我们选择的设计图形
c2.1、select选择器:可以进行选择设计图形,然后进行properties的值修改
c2.2、StartEvent开始事件:工作流的开始标志
c2.3、EndEvent结束事件:工作流的结束标志
c2.4、UserTask任务:工作流整个流程中的任务,可以指定任务负责人、候选人、候选组
c2.5、ParallelGateway并行网关:实现流程的分支与汇聚,所有的分支都不用进行条件判断,所有的分支都要统一汇聚到一起,才能执行下一个任务
c2.6、ExclusiveGateway排他网关:流程分支需要通过判断为true才能进行执行,如果所有分支都不能执行就会抛出异常,如果有1个以上的分支通过判断为true,那么只会执行其中一个分支,这种情况需要避免!
c2.7、InclusiveGateway包含网关:实现并行网关和排他网关的结合体,流程分支需要进行条件判断才能进行执行,条件不通过的不执行,所有通过条件判断的分支需要全部汇聚到一起,才能执行下一个任务
c2.8、SequenceFlow连线:设计图形之间的连接操作,可以加上condition条件,每个分支正是通过这个条件进行判断的
c3、Properties设计图形的属性查看,选择哪个设计图形就能查看相对应的属性
c31、不选中任何图形进行定义流程的Process:id为流程定义标识purchasingflow(重要),name为流程定义名称
c32、General下的id是选中的设计图形的唯一标识(重要),name即选中的设计图形的名称
c33、选中StartEvent开始事件:Main Config下的Initiator发起人(重要),我们可以对此属性进行设值startUser。而在接下来的第一个任务中我们可以通过${startUser}来给第一个任务的Assignee属性赋予值,即是这个流程的第一个任务执行者,即是这个流程的发起人。
c34、选中SequenceFlow:Main Config下的Condition即是流程分支的条件判断,可以通过借助流程变量进行流程条件编写,比如${userType=='1'},userType即是我们在代码中设置的流程变量
c35、选中UserTask任务:
Assignee一般对应业务数据中的用户id,任务负责人,可以通过流程变量进行编写或者直接硬编码指定用户id
Candidate users一般对应业务数据中的用户id,可以指定多个候选人,用','隔开,但是所有的候选人需要通过任务拾取才能变成任务的执行人,所有的候选人也可以拾取任务后再执行任务的设置或者归还任务
Candidate groups(重要)一般对应业务数据中的角色id,指定一个组id,与这个组id关联下的所有users的id都将成为候选人
c36、选中UserTask任务:Listeners监听器(重要),可以设置监听器在任务的哪个阶段Event进行执行

Create:在任务创建时执行

Assignment:在任务分配时执行

Complete:在任务完成时执行

All:以上边全部情况下执行

加载select class,该class文件需要实现TaskListener

当选中EndEvent事件进,class文件需要实现ExecutionListener

监听器可以方便我们在执行完某个任务后是否要对业务数据的状态进行更新,从而方便我们对业务数据的统计

比如,我们将流程的整个状态显示从业务流程抽取出来,仅当当通过业务数据是无法知道该业务数据的流程是否结束,此时就需要关联activiti表,这样是比较麻烦的,也是比较耗性能,所以可以通过在任EndEvent加载一个监听器,当执行完整个流程时,对业务数据的状态字段进行更新为‘已完成’,即可知道该业务数据已经结束,也无须再关联activiti表


4、部署流程定义


通过RepositoryService进行流程定义的部署,主要是对两个文件:bpmn文件和png文件的部署

Deployment deployment = repositoryService.createDeployment().addInputStream(bpmn_name, bpmn_inputStream)// 部署bpmn文件.addInputStream(png_name, png_inputStream)// 部署png文件            .deploy();
bpmn_name和bpmn_inputStream分别对应为bpmn文件的名称和输入流

     png_name和png_inputStream分别对应png文件的名称和输入流

可以在act_re_procdef查看到部署的流程定义信息

ID_表示流程定义id

NAME_表示流程定义名称(设计表时Process下的name)

KEY_表示流程定义标识(设计表时Process下的id)

DEPLOYMENT_ID_表示流程部署id,对应act_re_deployment流程部署的id

5、查询流程定义---act_re_procdef

通过RepositoryService获取到ProcessDefinitionQuery流程定义查询对象,指定流程定义标识,获取到该流程定义标识的所有流程定义
                ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();      processDefinitionQuery.processDefinitionKey(processDefinitionKey);      List<ProcessDefinition> list = processDefinitionQuery.list();

6、删除流程定义---act_re_procdef

通过RepositoryService执行deleteDeployment()方法进行删除流程部署时级联删除流程定义,所以删除某个流程定义,需要先通过RepositoryService获取到ProcessDefinitionQuery流程定义查询对象,指定流程定义id,查询到ProcessDefinition流程定义对象,从而获取到部署id,才能进行级联删除流程定义

ProcessDefinition processDefinition = repositoryService      .createProcessDefinitionQuery()      .processDefinitionId(processDefinitionId).singleResult();String deploymentId = processDefinition.getDeploymentId();repositoryService.deleteDeployment(deploymentId, true);

7、获取流程定义资源文件输入流,从而可以获取到资源文件
通过RepositoryService获取到ProcessDefinitionQuery流程定义查询对象,指定流程定义id,查询到ProcessDefinition流程定义对象,从而可以获得流程部署id、流程定义资源文件名称,通过流程部署id和流程定义资源文件名称可以获取到流程定义资源文件输入流

// bpmn资源文件名称String bpmnName = processDefinition.getResourceName();// png资源文件名称pngName = processDefinition.getDiagramResourceName();// 资源 文件输入流InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId, bpmnName);
InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId, pngName);

8、启动流程实例、开始一个流程

通过IdentityService的setAuthenticatedUserId(String userId)可以设置流程发起人id,即流程定义时的initial对应的startUser值,即为启动该流程的发起人
通过RuntimeService的startProcessInstanceByKey(String processDefinitionByKey,String business)方法可以指定流程定义标识和业务标识进行开启流程实例,业务标识是可以将该流程实例与业务数据绑定起来,同时获得的流程实例对象也可以将id绑定业务系统的表结构中,进行互相关联
//在启动流程实例前,同时设置流程实例发起人identityService.setAuthenticatedUserId(userId);//两个参数 ---processDefinitionKey流程定义key,businessKey业务标识(即业务数据的id,在整个流程中获取到业务数据)ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey);

9、查询任务负责人的待办任务、进行执行任务---act_ru_task


通过TaskService获取到TaskQuery任务查询对象,指定assignee任务负责人,即可以查询到相对应的待办任务

// 创建查询对象TaskQuery taskQuery = takService.createTaskQuery();// 设置查询条件
taskQuery.taskAssignee(assignee);// 指定 流程定义key,只查询某个流程的任务taskQuery.processDefinitionKey(processDefinitionKey);//根据任务的创建时间降序排序askQuery.orderByTaskCreateTime().desc();// 获取查询列表List<Task> list = taskQuery.list();
若需要获取该任务对应的业务数据,可以通过Task对象获取到流程实例id,通过RunTimeService获取到流程实例查询对象ProcessInstanceQuery,指定流程实例id进行查询流程实例对象ProcessInstance,从而获取到流程实例对象中的businessKey,从而关联到业务数据

10、任务负责人执行待办任务、推进流程

通过TaskService获取到TaskQuery任务查询对象,指定任务id和任务负责人来查询是否存在该任务,若存在则执行
TaskService.complete(String taskId, Map<String Object> valiables)方法,valiables参数可要可不要,是流程实例的全局变量,指定刻全局变量可以接下来的流程中的连线中的Condition条件起作用,比如在valiables.put("status","1");
Condition中的条件${status=='1'}将会返回true,流程变量可以是简单类型,也可以是pojo类型,但是pojo类型需要实现序列化
//根据任务id和assignee查询该任务Task task = takService.createTaskQuery().taskId(taskid).taskAssignee(assignee).singleResult();if(task!=null){//定义全局变量Map<String,Object> variables = new HashMap<String,Object>();variables.put("status", "1");takService.complete(taskId,variables);}

若需要获取该任务对应的业务数据,可以通过Task对象获取到流程实例id,通过RunTimeService获取到流程实例查询对象ProcessInstanceQuery,指定流程实例id进行查询流程实例对象ProcessInstance,从而获取到流程实例对象中的businessKey,从而关联到业务数据

11、查看当前的流程实例列表---act_ru_execution、监控流程

//通过RuntimeSerivice获取到流程实例查询对象,指定流程定义标识,流程实例id降序查询流程实例对象
ProcessInstanceQuery processInstanceQuery =  runtimeService.createProcessInstanceQuery();//指定 流程定义key只查询该类流程的实例,比如key为采购流程,只查询采购流程实例processInstanceQuery.processDefinitionKey(processDefinitionKey);processInstanceQuery.orderByProcessInstanceId().desc();//获取查询列表List<ProcessInstance> list =  processInstanceQuery.list();

12、查询已结束的流程实例列表----act_hi_procinst、监控流程

通过HistoryService获取到HistoricProcessInstanceQuery历史流程实例查询对象,执行finished()方法,查询已结束的流程实例列表
//创建历史流程实例 查询对象HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();historicProcessInstanceQuery.processDefinitionKey(processDefinitionKey);//设置只查询已完成的流程实例 historicProcessInstanceQuery.finished();historicProcessInstanceQuery.orderByProcessInstanceEndTime().desc();//数据列表List<HistoricProcessInstance> list =  historicProcessInstanceQuery.list();
同样如果需要关联到业务数据可以通过
HistoricProcessInstance对象的getBusinessKey()得到businessKey,从而关联到业务系统的数据表中

13、查询某个流程实例的历史任务实例act_hi_taskinst、监控流程

通过HistoryService获取到HistoricTaskInstanceQuery查询历史流程任务
//创建查询对象,用于查询历史 任务HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();//设置查询条件
historicTaskInstanceQuery.processDefinitionKey(processDefinitionKey);//指定 流程实例 id,只查询该流程实例执行的历史任务,流程实例的id可以完成也可以未完成的historicTaskInstanceQuery.processInstanceId(processInstanceId);historicTaskInstanceQuery.orderByHistoricTaskInstanceStartTime().asc();List<HistoricTaskInstance> list =  historicTaskInstanceQuery.list();

14、查询候选人组任务

即是配置了Candidate-groups或Candidate-users配置的候选组或候选人的任务,符合候选人或候选组的人都可以通过TaskService的TaskQuery指定候选人进行任务的查询
// 任务查询对象TaskQuery taskQuery = takService.createTaskQuery();taskQuery.taskCandidateUser(userId);taskQuery.processDefinitionKey(processDefinitionKey);List<Task> list = taskQuery.list();

这时是需要将activiti的act_id_user、act_id_group、act_id_membership跟业务数据的用户表user、角色表role、用户角色关联表user_role进行同步的,当数据达成一致时,我们在流程定义设计时,在候选人Candidate-users设置的用户id,与Candidate-groups候选组设置的角色id才会与登录信息中的用户id和角色匹配上

15、通过拾取任务,从候选人状态更改为任务负责人

通过TaskService获取到TaskQuery对象指定候选人和任务,查询是否该用户符合拾取的资格再进行拾取任务,调用TaskService的claim(String taskId,String userId)

// 根据候选人和组任务id查询,如果有记录说明该 候选人有资格拾取该 任务Task task = takService.createTaskQuery().taskId(taskId).taskCandidateUser(userId).singleResult();
if (task != null) {// 任务拾取taskService.claim(taskId, userId);}当成为任务负责人后,也可以通过taskService.claim(taskId,null)归还任务或taskService.claim(taskId,userId)重新指定给其他候选人

三、案例:请假流程

开始--->员工申请请假
                     --->主管审核-->主管审核不通过--->员工申请请假
                           ---主管审核通过--->(申请天数达3天以上需要)经理审核--->经理审核不通过--->员工申请请假
                                                                                                      ---->经理审核通过--->(结束)
                                                                     ---->(申请天数小于3天不需经理审核直接)审核通过--->(结束)

1、在流程定义的设计中(对应第二部分的3)

我们在“开始事件”设置initial为startUser,在第一个任务“员工申请请假”的Assignee的值中设计成${startUser}
在“主管审核”任务中设置Candidate-groups对应act_id_group中主管身份的id值
在“经理审核”任务中设置Candidate-groups对应act_id_group中经理身份的id值
在“主管审核”任务中指向“员工申请请假”的连线condition上设置${status=='0'}
在“主管审核”任务中指向“经理审核”的连接condition上设置${status=='1'&&day>=3}
在“主管审核”任务中指向“结束”的连接condition上设置${status=='1'&&day<3}
在“经理审核”任务中指向“员工申请请假”的连线condition上设置${managerStatus=='0'}
在“经理审核”任务中指向“结束”的连线condition上设置${managerStatus=='1'}

2、将设计出的流程定义进行部署(对应第二部分的4)

3、流程开始

a、员工填写请假条,进行点击保存,这时就要开启流程实例,并设置流程发起人为该登录员工的id,并且需要指定该请假条的信息key,因为到时需要设置全局变量day(对应第二部分的8)
b、该员工可以查看他当前的待办任务(对应第二部分的9)
c、该员工可以提交请假任务,这时需要获取通过Task获取流程实例id,从而获取到流程实例查询对象,接着获取到businessKey,从而关联得到该员工请假的day数据,并把day放入到variables里(对应第二部分的10)
d、这时到达了主管审核,登录系统的主管需要通过查询候选人组任务(对应第二部分的14),查看需要审核的任务,点击拾取操作(对应第二部分的15)成为该任务的负责人,然后查看他当前的待办任务进行(对应第二部分的9),然后主管就可以为该员工进行审核了,执行审核后需要把审核结果的status的值放入到variables中(对应第二部分的10)
如果status=='1'且day>=3,则流程会执行到“经理审核”
如果status=='1'且day<3,则流程结束
如果status=='0',则流程会执行到“员工申请请假”,员工可以重新更改申请信息,进行再次申请
e、到达“经理审核”,登录系统的经理需要通过查询候选人组任务(对应第二部分的14),查看需要审核的任务,点击拾取操作(对应第二部分的15)成为该任务的负责人,然后查看他当前的待办任务(对应第二部分的9),然后主管就可以为该员工进行审核了,执行审核后需要把审核结果managerStatus的值放入到variables中(对应第二部分的10)
如果managerStatus==‘1’,则流程结束
如果managerStatus==‘0’,则会回到”员工申请请假“,员工可以重新更改申请信息,进行再次申请


四、总结

1、从第三部分的案例中,
如果业务需求更改为只要申请一个月才需要经理审核,我们可以直接设计流程定义,重新进行部署流程定义即可解决业务需求的变更
如果业务需要突然不需要主管审核了,我们可以直接设计流程定义,删除主管审核环节,重新部署即可解决业务需求的变更
2、在实际开发中,案例中的day最好存放一个pojo对象,然后引用pojo的变量数据即可,这时如果业务需求变更需要用到其他数据作为判断依据,我们也不需要进行更改代码了,只要更改condition条件即可,比如到经理的审核条件变成只要是主管身份才需要经理进行审核,那么在使用pojo对象时,我们尽量将登录用户的信息都保存到pojo对象中,通过${user.userType=='1'&&status=='1'}才到经理审核
3、在请假案例中也可以加入排他网关,防止条件没有一个符合时,导致流程异常终止
4、需要更复杂的分支与汇聚流程,可以使用包含网关和并行网关
5、需要进行任务后对业务数据进行操作,可以使用监听器
最后讲一讲activiti的常用表
act_ge_bytearray 存取了流程定义的资源文件和序列化的流程变量
act_hi_actinst 流程实例的历史任务
act_hi_identitylink 历史流程参与者的数据表
act_hi_procinst 历史流程实例表
act_hi_taskinst  历史任务实例表
act_hi_varinst 历史流程变量表
act_id_group候选组表
act_id_membership候选组与候选人关联表
act_id_user候选人表
act_re_deployment流程定义部署表
act_re_procdef流程定义信息表
act_ru_identitylink 当前流程参与者的数据表
act_ru_task当前任务表
act_ru_variable当前流程变量表




原创粉丝点击