Activiti7进阶
一.流程实例
流程实例(
ProcessInstance)代表流程定义的执行实例。
一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。
例如:用户或程序按照流程定义内容发起一个流程,这就是一个流程实例。
流程定义和流程实例的图解:
启动流程实例 并添加Businesskey(业务标识)
流程定义部署在activiti后,就可以在系统中通过activiti去管理该流程的执行,执行流程表示流程的一次执行。
比如部署系统出差流程后,如果某用户要申请出差这时就需要执行这个流程,如果另外一个用户也要申请出差则也需要执行该流程,每个执行互不影响,每个执行是单独的流程实例。
启动流程实例时,指定的businesskey,就会在act_ru_execution #流程实例的执行表中存储businesskey。
Businesskey:业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据。
比如:出差流程启动一个流程实例,就可以将出差单的id作为业务标识存储到activiti中,将来查询activiti的流程实例信息就可以获取出差单的id从而关联查询业务系统数据库得到出差单信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Test public void addBusinessKey(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myEvection", "1001"); System.out.println("业务id = " + processInstance.getBusinessKey()); }
|
Activiti的act_ru_execution中存储业务标识:
操作数据库表
启动流程实例,操作如下数据库表:
SELECT * FROM act_ru_execution #流程实例执行表,记录当前流程实例的执行情况
说明:
流程实例执行,如果当前只有一个分支时,一个流程实例只有一条记录且执行表的主键id和流程实例id相同,如果当前有多个分支正在运行则该执行表中有多条记录,存在执行表的主键和流程实例id不相同的记录。不论当前有几个分支总会有一条记录的执行表的主键和流程实例id相同
一个流程实例运行完成,此表中与流程实例相关的记录删除。
SELECT * FROM act_ru_task #任务执行表,记录当前执行的任务
说明:启动流程实例,流程当前执行到第一个任务结点,此表会插入一条记录表示当前任务的执行情况,如果任务完成则记录删除。
SELECT * FROM act_ru_identitylink #任务参与者,记录当前参与任务的用户或组
SELECT * FROM act_hi_procinst #流程实例历史表
流程实例启动,会在此表插入一条记录,流程实例运行完成记录也不会删除。
开始一个任务,不仅在act_ru_task表插入记录,也会在历史任务表插入一条记录,任务历史表的主键就是任务id,任务完成此表记录不删除。
SELECT * FROM act_hi_actinst #活动历史表,记录所有活动
活动包括任务,所以此表中不仅记录了任务,还记录了流程执行过程的其它活动,比如开始事件、结束事件。
查询流程实例
流程在运行过程中可以查询流程实例的状态,当前运行结点等信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void queryProcessInstance(){ String processDefinitionKey = "evection"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 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()); } }
|
关联BusinessKey
需求:
在activiti实际应用时,查询流程实例列表时可能要显示出业务系统的一些相关信息,比如:查询当前运行的出差流程列表需要将出差单名称、出差天数等信息显示出来,出差天数等信息在业务系统中存在,而并没有在activiti数据库中存在,所以是无法通过activiti的api查询到出差天数等信息。
实现:
在查询流程实例时,通过businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。
通过下面的代码就可以获取activiti中所对应实例保存的业务Key。而这个业务Key一般都会保存相关联的业务操作表的主键,再通过主键ID去查询业务信息,比如通过出差单的ID,去查询更多的请假信息(出差人,出差时间,出差天数,出差目的地等)
processInstance.getBusinessKey();
在activiti的act_ru_execution表,字段BUSINESS_KEY就是存放业务KEY的。
挂起、激活流程实例
某些情况可能由于流程变更需要将当前运行的流程暂停而不是直接删除,流程暂停后将不会继续执行。
全部流程实例挂起
操作流程定义为挂起状态,该流程定义下边所有的流程实例全部暂停:
流程定义为挂起状态该流程定义将不允许启动新的流程实例,同时该流程定义下所有的流程实例将全部挂起暂停执
行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@Test public void suspendAllProcessInstance(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionKey("myEvenction") .singleResult(); boolean suspended = processDefinition.isSuspended(); String processDefinitionId = processDefinition.getId(); if (suspended) { repositoryService.activateProcessDefinitionById(processDefinitionId,true,null); System.out.println("流程定义:"+processDefinitionId+",已激活"); }else { repositoryService.suspendProcessDefinitionById(processDefinitionId,true,null); System.out.println("流程定义:"+processDefinitionId+",已挂起"); } }
|
单个流程实例挂起
操作流程实例对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续执行,完成该流程实例的当前任务将报异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
@Test public void suspendSingleProcessInstance(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processDefinitionKey("2501") .singleResult(); boolean suspended = processInstance.isSuspended(); String processDefinitionId = processInstance.getId(); if (suspended) { runtimeService.activateProcessInstanceById(processDefinitionId); System.out.println("流程定义:"+processDefinitionId+",已挂起"); }else { runtimeService.suspendProcessInstanceById(processDefinitionId); System.out.println("流程定义:"+processDefinitionId+",已挂起"); } }
@Test public void completeSingleTask(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .processInstanceId("2501") .taskAssignee("zhangsan") .singleResult(); System.out.println("流程实例id = " + task.getProcessInstanceId()); System.out.println("任务id = " + task.getId()); System.out.println("任务负责人 = " + task.getAssignee()); System.out.println("任务名称 = " + task.getName()); taskService.complete(task.getId());
}
|
二.个人任务
2.1、分配任务负责人
2.1.1、固定分配
在进行业务流程建模时指定固定的任务负责人,如图:
并在properties试图中,填写Assignee项为任务负责人。
2.1.2、表达式分配
由于固定分配方式,任务只管一步一步执行任务,执行到每一个任务将按照 bpmn 的配置去分配任 务负责人。
2.1.2.1、UEL表达式
Activiti 使用 UEL 表达式, UEL 是 java EE6 规范的一部分, UEL(Unified Expression Language)即 统一表达式语言,activiti 支持两个 UEL 表达式: UEL-value 和 UEL-method。
1)UEL-value定义
如图:
assignee这个变量是activiti的一个流程变量,
或者使用这种方式定义:
如图:
user 也是 activiti 的一个流程变量, user.assignee 表示通过调用 user 的 getter 方法获取值。
2)UEL-method方式
如图:
UserBean是Spring容器中的一个Bean,表示调用该Bean的getUserId()方法。
3)UEL-method与UEL-value结合
再比如: ${ldapService.findManagerForEmployee(emp)} ldapService 是 spring 容器的一个 bean,
findManagerForEmployee 是该 bean 的一个方法,emp 是 activiti 流程变量, emp 作为参数传到
ldapService.findManagerForEmployee 方法中。
4)其他
表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。 如下:
${order.price > 100 && order.price < 250}
2.1.2.2、编写代码配置负责人
1)定义任务分配流程变量
如图:
2)设置流程变量
在启动流程实例时设置流程变量,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Test public void assgineeUEL(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); HashMap<String, Object> assgineeMap = new HashMap<>(); assgineeMap.put("assginee0","张三"); assgineeMap.put("assginee1","李经理"); assgineeMap.put("assginee2","王总经理"); assgineeMap.put("assginee3","赵财务"); runtimeService.startProcessInstanceByKey("myEvection",assgineeMap); System.out.println(processEngine.getName());
}
|
执行成功后,在act_ru_variable表中看到刚才map中的数据
2.1.2.3、注意事项
由于使用了表达式分配,必须保证在任务执行过程表达式执行成功,比如:
某个任务使用了表达式${order.price > 100 && order.price < 250},当执行该任务时必须保证 order 在 流程变量中存在,否则 activiti 异常。
2.1.3、监听器分配
可以使用监听器来完成很多Activiti流程的业务。
在本章我们使用监听器的方式来指定负责人,那么在流程设计时就不需要指定assignee。
任务监听器是发生对应的任务相关事件时执行自定义 java 逻辑 或表达式。 任务相当事件包括:
Event的选项包含:
1 2 3 4
| Create:任务创建后触发 Assignment:任务分配后触发 Delete:任务完成后触发 All:所有事件发生都触发
|
定义任务监听类,且类必须实现 org.activiti.engine.delegate.TaskListener 接口
1 2 3 4 5 6 7 8 9
| public class MyTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { if (delegateTask.getName().equals("创建出差申请") && delegateTask.getEventName().equals("create")) { delegateTask.setAssignee("张三"); } } }
|
DelegateTask对象的内容如下
2.1.3.1、注意事项
使用监听器分配方式,按照监听事件去执行监听类的 notify 方法,方法如果不能正常执行也会影响 任务的执行。
2.2、查询任务
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| /** * 查询个人当前待执行的任务 */ @Test public void findPersonalTaskList(){ // 流程定义key String processDefinitionKey = "demo"; // 任务负责人 String assignee = "张三"; // 获取TaskService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = processEngine.getTaskService(); List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey(processDefinitionKey) .includeProcessVariables() .taskAssignee(assignee) .list(); for (Task task : taskList) { System.out.println("---------------------------------------"); System.out.println("流程实例id = " + task.getProcessInstanceId()); System.out.println("任务id = " + task.getId()); System.out.println("任务负责人 = " + task.getAssignee()); System.out.println("任务名称 = " + task.getName());
} }
|
关联businessKey
需求:在 activiti 实际应用时,查询待办任务可能要显示出业务系统的一些相关信息。
比如:查询待审批出差任务列表需要将出差单的日期、 出差天数等信息显示出来。
出差天数等信息在业务系统中存在,而并没有在 activiti 数据库中存在,所以是无法通过 activiti 的 api 查询到出差天数等信息。 实现: 在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
@Test public void findProcessInstance(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = processEngine.getTaskService(); RuntimeService runtimeService = processEngine.getRuntimeService(); Task task = taskService.createTaskQuery() .processDefinitionKey("demo") .taskAssignee("张三") .singleResult(); String processInstanceId = task.getProcessInstanceId(); ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); String businessKey = processInstance.getBusinessKey(); System.out.println("businessKey = " + businessKey); }
|
2.3、办理业务
注意:在实际应用中,完成任务需要校验任务的负责人是否具有该任务的办理权限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
@Test public void completeTask() { String taskId = "20001"; String assignee = "张三"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .taskAssignee(assignee) .taskId(taskId) .singleResult(); if (task != null) { taskService.complete(taskId); System.out.println("完成任务"); } }
|