一、 Activiti概述:
前言:
系统的核心根本上是业务流程,工作流只是协助进行业务流程管理。
在没有使用工作流引擎时,可以采用状态字段来跟踪流程的变化情况,这样不同角色的用户,通过状态字段的取值来决定记录是否显示。针对有权限可以查看的记录,当前用户根据自己的角色来决定审批是否通过的操作。如果通过将状态字段设置一个值,否则设置另一个值。
通过状态字段虽然能做到流程控制,但是当流程发生变更时,所编写的代码也要进行调整。
Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
Activiti工作机制:
Activiti解析流程图文件,将每个流程节点的数据读取保存到数据库中,对于流程图流程的增删,只是在表中多或少一条记录,不需要改变源代码。
二、Activiti核心类与表
2.1 表命名规则和作用
Activiti 的表都以 ACT_ 开头,第二部分是表示表的用途的两个字母标识。用途也和服务的 API 对应。
① ACT_RE :RE表示 repository。这个前缀的表包含了流程定义和流程静态资源。
② ACT_RU:RU表示 runtime。这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。这样运行时表可以一直很小速度很快。
③ ACT_HI:HI表示 history。这些表包含历史数据,比如历史流程实例, 变量,任务。
④ ACT_GE :GE 表示 general。通用数据, 用于不同场景下。
2.2 Activiti数据表介绍
表分类 | 表名 | 解释 |
---|---|---|
一般数据 | ||
ACT_GE_BYTEARRAY | 通用的流程定义和流程资源 | |
ACT_GE_PROPERTY | 系统相关属性 | |
流程历史记录 | ||
ACT_HI_ACTINST | 历史流程实例 | |
ACT_HI_ATTACHMENT | 历史流程附件 | |
ACT_HI_COMMENT | 历史说明性信息 | |
ACT_HI_DETAIL | 历史流程运行中的细节信息 | |
ACT_HI_IDENTITYLINK | 历史流程运行过程中用户信息 | |
ACT_HI_PROCINST | 历史流程实例 | |
ACT_HI_TASKINST | 历史任务实例 | |
ACT_HI_VARINST | 历史的流程运行中的变量信息 | |
流程定义表 | ||
ACT_RE_DEPLOYMENT | 部署单元信息 | |
ACT_RE_MODEL | 模型信息 | |
ACT_RE_PROCDEF | 已部署的流程定义 | |
运行实例表 | ||
ACT_RU_EVENT_SUBSCR | 运行时事件 | |
ACT_RU_EXECUTION | 运行时流程执行实例 | |
ACT_RU_IDENTITYLINK | 运行时用户关系信息,存储任务节点与参与者的相关信息 | |
ACT_RU_JOB | 运行时作业 | |
ACT_RU_TASK | 运行时任务 | |
ACT_RU_VARIABLE | 运行时变量表 |
2.3 Service服务接口
Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口可以就是操作服务对应的数据表。
//通过ProcessEngineConfiguration创建ProcessEngine
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
工作流引擎(ProcessEngine),相当于一个门面接口,通过ProcessEngineConfiguration创建processEngine,通过ProcessEngine创建各个service接口。
Service总览
RepositoryService | activiti的资源管理类 |
RuntimeService | activiti的流程运行管理类 |
TaskService | activiti的任务管理类 |
HistoryService | activiti的历史管理类 |
ManagerService | activiti的引擎管理类 |
三、Activiti核心组件
3.1 流程部署Deployment
使用Activiti提供的api把流程定义内容(.bpmn文件)存储在数据库中,在Activiti执行过程中可以查询定义的内容。
@SpringBootTest
public class Part1_Deployment {@Autowiredprivate RepositoryService repositoryService;//通过bpmn部署流程@Testpublic void initDeploymentBPMN(){String filename="BPMN/Part4_Task_claim.bpmn";// String pngname="BPMN/Part1_Deployment.png";Deployment deployment=repositoryService.createDeployment().addClasspathResource(filename)//.addClasspathResource(pngname)//图片.name("流程部署测试候选人task").deploy();System.out.println(deployment.getName());}//通过ZIP部署流程@Testpublic void initDeploymentZIP() {InputStream fileInputStream = this.getClass().getClassLoader().getResourceAsStream("BPMN/Part1_DeploymentV2.zip");ZipInputStream zip=new ZipInputStream(fileInputStream);Deployment deployment=repositoryService.createDeployment().addZipInputStream(zip).name("流程部署测试zip").deploy();System.out.println(deployment.getName());}//查询流程部署@Testpublic void getDeployments() {List<Deployment> list = repositoryService.createDeploymentQuery().list();for(Deployment dep : list){System.out.println("Id:"+dep.getId());System.out.println("Name:"+dep.getName());System.out.println("DeploymentTime:"+dep.getDeploymentTime());System.out.println("Key:"+dep.getKey());}}
}
影响的表:
act_ge_bytearrayact_re_deploymentact_re_procdef
3.2 流程定义ProcessDefinition
使用流程建模工具定义的.bpmn文件,通过xml定义业务流程。
@SpringBootTest
public class Part2_ProcessDefinition {@Autowiredprivate RepositoryService repositoryService;//查询流程定义@Testpublic void getDefinitions(){List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();for(ProcessDefinition pd : list){System.out.println("------流程定义--------");System.out.println("Name:"+pd.getName());System.out.println("Key:"+pd.getKey());System.out.println("ResourceName:"+pd.getResourceName());System.out.println("DeploymentId:"+pd.getDeploymentId());System.out.println("Version:"+pd.getVersion());}}//删除流程定义@Testpublic void delDefinition(){String pdID="44b15cfe-ce3e-11ea-92a3-dcfb4875e032";repositoryService.deleteDeployment(pdID,true);System.out.println("删除流程定义成功");}
}
3.3 流程实例ProcessInstance
启动一个流程实例表示开始一次业务流程的运行。
@SpringBootTest
public class Part3_ProcessInstance {@Autowiredprivate RuntimeService runtimeService;//初始化流程实例@Testpublic void initProcessInstance(){//1、获取页面表单填报的内容,请假时间,请假事由,String fromData//2、fromData 写入业务表,返回业务表主键ID==businessKey//3、把业务数据与Activiti7流程数据关联ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","bKey002");System.out.println("流程实例ID:"+processInstance.getProcessDefinitionId());}//获取流程实例列表@Testpublic void getProcessInstances(){List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();for(ProcessInstance pi : list){System.out.println("--------流程实例------");System.out.println("ProcessInstanceId:"+pi.getProcessInstanceId());System.out.println("ProcessDefinitionId:"+pi.getProcessDefinitionId());System.out.println("isEnded"+pi.isEnded());System.out.println("isSuspended:"+pi.isSuspended());}}//暂停与激活流程实例@Testpublic void activitieProcessInstance(){// runtimeService.suspendProcessInstanceById("73f0fb9a-ce5b-11ea-bf67-dcfb4875e032");//System.out.println("挂起流程实例");runtimeService.activateProcessInstanceById("73f0fb9a-ce5b-11ea-bf67-dcfb4875e032");System.out.println("激活流程实例");}//删除流程实例@Testpublic void delProcessInstance(){runtimeService.deleteProcessInstance("73f0fb9a-ce5b-11ea-bf67-dcfb4875e032","删着玩");System.out.println("删除流程实例");}
}
影响的表:
act_hi_actinst 已完成的活动信息act_hi_identitylink 参与者信息act_hi_procinst 流程实例act_hi_taskinst 任务实例act_ru_execution 执行表act_ru_identitylink 参与者信息act_ru_task 任务
3.4 任务处理Task
@SpringBootTest
public class Part4_Task {
@Autowired
private TaskService taskService;//任务查询
@Test
public void getTasks(){List<Task> list = taskService.createTaskQuery().list();for(Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("Assignee:"+tk.getAssignee());}
}//查询我的代办任务
@Test
public void getTasksByAssignee(){List<Task> list = taskService.createTaskQuery().taskAssignee("bajie").list();for(Task tk : list){System.out.println("Id:"+tk.getId());System.out.println("Name:"+tk.getName());System.out.println("Assignee:"+tk.getAssignee());}
}//办理任务
@Test
public void completeTask(){taskService.complete("d07d6026-cef8-11ea-a5f7-dcfb4875e032");System.out.println("完成任务");
}//拾取任务
@Test
public void claimTask(){Task task = taskService.createTaskQuery().taskId("1f2a8edf-cefa-11ea-84aa-dcfb4875e032").singleResult();taskService.claim("1f2a8edf-cefa-11ea-84aa-dcfb4875e032","bajie");
}//归还与交办任务
@Test
public void setTaskAssignee(){Task task = taskService.createTaskQuery().taskId("1f2a8edf-cefa-11ea-84aa-dcfb4875e032").singleResult();taskService.setAssignee("1f2a8edf-cefa-11ea-84aa-dcfb4875e032","null");//归还候选任务taskService.setAssignee("1f2a8edf-cefa-11ea-84aa-dcfb4875e032","wukong");//交办
}// 组任务拾取
@Test
public void setTaskAssignee(){设置一些参数,流程定义的key,候选用户String key = "myProcess_1";String candidate_users="zhangsan";Task task = taskService.createTaskQuery().processDefinitionKey(key ).taskCandidateUser(candidate_users)//设置候选用户.singleResult();if(task!=null){taskService.claim(task.getId(),candidate_users);//第一个参数任务ID,第二个参数为具体的候选用户名System.out.println("任务拾取完毕!");
}
}
影响的表:
act_hi_actinstact_hi_identitylinkact_hi_taskinstact_ru_identitylinkact_ru_task
3.5 历史数据HistoryService
@SpringBootTest
public class Part5_HistoricTaskInstance {@Autowiredprivate HistoryService historyService;//根据用户名查询历史记录@Testpublic void HistoricTaskInstanceByUser(){List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceEndTime().asc().taskAssignee("bajie").list();for(HistoricTaskInstance hi : list){System.out.println("Id:"+ hi.getId());System.out.println("ProcessInstanceId:"+ hi.getProcessInstanceId());System.out.println("Name:"+ hi.getName());}}//根据流程实例ID查询历史@Testpublic void HistoricTaskInstanceByPiID(){List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceEndTime().asc().processInstanceId("1f2314cb-cefa-11ea-84aa-dcfb4875e032").list();for(HistoricTaskInstance hi : list){System.out.println("Id:"+ hi.getId());System.out.println("ProcessInstanceId:"+ hi.getProcessInstanceId());System.out.println("Name:"+ hi.getName());}}
}
3.6 流程变量
流程变量就是 Activiti 在管理工作流时根据管理需要而设置的变量。
3.6.1 流程变量的作用域
流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution),默认是流程实例。
- 当一个流程变量的作用域为流程实例时,可以称为 global 变量。global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
- 任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。
3.6.2 流程变量的使用方法 - UEL表达式
- 在属性上使用UEL表达式
可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。
Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配。 - 在连线上使用UEL表达式
可以在连线上设置UEL表达式,决定流程走向。
比如:${price<10000} 。price就是一个流程变量名称,uel表达式结果类型为布尔类型。
如果UEL表达式是true,要决定 流程执行走向。
3.6.3 设置流程变量
在设置流程变量时,可以在启动流程时设置,也可以在任务办理时设置。
public class Part6_UEL {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;//启动流程实例带参数,执行执行人@Testpublic void initProcessInstanceWithArgs() {//流程变量Map<String, Object> variables = new HashMap<String, Object>();variables.put("ZhiXingRen", "wukong");//variables.put("ZhiXingRen2", "aaa");//variables.put("ZhiXingRen3", "wukbbbong");ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_UEL_V1", "bKey002", variables);System.out.println("流程实例ID:" + processInstance.getProcessDefinitionId());}//完成任务带参数,指定流程变量测试@Testpublic void completeTaskWithArgs() {Map<String, Object> variables = new HashMap<String, Object>();variables.put("pay", "101");taskService.complete("a616ea19-d3a7-11ea-9e14-dcfb4875e032",variables);System.out.println("完成任务");}//启动流程实例带参数,使用实体类@Testpublic void initProcessInstanceWithClassArgs() {UEL_POJO uel_pojo = new UEL_POJO();uel_pojo.setZhixingren("bajie");//流程变量Map<String, Object> variables = new HashMap<String, Object>();variables.put("uelpojo", uel_pojo);ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_uelv3", "bKey002", variables);System.out.println("流程实例ID:" + processInstance.getProcessDefinitionId());}//任务完成环节带参数,指定多个候选人@Testpublic void initProcessInstanceWithCandiDateArgs() {Map<String, Object> variables = new HashMap<String, Object>();variables.put("houxuanren", "wukong,tangseng");taskService.complete("4f6c9e23-d3ae-11ea-82ba-dcfb4875e032",variables);System.out.println("完成任务");}//直接指定流程变量@Testpublic void otherArgs() {runtimeService.setVariable("4f6c9e23-d3ae-11ea-82ba-dcfb4875e032","pay","101");
// runtimeService.setVariables();
// taskService.setVariable();
// taskService.setVariables();}//局部变量@Testpublic void otherLocalArgs() {runtimeService.setVariableLocal("4f6c9e23-d3ae-11ea-82ba-dcfb4875e032","pay","101");
// runtimeService.setVariablesLocal();
// taskService.setVariableLocal();
// taskService.setVariablesLocal();}}
四、 BPMN2.0标准
4.1 事件 Event
- 开始事件
开始事件:开始事件都是捕获事件: 最终这些事件都是(一直)等待着,直到对应的触发时机出现。空、定时、错误、信号、消息。 - 结束事件
结束事件都是触发事件。 这是说当流程达到结束事件,会触发一个结果。 - 捕获事件
当流程执行到事件, 它会等待被触发。 捕获事件与触发事件在显示方面是根据内部图表是否被填充来区分的(白色的)。 - 触发事件
当流程执行到事件, 会触发一个事件。 触发事件与捕获事件在显示方面是根据内部图表是否被填充来区分的(被填充为黑色)。 - 信号事件
信号事件会引用一个已命名的信号。信号全局范围的事件(广播语义)。 会发送给所有激活的处理器。 - 消息事件
消息事件会引用一个命名的消息。每个消息都有名称和内容。和信号不同, 消息事件总会直接发送个一个接受者。
4.2 任务 Task
-
用户任务
用户任务用来设置必须由人员完成的工作。 当流程执行到用户任务,会创建一个新任务, 并把这个新任务加入到分配人或群组的任务列表中。 -
Java服务任务
用来调用外部java类。 -
手工任务
用来表示工作需要某人完成,而引擎不需要知道,手工任务是直接通过的活动, 流程到达它之后会自动向下执行。 -
执行监听器
执行监听器可以执行外部Java代码或执行表达式,当流程定义中发生了某个事件。
可以捕获的事件有:流程实例的启动和结束、选中一条连线、节点的开始和结束、网关的开始和结束、中间事件的开始和结束、开始时间结束或结束事件开始。 -
任务监听器:
任务监听器可以在发生对应的任务相关事件时执行自定义Java逻辑或表达式。
任务监听器支持以下属性:
event(必选):任务监听器会被调用的任务类型。 可能的类型为:- create:任务创建并设置所有属性后触发。
- assignment:任务分配给一些人时触发。
当流程到达userTask, assignment事件 会在create事件之前发生。 这样的顺序似乎不自然,但是原因很简单:当获得create时间时, 我们想获得任务的所有属性,包括执行人。 - complete:当任务完成,并尚未从运行数据中删除时触发。
- delete:只在任务删除之前发生。 注意在通过completeTask正常完成时,也会执行。
-
class:必须调用的代理类。 这个类必须实现org.activiti.engine.delegate.TaskListener接口。
public class MyTaskCreateListener implements TaskListener {public void notify(DelegateTask delegateTask) {// Custom logic goes here}
}
可以使用属性注入把流程变量或执行传递给代理类。 注意代理类的实例是在部署时创建的 (和activiti中其他类代理的情况一样),这意味着所有流程实例都会共享同一个实例。
- expression:(无法同时与class属性一起使用): 指定事件发生时执行的表达式。 可以把DelegateTask对象和事件名称(使用task.eventName) 作为参数传递给调用的对象。
<activiti:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
- delegateExpression可以指定一个表达式,解析一个实现了TaskListener接口的对象, 这与服务任务一致。
<activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
4.3 子流程
一个包含其他节点,网关,事件等等的节点。 它自己就是一个流程,同时是更大流程的一部分。
事件子流程: 事件子流程可以添加到流程级别或任意子流程级别。用于触发事件子流程的事件是使用开始事件配置的。
调用活动(子流程): 这个流程定义需要被很多其他流程定义调用的时候。
4.4 网关 Gateway
-
排它网关: 内部是一个“X”图标,用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支。
注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。
不用排他网关也可以实现分支,如:在连线的condition条件上设置分支条件。
在连线设置condition条件的缺点:如果条件都不满足,流程就结束了(是异常结束)。 -
并行网关: 内部是一个“加号”图标。并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
l 分支:
并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
l 汇聚:
所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意:并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
总结:所有分支到达汇聚结点,并行网关执行完成。 -
包含网关: 内部包含一个圆圈图标,包含网关可以看做是排他网关和并行网关的结合体。
和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。
包含网关的功能是基于进入和外出顺序流的:
l 分支:
包含网关可以在外出顺序流上定义条件,结果为true的顺序流会以并行方式继续执行, 为每个顺序流创建一个分支。
l 汇聚:
所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。
总结:在分支时,需要判断条件,符合条件的分支,将会执行,符合条件的分支最终才进行汇聚。 -
基于事件网关: 网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。 与此同时,会为每个外出顺序流创建相对的事件订阅。
4.4 流向 Flow
- 顺序流
- 消息流
五、Activiti7新特性
5.1 ProcessRuntime
@SpringBootTest
public class Part8_ProcessRuntime {@Autowiredprivate ProcessRuntime processRuntime;@Autowiredprivate SecurityUtil securityUtil;//获取流程实例@Testpublic void getProcessInstance() {securityUtil.logInAs("bajie");Page<ProcessInstance> processInstancePage = processRuntime.processInstances(Pageable.of(0,100));System.out.println("流程实例数量:"+processInstancePage.getTotalItems());List<ProcessInstance> list = processInstancePage.getContent();for(ProcessInstance pi : list){System.out.println("-----------------------");System.out.println("getId:" + pi.getId());System.out.println("getName:" + pi.getName());System.out.println("getStartDate:" + pi.getStartDate());System.out.println("getStatus:" + pi.getStatus());System.out.println("getProcessDefinitionId:" + pi.getProcessDefinitionId());System.out.println("getProcessDefinitionKey:" + pi.getProcessDefinitionKey());}}//启动流程实例@Testpublic void startProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("myProcess_ProcessRuntime").withName("第一个流程实例名称")//.withVariable("","").withBusinessKey("自定义bKey").build());}//删除流程实例@Testpublic void delProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.delete(ProcessPayloadBuilder.delete().withProcessInstanceId("6fcecbdb-d3e0-11ea-a6c9-dcfb4875e032").build());}//挂起流程实例@Testpublic void suspendProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.suspend(ProcessPayloadBuilder.suspend().withProcessInstanceId("1f2314cb-cefa-11ea-84aa-dcfb4875e032").build());}//激活流程实例@Testpublic void resumeProcessInstance() {securityUtil.logInAs("bajie");ProcessInstance processInstance = processRuntime.resume(ProcessPayloadBuilder.resume().withProcessInstanceId("1f2314cb-cefa-11ea-84aa-dcfb4875e032").build());}//流程实例参数@Testpublic void getVariables() {securityUtil.logInAs("bajie");List<VariableInstance> list = processRuntime.variables(ProcessPayloadBuilder.variables().withProcessInstanceId("2b2d3990-d3ca-11ea-ae96-dcfb4875e032").build());for(VariableInstance vi : list){System.out.println("-------------------");System.out.println("getName:" + vi.getName());System.out.println("getValue:" + vi.getValue());System.out.println("getTaskId:" + vi.getTaskId());System.out.println("getProcessInstanceId:" + vi.getProcessInstanceId());}}
}
5.2 TaskRuntime
@SpringBootTest
public class Part9_TaskRuntime {@Autowiredprivate SecurityUtil securityUtil;@Autowiredprivate TaskRuntime taskRuntime;//获取当前登录用户任务@Testpublic void getTasks() {securityUtil.logInAs("wukong");Page<Task> tasks = taskRuntime.tasks(Pageable.of(0,100));List<Task> list=tasks.getContent();for(Task tk : list){System.out.println("-------------------");System.out.println("getId:"+ tk.getId());System.out.println("getName:"+ tk.getName());System.out.println("getStatus:"+ tk.getStatus());System.out.println("getCreatedDate:"+ tk.getCreatedDate());if(tk.getAssignee() == null){//候选人为当前登录用户,null的时候需要前端拾取System.out.println("Assignee:待拾取任务");}else{System.out.println("Assignee:"+ tk.getAssignee());}}}//完成任务@Testpublic void completeTask() {securityUtil.logInAs("wukong");Task task = taskRuntime.task("db9c5f80-d3ae-11ea-99e8-dcfb4875e032");if(task.getAssignee() == null){taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());}taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());System.out.println("任务执行完成");}
}
六、常见业务流程设计:
6.1 通知与默认审核人 - 任务监听器
任务监听器拿到的数据和任务相关
场景:项目启动后,流转到任意环节都需要给相应的执行人发送短信提醒。一般用来通知或动态修改任务的执行人/候选组使用。
public class TkListener1 implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {System.out.println("执行人:"+delegateTask.getAssignee());//根据用户名查询用户电话并调用发送短信接口delegateTask.setVariable("delegateAssignee",delegateTask.getAssignee());}
}
public class TkListener2 implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {System.out.println("执行人2:"+delegateTask.getVariable("delegateAssignee"));//根据执行人username获取组织机构代码,加工后得到领导是wukongdelegateTask.setAssignee("wukong");//delegateTask.addCandidateGroup("wukong");}
}
6.2 记录环节执行时间 - 执行监听器
执行监听器常用于统计执行时长。
执行监听器拿到的数据和流程相关,
执行监听器作用:
存储读取变量
处理业务信息
public class PiListener implements ExecutionListener {@Autowiredprivate Expression sendType;@Overridepublic void notify(DelegateExecution execution) {System.out.println(execution.getEventName());System.out.println(execution.getProcessDefinitionId());if("start".equals(execution.getEventName())){//记录节点开始时间}else if("end".equals(execution.getEventName())){//记录节点结束时间}System.out.println("sendType:"+sendType.getValue(execution).toString());}
}
针对某些环节短信通知、某些环节邮件提醒当前执行人
6.3 超时提醒 - 定时事件
常用场景:
指定日期开启流程实例
24小时任务未办理短信提醒
3天未审核则主管领导介入
定时事件类型:
Time date:日期,什么时间触发
Time duration:持续延时多长时间后触发
Time cycle:循环,循环规则(监控报警,循环推送)
持续例子:
P1DT1M - 一天一分钟执行一次
P1W - 一周执行一次
PT1H - 一小时执行一次
PT10S - 十秒执行一次
说明:
P - 开始标记
1Y - 一年
2M -两个月
10D - 十天
T - 时间和日期分的割标记
2H - 两个小时
30M - 三十分钟
15S - 十五秒钟
循环例子:
循环3次/开始循环时间/每次间隔:R3/2021-07-30T19:12:00/PT1M(13【开始时间+间隔】分后开始)
执行2次,1分钟执行一次:R2/PT1M
无限循环/时间间隔/结束时间:R/RT1M/2021-01-01
变量无限循环:R/PT1H/${EndTime}
事件中间事件:
第三个任务:3天未审核,任务不会向下执行,自动执行到他主管领导这,可以使用边界事件。
第二个任务:非中断边界事件,与边界事件的区别是本身任务不会流转,八戒继续办理,他的主管领导也会接到一个任务,还会保存在当前的任务,典型场景24小时未处理,短信提醒,任务还在八戒那里,下面的是如果三天未审核,任务由主管来办理,八戒的任务被取消掉了,而非中断是八戒和主管领导都有任务。
第一种:
定时任务创建后会在act_ru_timer_job表创建一条数据,运行后删除
第三种(中间事件):
十秒后只有八戒2的任务
第二种:中间事件非中断
十秒后有八戒1和八戒2的任务
6.4 预案启动多部门协调 - 信号事件
6.5 提交后取回任务 - 消息事件
6.6 付款失败重试 - 错误事件
6.7 手工任务、服务任务
6.8 财务审核每次都一样 - 调用子流程
6.9 会签与多小组协作 - 多实例任务
七、扩展
7.1 动态表单
7.1.1 动态表单渲染
7.1.2 动态表单提交入库
7.1.3 动态表单UEL表达式
7.1.3 动态表单读取历史数据
7.2 高亮历史流程
7.3 自定义用户控件
参考文献:
Activiti7精讲&Java通用型工作流开发实战
工作流引擎 Activiti 万字详细进阶
activiti7笔记
史上最全的工作流引擎 Activiti 学习教程(值得收藏)
activiti BPMN—顺序流、网关、任务、子流程-多极客编程