Activiti-相关API

本文介绍与Activiti工作流具体操作相关的API。

第一部分 几个Service相关的API

1.流程定义API--RepostoryService API

activiti.cfg.xml是一个spring依赖注入的xml文件,其具体内容可以参考
Spring集成activiti
测试类

public class ProcessDefinitionTest {// activitii.cfg.xml已经在classpath目录下,直接加载默认的流程引擎即可private ProcessEngine processEngine = processEngines.getDefaultProcessEngine();
}

1.1 部署流程定义

和流程部署相关的表

表名 中文名 解释
act_re_deployment 部署对象信息表 存放流程定义的显示名称和部署时间,部署一次增加一条记录,注意:当流程定义的key相同的情况下,使用的是版本升级
act_re_procdef 流程定义表 存放流程定义的属性信息,部署每一个新的流程定义都会在这张表中增加一条记录
act_ge_bytearray 资源文件表 存储流程定义相关的部署信息,即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于bpmn规则文件的,一条是图片的(如果部署时只指定了bpmn一个文件,activiti会在部署时解析bpmn文件内容自动生成流程图),两个文件不是很大,都是以二进制形式存储在数据库中。
act_ge_property 主键生成策略表 -

从classpath部署流程

@Test
public void deploymentProcessDefinition_classpath() {Deployment deployment = processEngine.getRepositoryService() // 获取流程定义和部署对象相关的Service.createDeployment() // 创建一个部署对象.name("helloworld入门程序") //添加部署名称.addClasspathResource("diagrams/helloworld.bpmn") // 从classpath的资源中加载,一次只能加载一个文件.addClasspathResource("diagrams/helloworld.png") // 从classpath的资源中加载,一次只能加载一个文件.deploy(); // 完成部署System.out.println(deployment.getId()); // 1System.out.println(deployment.getName()); // helloworld入门程序
}

从zip部署流程

// 从zip部署流程
// 打包helloworld文件,包含helloworld.bpmn, helloworld.png, 存放到classpath目录下
@Test
public void deploymentProcessDefinition_zip() {InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrams/helloworld.zip")ZipInputStream zipInputStream = new ZipInputStream(in);// 1.部署流程定义-- activitii.cfg.xml已经在classpath目录下,直接加载默认的流程引擎即可ProcessEngine processEngine = processEngines.getDefaultProcessEngine();Deployment deployment = processEngine.getRepositoryService() // 获取流程定义和部署对象相关的Service.createDeployment() // 创建一个部署对象.name("helloworld入门程序") //添加部署名称.addZipInputStream(zipInputStream) // 指定zip格式的文件完成部署.deploy(); // 完成部署System.out.println(deployment.getId()); // 1System.out.println(deployment.getName()); // helloworld入门程序
}

inputStream方式部署

// 通过addInputStream 方式部署流程定义实现--此处从绝对路径,即classpath下查找bpmn和png资源
@Test
public void deploymentProcessDefinition_inputstream() {InputStream bmpnInputstream = this.getClass().getResourceAsStream("/diagrams/processVariables.bpmn");InputStream pngInputstream = this.getClass().getResourceAsStream("/diagrams/processVariables.bpmn");Deployment deployment = processEngine.getRepositoryService().createDeployment().name("流程定义").addInputStream("processVariables.bpmn", bmpnInputstream).addInputStream("processVariables.png", pngInputstream).deploy();System.out.println("DEPLOYMENT_ID: " + deployment.getId());System.out.println("DEPLOYMENT_NAME: " + deployment.getName());
}

几种获取inputStrem的方式及其异同
当testVariables.bpmn文件存在于 /src/main/java/processDefine/helloworld.bpmn目录下且下面的代码在helloworld.bpmn的同目录下的类中时,有以下几种获取InputStream的方式


// 从类加载器中加载input stream
InputStream is = this.getClass().getClassLoader().getResourceAsStream("processDefine/helloworld.bpmn") // 类加载器// 从当前类的包下找
InputStream is = this.getClass().getResourceAsStream("helloworld.bpmn")// 从classpath下找
InputStream is = this.getClass().getResourceAsStream("/processDefine/helloworld.bpmn")

1.2 查询流程定义

查询流程定义

// 查询流程定义
@Test
public void findProcessDefinition() {List<ProcessDefinition> list = processEngine.getRepositoryService() // 与部署和流程定义相关的service.createProcessDefinitionQuery() // 创建一个流程定义查询// 指定查询条件,where条件// .deploymentId(deploymentId) // 使用部署对象ID查询// .processDefinitionId(processDefinitionId) // 使用流程定义Id查询// .processDefinitionKey(processDefinitionKey) // 使用流程定义的key查询// .processDefinitionNameLike(processDefinitionNameLike) // 使用流程定义的名称模糊查询// 排序// .orderByProcessDefinitionVersion().asc() // 按照版本的升序排序.orderByProcessDefinitionName().desc() // 按照流程定义的名称降序排序// 返回的结果集.list(); // 返回一个集合列表,封装流程定义// .singleResult(); // 返回唯一结果集// .count(); // 返回结果集数量// .listPage(firstResult, maxResults); // 分页查询if (list != null && list.size() > 0) {for (ProcessDefinition pd:list) {System.out.println("流程定义Id: " + pd.getId()); // 流程定义的key + 版本 + 随机生成树System.out.println("流程定义名称: " + pd.getName()); // 对应helloworld.bpmn文件中的name属性值System.out.println("流程定义的key: " + pd.getKey()); // 对应helloworld.bpmn文件中的id属性值System.out.println("流程定义的版本: " + pd.getVersion()); // 流程定义key相同时,版本升级System.out.println("资源名称bpmn文件: " + pd.getResourceName());System.out.println("资源名称png文件: " + pd.getDaigramResourceName());System.out.println("部署对象ID: " + pd.DeploymentId());}}
}

查询所有最新的流程定义

// 查询所有最新版本的流程定义
@Test
public void findLastVersionProcessDefinition() {List<ProcessDefinition> list = ProcessEngine processEngine = processEngines.getDefaultProcessEngine().createProcessDefinitionQuery().orderByProcessDefinitionVersion().asc() // 使用流程定义的升序排列.list();Map<String, ProcessDefinition> map = new LinkedHashMap<>();if (list != null && list.size() > 0) {for (ProcessDefinition pd : list) {map.put(pd.getKey(), pd);}}List<PorcessDefinition> pdList = new ArrayList<>(map.values());if (pdList != null && pdList.size() > 0) {for (ProcessDefinition pd:pdList) {System.out.println("流程定义Id: " + pd.getId()); // 流程定义的key + 版本 + 随机生成树System.out.println("流程定义名称: " + pd.getName()); // 对应helloworld.bpmn文件中的name属性值System.out.println("流程定义的key: " + pd.getKey()); // 对应helloworld.bpmn文件中的id属性值System.out.println("流程定义的版本: " + pd.getVersion()); // 流程定义key相同时,版本升级System.out.println("资源名称bpmn文件: " + pd.getResourceName());System.out.println("资源名称png文件: " + pd.getDaigramResourceName());System.out.println("部署对象ID: " + pd.DeploymentId());}}
}

小结

  1. 流程定义和部署对象相关的service都是RepositoryService
  2. 创建流程定义查询对象,可以在ProcessDefinitionQuery上设置查询的相关参数
  3. 调用ProcessDefinitionQuery对象的list方法,执行查询,获得符合条件的流程定义列表
  4. 运行结果可以看出,
    Key和Name的值为:bpmn文件process节点的id和name的属性值
    <process id="LeaveFlow" name="请假流程" isExecutable="true">
  5. key属性被用来区别不同的流程定义
  6. 带有特定key的流程定义第一次部署时,版本为1.之后每次部署都会在当前最高版本号上加1
  7. Id的值的生成规则为: {processDefinitionKey}:{processDefinitionVersion}:{generated-id},这里的generated-id是一个自动生成的唯一的数字
  8. 重复部署一次,deploymentId的值以一定的形式变化,规则act_ge_property表生成

1.3 删除流程定义

删除流程定义--根据部署ID删除

// 删除流程定义
@Test
public void deleteProcessDefinition() {String deploymentId = "1";// 不带级联的删除// 只能删除没有启动的流程,如果流程启动,就会抛出异常// processEngine.getRepositoryService()//          .deleteDeployment(deploymentId);// 级联删除//  不管流程是否启动,都可以删除processEngine.getRepositoryService().deleteDeployment(deploymentId, true);System.out.println("删除成功!");
}

删除流程定义--删除key相同的所有不同版本的流程定义

// 删除流程定义(删除key相同的所有不同版本的流程定义)
@Test
public void deleteProcessDefinitionByKey() {String processDefinitionKey = "helloworld";// 先使用流程定义的key查询流程定义,查询出所有的版本List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).list();// 遍历删除 if (list != null && list.size() > 0) {for (ProcessDefinition pd:list) {// 获取部署IDString deploymentId = pd.getDeploymentId();processEngine.getRepositoryService().deleteDeployment(deploymentId, true);}}
}

小结

  1. 因为删除的是流程定义,而流程定义的部署是属于仓库服务的,所以应该先得到RepositoryService
  2. 如果该流程定义下没有正在运行的流程,则可以用普通删除。如果是有关联的信息,用级联删除。项目开发中使用级联删除的情况比较多,删除操作一般只开放给超级管理员使用。

1.4 查看流程图

// 查看流程图
@Test
public void viewPic() throws IOException {// 将生成的图片放置到文件夹下String deploymentId = "801"; // act_ge_bytearray 表中的deployment_id字段// 获取图片资源名称List<String> list = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId); // 两个值,一个是bpmn文件名,一个是png文件名String resourceName = "";if (list != null && list.size() > 0) {for (String name : list) {if (name.indexOf(".png") >= 0) {resourceName = name;}}}InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);// 将图片生成到D盘的目录下File file = new File("D://" + resourceName);// 将输入流的图片写到D盘下FileUtils.copyInputStreamToFile(in, file);
}

2.运行时RuntimeService API

流程实例对象:ProcessInstance代表流程定义的实例。如范冰冰请了一天的假,她就必须发出一个流程实例的申请。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。流程实例就表示一个流程从开始到结束的最大的流程分支,即一个流程中流程实例只有一个。

执行对象:Activiti 用这个对象去描述流程执行的每一个节点。在没有并发的情况下,Execution就是同 ProcessInstance。流程按照流程定义的规则执行一次的过程,就可以表示执行对象Execution。

流程图依旧接着第一部分的流程图(helloworld.bpmn)。
在做后面的启动流程实例和完成任务的过程中,完成以下查询。

select * from act_ru_execution   # 正在执行的执行对象列表
select * from act_hi_procinst    # 流程实例的历史列表,启动一个流程,产生一个流程实例历史记录,直到流程结束也只产生这一笔 记录
select * from act_ru_task        # 正在执行的任务表(只有节点是UserTask的时候,该表中才会有数据)
select * from act_hi_taskinst    # 任务历史表(只有节点是UserTask的时候,该表中才会有数据)
select * from act_hi_actinst     # 所有活动节点的历史表(包括开始节点,任务节点,结束节点等)

测试类

public class ProcessInstanceTest {// activitii.cfg.xml已经在classpath目录下,直接加载默认的流程引擎即可private ProcessEngine processEngine = processEngines.getDefaultProcessEngine();
}

2.1 启动流程实例

// 启动流程实例
// 默认启动最新版本的流程
@Test
public void startProcessInstance() {String processEngine = "helloworld";ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey) // 使用流程定义的key启动实例,key对应helloworld.bpmn中Propertes流程的Id属性值System.out.println(pi.getId()); // 流程实例Id   101System.out.println(pi.getProcessDefinitionId()); // 流程定义Id:   1:4
}

做一下如下查询

select * from act_ru_execution
select * from act_hi_procinst

会发现,流程实例和执行对象相同。

2.2 查询流程状态

查询运行时是否存在流程实例来判断流程是正在进行中还是已结束。

// 查询流程状态(判断流程正在执行,还是结束)
@Test
public void isProcessEnd() {String processInstanceId = "1001";ProcessInstance pi = processEngine.getRuntimeService() // 正在执行的流程实例和执行对象.createProcessInstanceQuery() // 创建流程实例查询.processInstanceId(processInstanceId) // 根据流程实例ID查询.singleResult();if (pi == null) {System.out.println("流程已结束");} else {System.out.println("流程没有结束");}
}

3.TaskService API

流程图接着第一部分的流程图(helloworld.bpmn)。
测试类接着第二部分的测试类。

3.1 查询指定人(assignee)的用户的用户任务

// 查询当前人的个人任务
@Test
public void findMyPersonTask() {String assignee = "张三";List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的service;.createTaskQuery()// 查询条件,where部分.taskAssignee(assignee) // 指定个人任务查询,指定办理人// .taskCandidateUser(candidateUser) // 组任务的办理人查询// .processDefinitionId(processDefinitionId) // 使用流程定义ID查询// .processInstanceId(processInstanceId) // 使用流程实例ID查询// .executionId(executionId) // 使用执行对象ID查询// 排序// .orderByTaskDescription().asc() // // .orderByTaskAssignee().asc() // 按处理// .orderByTaskCreateTime().asc() // 按照创建时间排序// 返回结果集// .singleResult(); // 范围唯一结果集// .count(); // 返回结果集的数量// .listPage(first, max); // 分页查询.list(); // 返回列表if (list != null && list.size() > 0) {for (Task task: list) {System.out.println("任务Id:" + task.getId());System.out.println("任务名称:" + task.getName());System.out.println("任务创建时间:" + task.getCreateTime());System.out.println("任务办理人:" + task.getAssignee());System.out.println("流程实例ID:" + task.getProcessInstanceId());System.out.println("执行对象ID:" + task.getExecutionId());System.out.println("流程定义ID:" + task.getProcessDefinitionId());System.out.println("######################################################");}}
}

3.2 完成任务

// 完成我的任务
@Test
public void completeMyPersonTask() {String taskId = "104";processEngine.getTaskService().complete(taskId);System.out.println("完成任务,任务ID: " + taskId);
}

4.HistoryService API

测试类

public class HistoryQueryTest {ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
}

4.1 查询指定某人的历史任务

// 根据历史任务办理人查询历史任务
@Test
public void findHistoryTask() {String taskAssignee = "张三";List<HistoricTaskInstance> list = processEngine.getHistoryService() // 与历史数据(历史表)相关的service.createHistoricTaskInstanceQuery().taskAssignee(taskAssignee) // 指定历史任务的办理人.list();if (list != null && list.size() > 0 ) {for (HistoricTaskInstance hti: list) {System.out.println(hti.getId() + "   " + hti.getName() + "   " + hti.getProcessInstanceId() + "   " + hti.getStartTime() + "   " + hti.getEndTime() + "   " + hti.getDurationInMillis());System.out.println("####################################################");}}
}

4.2 流程执行完后,可以根据流程实例ID查询流程历史

流程执行完后,历史流程实例的endTime,durationInMillis将会有数据,在执行完之前,endTime,durationInMillis为空。

// 根据流程实例ID查询历史流程实例
@Test
public void findHistoryProcessInstance() {String processInstanceId = "1001";HistoricProcessInstance hpi = processEngine.getHistoryService().createHistoricProcessInstanceQuery() // 历史流程实例查询.processInstanceId(processInstanceId) // 使用流程实例ID查询.singleResult();System.out.println(hpi.getId() + ", " + hpi.getProcessDefinitionId() + ", " + hpi.getStartTime() + ", " + hpi.getEndTime() + ", " + hpi.getDurationInMillis());
}

4.3 根据流程实例ID查询历史流程实例

@Test
public void findHistoryProcessInstanceHistory() {String processInstanceId = "";HistoryProcessInstance hpi = processEngine.getHistoryService().createHisotricProcessInstanceQuery().processInstanceId(processInstanceId().singleResult();
}

4.4 根据流程实例ID查询历史活动

@Test
public void findHisotryActiviti() {String processInstanceId=""List<HistoricActivityInstance> list = processEngine.getHistoryService();.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId()).orderByHistoricActivityInstanceStartTime().asc().list();// 遍历并打印:活动类型,开始时间,结束时间,活动耗时// sysout: list-> hai: activityType, startTime, endTime, durationInMillis
}

4.5 根据流程实例ID查询历史任务

@Test
public void findHistoricTask() {String processInstanceId=""List<HistoricTaskInstance> list = processEngine.getHistoryService();.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByHistoricTaskInstanceStartTime().asc().list();// 遍历并打印:任务ID,任务名称,流程实例ID,开始时间,结束时间,任务完成耗时// sysout: list-> hai: id, name, processInstanceId, startTime, endTime, durationInMillis
}

4.6 根据流程实例ID查询历史变量

@Test
public void findHistoryVariables() {String processInstanceId=""List<HistoricVariableInstance> list = processEngine.getHistoryService();.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).orderByHistoricTaskInstanceStartTime().asc().list();// 遍历并打印:流程变量ID,流程实例ID,变量名,变量类型名,变量值// sysout: list-> hai: id, processInstanceId, variableName, variableTypeName, value
}

第二部分 核心知识点

1.流程变量

流程变量的作用:

  1. 用来传递业务参数
  2. 指定连线完成任务(同意和拒绝)
  3. 动态指定任务的办理人

和流程变量相关的表:

column 中文名
act_ru_variable 正在执行的流程变量表
act_hi_varinst 历史的流程变量表

测试类定义如下

public class ProcessVariablesTest {ProcessEngine processEngine = ProccessEngines.getDefaultProcessEngine();// 部署流程定义,此处建议采用inputStream方式部署,参考流程定义部分代码// 启动流程代码}

processVariables.bpmn

流程变量支持的数据类型

string
long
double
serializable
binary
date
short
integer

1.1 在任务中设置流程变量--taskService

// 设置流程变量
@Test
public void setVariables() {// 与任务相关的,正在执行的TaskService taskService = processEngine.getTaskService();String taskId = "1504";// 设置流程变量,使用基本数据类型taskService.setVariableLocal(taskId, "请假天数", 3); // 与任务ID绑定,当任务流转到下一个任务时,将不能再查询到这个参数taskService.setVariable(taskId, "请假日期", new Date());taskService.setVariable(taskId, "请假原因", "回家探亲");System.out.println("设置流程变量成功!");
}

1.2 获取流程变量

// 获取流程变量
@Test
public void getVariables() {// 与任务相关的,正在执行的TaskService taskService = processEngine.getTaskService();String taskId = "1504";Integer days = (Integer)taskService.getVariable(taskId, "请假天数");Date date = (Date)taskService.getVariable(taskId, "请假日期");String reason = (String)taskService.getVariable(taskId, "请假原因");System.out.println("请假天数:" + days + ", 请假日期: " + date + ", 请假原因: " + reason);
}

1.3 其它几种设置流程变量的方式

用HashMap同时设置多个参数--runtimeService和taskService均可设置流程变量

runtimeService.setVariables(executionId, variables) // variables: Map集合,表示使用执行对象ID和Map集合设置流程变量的名称,map集合的value就是变量的值taskService.setVariable(taskId, variab****les) // variables: Map集合

启动流程实例时设置流程变量

runtimeService.startProcessInstanceByKey(processDefinitionKey, variables) // 表示启动流程实例的时候可以设置map参数

完成任务时设置流程变量

taskService.complete(taskId, variables) // 表示完成实例的时候可以设置map参数

设置流程变量仅在当前任务有效--Local

runtimeService.setVariableLocal(executionId, variableName, value)
taskService.setVariableLocal(executionId, variableName, value)

1.4 其它几种获取流程变量的方式

根据variableName获取值--runtimeService和taskService均可获取流程变量

runtimeService.getVariable(executionId, variableName) // 使用执行对象Id,和流程变量的名称,获取流程变量的值
taskService.getVariable(taskId, variableName) // 使用任务Id,和流程变量的名称,获取流程变量的值

根据执行对象ID或taskId获取键值的HashMap集合(全部的)

runtimeService.getVariables(executionId) // 使用执行对象Id,将流程变量放置到Map集合中
taskService.getVariables(taskId) // 使用任务Id,将流程变量放置到Map集合中

传入指定的variableNames(HashMap)获取键值的HashMap集合(指定name的)

runtimeService.getVariables(executionId, variableNames) // 使用执行对象ID获取流程变量的值,通过设置流程变量的名称存放到一个集合中,获取指定流程变量名称的流程变量值的集合
taskService.getVariables(taskId, variableNames) // 使用任务Id获取流程变量的值,通过设置流程变量的名称存放到一个集合中,获取指定流程变量名称的流程变量值的集合

1.5 汇总

// 模拟设置和获取流程变量的场景
@Test
public void setAndGetVariables() {// 与流程实例,执行对象相关的RuntimeService runtimeService = processEngine.getRuntimeService()// 与任务相关的,正在执行的TaskService taskService = processEngine.getTaskService();// runtimeService.setVariable(executionId, variableName, value) // 表示使用执行对象ID,和流程变量的名称,设置流程变量的值(一次只能设置一个值)// runtimeService.setVariables(executionId, variables) // variables: Map集合,表示使用执行对象ID和Map集合设置流程变量的名称,map集合的value就是变量的值// taskService.setVariable(taskId, variableName, value) // taskService.setVariable(taskId, variables) // variables: Map集合// runtimeService.startProcessInstanceByKey(processDefinitionKey, variables) // 表示启动流程实例的时候可以设置map参数// taskService.complete(taskId, variables) // 表示完成实例的时候可以设置map参数// 获取流程变量// runtimeService.getVariable(executionId, variableName) // 使用执行对象Id,和流程变量的名称,获取流程变量的值// runtimeService.getVariables(executionId) // 使用执行对象Id,将流程变量放置到Map集合中// runtimeService.getVariables(executionId, variableNames) // 使用执行对象ID获取流程变量的值,通过设置流程变量的名称存放到一个集合中,获取指定流程变量名称的流程变量值的集合// taskService.getVariable(taskId, variableName) // 使用任务Id,和流程变量的名称,获取流程变量的值// taskService.getVariables(taskId) // 使用任务Id,将流程变量放置到Map集合中// taskService.getVariables(taskId, variableNames) // 使用任务Id获取流程变量的值,通过设置流程变量的名称存放到一个集合中,获取指定流程变量名称的流程变量值的集合
}

1.6 传参-javabean

定义Person类

public class Person implements Serializable{private Integer id;private String name;// getter ... setter ...
}

设置流程变量

Person p = new Person();
p.setId(10);
p.setName("翠花");
taskService.setVariable(taskId, "人员信息", p);

值得注意的是,serializable类型的参数值是二进制类型的,会存放到 act_ge_bytearray 表中,当一个javabean(实现序列化)放置到流程变量中,要求javabean的属性不能再发生变化,如果发生变化,再获取的时候,抛出异常(假定你在Person中再加一个education属性,那么你再获取先前set的值,则取不出来了),要解决这个问题,需要给Person类添加序列化ID。

publlic static final long serialVersionUID=253678908763728903L;

获取person

@Test
public void getVariables() {Person p = (Person) taskService.getVariable(taskId, "人员信息");System.out.println("person.id = " + p.getId() + ", person.name = " +  p.getName());
}

1.7 查询历史的流程变量

@Test
public void findHistoryProcessVariables() {List<HistoricVariableInstancce> list = processEngine.getHistoryService().createHistoricVariableInstanceQuery().variableName("请假天数").list();if(list != null and list.size > 0) {for (HistoricVariableInstancce hvi: list) {System.out.println("id = " + hvi.getId() + ", processInstanceId = " + hvi.getProcessInstanceId()+ ", variableName = " + hvi.getVariableName()+ ", variableValue = " + hvi.getVariableValue());System.out.println("######################");}}
}

2.连线

流程图

测试类

public class SequenceFlowTest {ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 部署流程// 启动流程实例
}

参数传入“不重要”,走连线a

// 完成个人任务
@Test
public void completePersonProcess() {String taskId = "";Map<String, Object> variables = new HashMap<>();variables.put("message", "不重要");proccessEngine.getTaskService.complete(taskId, variables);System.out.println("taskId = " + taskId);
}

参数传入“重要”,走连线b

// 完成个人任务2 -- 会执行到总经理再结束
@Test
public void completePersonProcess() {String taskId = "";Map<String, Object> variables = new HashMap<>();variables.put("message", "重要");proccessEngine.getTaskService.complete(taskId, variables);System.out.println("taskId = " + taskId);
}

3.排他网关

流程图

测试类

public class ExclusiveGateWayTest {ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 部署流程// 启动流程实例
}

完成任务--money参数分别传入800,1200,300

    // 完成任务completeMyPersonTask() {String taskId = "";Map<String, Object> variables = new HashMap<>();variables.put("money", 800); // 800 -> 部门经理射频// 1200 -> 总经理// 300  -> 财务proccessEngine.getTaskService.complete(taskId, variables);sysout: taskId}

小结

  1. 一个排他网关对应一个以上的顺序流
  2. 由排他网关流出的顺序流都有个conditionExpression 元素,在内部维护返回boolean类型的决策结果。
  3. 决策网关只会返回一条结果,当流程执行到排他网关时,流程引擎会自动检索网关出口,从上到下检索如果发现第一条决策结果为true或者没有设置条件的(默认为成立),则流出。
  4. 如果没有任何一个出口符合条件,则抛出异常。
  5. 如果流程变量,设置连线的条件,并按照连线的条件执行工作流,如果没有符合的条件,则以默认的连线(Default Flow)离开。

4.并行网关

并行网关可以表示分支和聚合 -- 流程实例和执行对象将不再是同一个
流程图

测试类

public class ParallelGateWayTest {ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 部署流程// 启动流程实例
}

依次完成任务

// 完成任务
@Test
public void completeMyPersonTask() {String taskId = "";proccessEngine.getTaskService().complete(taskId);sysout: taskId
}

流程执行过程

当启动流程实例时

select * from act_ru_execution
3条记录,一个流程实例,两个执行对象act_hi_procinst  流程实例的历史表
1条记录act_ru_task    正在执行的任务
2个,付款和发货act_hi_taskinst   历史任务表
2个,付款和发货act_hi_actinst  历史活动
4个,付款,发货,开始和并行网关

当所有任务完成后

select * from act_ru_execution
0act_hi_procinst  流程实例的历史表
1条记录act_ru_task    正在执行的任务
0act_hi_taskinst   历史任务表
4个,付款,发货,收货,收款act_hi_actinst  历史活动
9个,付款,发货,收货,收款,开始,结束,并行网关1(分支),并行网关2-商家(聚合),并行网关2-厂家(聚合)

小结

  1. 一个流程中流程实例只有1个,执行对象有多个
  2. 并行网关的功能是基于进入和外出的顺序流的:
    分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支
    聚合:所有到达并行网关,在此等待的进入分支,直到所有进入顺序流的分支都到达以后,流程就会通过汇聚网关
  3. 并行网关的进入和外出都是使用相同节点标识
  4. 如果同一个并行网关有多个进入和多个外出顺序流,它就同时具有分支和汇聚功能。这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
  5. 并行网关并不会解析条件。即使顺序流中定义了条件,也会被忽略。
  6. 并行网关不需要是“平衡的”(比如,对应并行网关的进入和外出节点数目不一定相等)

5.开始活动

流程图

开始活动节点流程图就是从开始节点直接到结束节点,基本上没多大意义。

代码

小结

  1. 结束节点没有出口
  2. 其它节点有一个或多个出口
    如果有一个出口,则代表是一个单线流程
    如果有多个出口,则代表是开启并发流程

6.接收任务活动

概念

接收任务是一个简单任务,它会等待对应消息的到达。当前,官方只实现了这个任务的java语义。当流程达到接收任务,流程状态会保存到数据库中。

在任务创建后,意味着流程会进入等待状态直到引擎接收了一个特定的消息,这会触发流程穿过接收任务继续执行。

流程图

代码

// 启动流程实例推断流程是否结束 + 查询历史
@Test
public void startProcessInstance() {// 流程定义的keyString processDefinitionKey = "receiveTask";ProcessInstance pi = processEngine.getRuntimeService() //  与正在执行的流程实例和执行对象相关的 Service.startProcessInstanceByKey(processDefinitionKey); // 使用 流程定义的key启动流程实例,key对应helloworld.bpmn文件中id属性的值System.out.println("流程实例ID:" + pi.getId());System.out.println("流程实例ID:" + pi.getId());// 查询执行对象IDExecution execution1 = processEngine.getRuntimeService().createExecutionQuery() // 创建执行对象查询.processInstanceId(pi.getId()) // 使用流程实例ID查询.activityId("receivetask1") // 当前活动的id,对应receiveTask.bpmn文件中的活动节点id的属性值.singleResult();// 使用流程变量设置当日销售额,用来传递业务参数processEngine.getRuntimeService().setVariable(execution1.getId(), "汇总当日销售额", 21000);// 向后执行一步,如果流程处于等待状态,使得流程继续执行processEngine.getRuntimeService().signal(execution1.getId());// 查询执行对象IDExecution execution2 = processEngine.getRuntimeService().createExecutionQuery() // 创建执行对象查询.processInstanceId(pi.getId()) // 使用流程实例ID查询.activityId("receivetask2") // 当前活动的id,对应receiveTask.bpmn文件中的活动节点id的属性值.singleResult();// 向后执行一步,如果流程处于等待状态,使得流程继续执行processEngine.getRuntimeService().signal(execution2.getId());// 执行完后,流程结束
}

小结:

  1. 当前任务(一般指机器自动完成,但需要耗费一定时间的工作)完成后,向后推移流程,可以调用runtimeService.signal(executionId),传递接收执行对象的ID
  2. receiveTask在act_ru_task表中是不会产生数据的

7.任务分配

任务分配包括个人任务分配和组任务分配,两者均存在3种任务分配方式:

  • 直接指定办理人--先前的任务分配均是此种,指定具体的人来办理;
  • 使用流程变量动态分配;
  • 实现 org.activiti.engine.delegate.TaskListener 接口动态分配任务。

7.1 个人任务分配

Ⅰ 直接指定任务办理人

略,前面几节讲的都是直接在流程定义中指定Assignee来指定任务办理人。

Ⅱ 使用流程变量动态分配任务办理人

如下图所示,在任务的Properties中指定Assignee为一个变量${userId}

我们可以在流程启动时,指定流程变量userId

Map<String, Object> variables = new HashMap<>();
variables.put("userId", "周芷若");
processEngine.getRuntimeService().startProcessInstanceByKey(processInstanceKey, variables);

当然,你也可以在其它地方指定流程变量userId,譬如,本次任务完成时,指定下一个任务的办理人,在某个条件下通过taskService.setVariable(taskId, "userId", "王五");来指定任务办理人。

Ⅲ 实现 org.activiti.engine.delegate.TaskListener 接口动态分配任务。

先设置Main Config下的Assignee的值为空

在Listener下,指定类 TaskListenerImpl ,实现接口org.activiti.engine.delegate.TaskListener

New的内容如下:

Event=create
Type=java class
点击Select Class去选择TaskListenerImpl

TaskListenerImpl 类定义如下

public class TaskListenerImpl implements TaskListener {public void notify(DeletegateTask delegateTask) {// 指定个人任务的办理人,也可以指定组任务的办理人// 个人任务:通过类去查询数据库,将下一个任务的办理人查询获取,然后通过 setAssignee() 方法指定任务的办理人delegateTask.setAssignee("灭绝师太");}
}

在启动流程实例时,会执行回调函数。

当然,你也可以在指定了任务办理人后再重新分配任务办理人(认领任务)。

String taskId = "5804";
String userId = "张翠山";
processEngine.getTaskService()setAssignee(taskId, userId);

小结

个人任务及三种分配方式:

  1. 在taskProcess.bpmn中直接写assignee="张三丰"
  2. 在taskProcess.bpmn中写 assignee="#{userId}",变量的值是String的。使用流程变量指定办理人。
  3. 使用TaskListener接口,要使类实现该接口,在实现方法中调用 delegateTask.setAssignee(assignee) 来指定个人任务的办理人。

最后,还可以通过任务ID和办理人重新指定办理人:

processEngine.getTaskService().setAssignee(taskId, userId);

7.2 组任务分配

流程图

和组任务相关的表
a.任务表(个人任务,组任务)

select * from act_ru_identitylink
-- 其中字段 TYPE的值有如下两个:participant // 参与者candidate // 候选者

b.历史任务办理人表

select * from act_hi_identitylink
该表中,个人任务的TYPE_都为participant(参与者)
每一个组任务用户的TYPE_包含participant和candidate两条数据

做如下操作

1.部署流程
2.启动流程实例

Ⅰ 直接指定任务办理人

查询我的组任务

// 查询我的组任务
findMyGroupTask() {String candidateUser = "小A";List<Task> list = processEngine.getTaskService.createTaskQuery.taskCandidateUser(candidateUser).orderByTaskCreateTime().asc().list()
}

查询正在执行的任务办理人表

// 查询正在执行的任务办理人表
findRunPersonTask() {String taskId = "";List<IdenityLink> list = processEngine.getTaskService.getIdentityLinksForTask(taskId)if list != null && list.size > 0for tl:listsysout: tl -> taskId, type, processInstanceId, userId // 小C, 小D, 小B, 小A
}

查询历史任务的办理人表

// 查询历史任务的办理人表
findHistoryPersonTask() {String processInstanceId = "";List<HistoricIdentityLink> list = processEngine.getHistoryService.getHistoricIdentityLinksForProcessInstance(processInstanceId)if list != null && list.size > 0for hil:listsysout: tl -> taskId, type, processInstanceId, userId
}

拾取任务,将组任务分配给个人任务,指定任务的办理人字段

// 拾取任务,将组任务分配给个人任务,指定任务的办理人字段
claim() {// 将组任务分配给个人任务String taskId = "";// 分配的个人任务(可以是组任务中的成员,也可以是非组任务的成员)String userId = "小C" // 后再改为大F可以测试非组任务成员,指定了大F之后,再用小A查它的组任务结果为空,可以通过下面的方法改回到组任务proccessEngine.getTaskService.claim(taskId, userId);
}

将个人任务回退到组任务,前提,之前一定是个组任务

// 将个人任务回退到组任务,前提,之前一定是个组任务
setAssignee() {String taskId ="";processEngine.getTaskService.setAssignee(taskId, null);
}

向组任务中添加成员

// 向组任务中添加成员
addGroupUser() {String taskId = "";String userId = "大H";processEngine.getTaskService.addCadidateUser(taskId, userId);
}

从组任务中删除成员

// 从组任务中删除成员
deleteGroupUser() {String taskId = "";String userId = "小B";processEngine.getTaskService.deleteCandidateUser(taskId, userId); // 删除后查询小B的组任务,结果为空。但删除仅删除了小B的候选者数据,并没有删除参与者数据。
}

说明:

  1. 小A,小B,小C,小D是组任务的办理人
  2. 这样分配组任务的办理人不够灵活,因为项目开发中任务的办理人不要放置XML文件中
  3. act_ru_identitylink表存放任务的办理人,包括个人任务和组任务,表示正在执行的任务。
  4. act_hi_identitylink表存放任务的办理人,包括个人任务和组任务,表示历史任务

区别在于:
如果是个人任务TYPE的类型表示participant(参与者)
如果是组任务TYPE的类型表示candidate(候选者)和participant(参与者)

Ⅱ 通过流程变量指定组任务成员

修改bpmn文件,指定Candidate users

Main Config/Candidate users: #{userIDs}

启动流程实例时指定组成员

// 启动流程实例时指定组成员
startProcessInstancevariables.put("userIDs", "大大,中中,小小");

之后完成下面的步骤

  1. 启动完后,查询大大的组任务
  2. 指定大大拾取claim任务
  3. 拾取后,正在执行的任务表中,就有大大了
  4. 完成任务
  5. 查询历史任务表就能查询到大大完成了任务

Ⅲ 使用TaskListener来分配组任务

在bpmn文件中指定Listener

Listenerscreate class TaskListenerImpl implements TaskListener

编写TaskListenerImpl 类

public class TaskListenerImpl implements TaskListener {public void notify(DeletegateTask delegateTask) {delegateTask.addCadidateUser("郭靖")delegateTask.addCadidateUser("黄蓉")// delegateTask.addCadidateUsers(Collection)}
}

后续完成下面的步骤

  1. 部署流程
  2. 启动流程
  3. 查询表:run任务表,act_run_execution表,act_ru_identitylink表
  4. 用郭靖查组任务
  5. 用郭靖拾取claim任务
  6. 查询执行任务表中,郭靖在表中
  7. 查询郭靖的个人任务
  8. 完成个人任务
  9. 查询表,act_hi_identitylink表,查询历史任务表

说明:

  1. 在类中使用delegateTask.addCandidateUser(userId); 的方式分配组任务的处理人,此时郭靖和黄蓉是下一个任务的办理人。
  2. 通过processEngine.getTaskService().claim(taskId, userId); 将组任务分配给个人任务,也叫认领任务,即指定某个人去办理这个任务,此时由郭靖办理任务。

    注意:认领任务的时候,可以是组任务成员中的人,也可以不是组任务成员中的人,此时通过TYPE的类型participant来指定任务的办理人

  3. addCandidateUser()即向组任务添加成员,deleteCandidateUser()即删除组任务的成员
  4. 在开发中,可以将每一个人物的办理人规定好,例如张三的领导是李四和王五,这样张三提交任务,由李四或者王五去查询组任务,可以看到对应张三的申请李四或王五在通过认领任务(claim)的方式,由某个人去完成这个任务。

小结:

组任务及三种分配方式:

  1. 在taskProcess.bpmn中直接写candidate-users="小A,小B,小C,小D"
  2. 在在taskProcess.bpmn中写candidate-users="#{userIDs}",变量的值要是String的。
    使用流程变量指定办理人的代码如下
Map<String, Object> variables = new HashMap<>();
variables.put("userIDs", "大大,中中,小小");
  1. 使用TaskListener接口,使用类实现该接口,在类中定义:
// 添加组任务的用户
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);

组任务分配给个人任务

processEngine.getTaskService().claim(taskId, userId);

个人任务分配给组任务:

processEngine.getTaskService().setAssignee(taskId, null);

向组任务添加成员:

processEngine.getTaskService().addCandidateUser(taskId, userId)

向组任务删除人员:

processEngine.getTaskService().deleteCandidateUser(taskId, userId)

个人任务和组任务存放办理人对应的表:

- act_ru_identitylink 表存放任务的办理人,包括个人任务和组任务表,表示正在执行的任务
- act_hi_identitylink 表存放任务的办理人,包括个人任务和组任务表,表示历史任务

区别:
- 如果是个人任务TYPE的类型表示participant(参与者)
- 如果是组任务TYPE的类型表示candidate(候选者)和participant(参与者)

8. 工作流定义的角色组(了解)

流程图

注:

  • bpmn文件中指定角色组
  • Main Config/Candidate groups 指定角色组:部门经理

在deploymentprocessDefinition方法的最后

// 添加用户角色组
IdentityService identityService = processEngine.getIdentityService()
// 创建角色
// GroupEntity是Group接口的子类
identityService.saveGroup(new GroupEntity("总经理"))
identityService.saveGroup(new GroupEntity("部门经理"))
// 创建用户
// UserEntity是User接口的子类
identityService.saveUser(new UserEntity("张三")/*.set  去尝试一下这个操作*/);
identityService.saveUser(new UserEntity("李四"));
identityService.saveUser(new UserEntity("王五"));
// 建立用户和角色的关联关系
identityService.createMembership("张三", "部门经理");
identityService.createMembership("李四", "部门经理");
identityService.createMembership("王五", "总经理经理");sysout: 添加组织机构成功!
  • 部署流程
  • 查询表
select * from act_id_group // 工作流提供的角色表
select * from act_id_user // 用户表
select * from act_id_membership // 用户角色关联表
  • 启动流程实例
  • 查询act_ru_identitylink表,可以得知“审批”任务是一个组任务,其TYPE_字段值为candidate,其GROUP_ID的值为部门经理

  • 查询张三的组任务,可以查询到张三拥有“审批”这个组任务。
  • 查询李四的组任务,可以查询到李四拥有“审批”这个组任务。
  • 试一下王五呢,(已知:Main Config配置了角色)

  • 拾取任务给张三
  • 查询act_ru_task,就可以看到assignee就有值了,其值为张三

  • 完成任务

【小主,觉得有用打赏点哦!多多少少没关系,一分也是对我的支持和鼓励。谢谢!】

转载于:https://www.cnblogs.com/iuie/p/8006068.html

Activiti入门文档相关推荐

  1. 一份其实很短的 LaTeX 入门文档

    一份其实很短的 LaTeX 入门文档 优雅的 LaTeX 有很多 Geeks 或者 LaTeX's Fanatical Fans 过分地强调了 LaTeX 的一些并非重点的特性,以至于很多初学者会觉得 ...

  2. Entity Framework Core 中文入门文档

    点击链接查看文档: Entity Framework Core 中文入门文档 转载于:https://www.cnblogs.com/ideck/p/efcore.html

  3. CAN总线入门、LIN总线入门文档-蓝凑云下载。 LDFEditor下载 ISO-14229、15765、11898下载与阅读

    这两个入门文档: 中文版.讲解得全面又详细,初学者值得一看. 一.CAN总线入门-瑞萨版 1)https://wwe.lanzoui.com/igt4hit08je2)链接:https://pan.b ...

  4. Android入门文档

    该文章为网络材料整理,部分内容经过重新编写. 一. 名词介绍 JDK:Java Development Kit Java 语言的软件开发工具包 JRE:Java Runtime Environment ...

  5. CloudEvents 入门文档

    CloudEvents 入门文档 - 1.0.3 版本 文档来自:GitHub CloudEvents 摘要 这份非技术规范文档用来为你提供关于 CloudEvents 规范的总体概览.它补充了 Cl ...

  6. matlab入门文档

    博主太赞了,把文档搬运好了:https://blog.csdn.net/in_nocence/article/details/78358614 matlab自带的入门文档,我看了半天的英文,脑阔疼 但 ...

  7. AGM AG32VF407VGT6(248M主频MCU + 内置2KLE CPLD)开发入门文档

    AGM AG32VF407VGT6(248M主频MCU + 内置2KLE CPLD)开发入门文档 第一章:器件特性概述 产品概述: AGM32系列32位微控制器旨在为MCU用户提供新的自由度和丰富的兼 ...

  8. Pushy入门文档中文翻译

    本文为博主原创,允许转载,但请声明原文地址:http://www.coselding.cn/article/2016/12/01/Pushy入门文档中文翻译/ pushy 这是我自己的翻译版本,原文地 ...

  9. GeoPandas官方快速入门文档(中文翻译版)

    GeoPandas官方快速入门文档(中文翻译版) 原文链接>>Introduction to GeoPandas 点这里可以一键跑通>>GeoPandas 简介 GeoPand ...

最新文章

  1. ERROR while rich displaying an object: Error: Continuous value supplied to discrete scale
  2. 二十五、求单点的最短路径
  3. Linux最常用命令:简单易学,但能解决95%以上的问题
  4. zeroclipboard浏览器复制插件使用记录
  5. 我如何使用React和Typescript在freeCodeCamp中构建天气应用
  6. Libevent:6辅助函数以及类型
  7. Kamailio 简介
  8. 【BZOJ1257】余数之和sum,数论练习之取值讨论
  9. gc:C语言的垃圾回收库-英文
  10. idea生成类注释和方法注释的方法
  11. CCF 201809-2 买菜
  12. 最基础eacharts图带数字,百分比,tab切换
  13. PHP exec() has been disabled for security reasons
  14. 【安卓按键精灵】教你一个小时自己开发脚本,零基础1个小时上手
  15. 那些黑天鹅教会我们的IT知识
  16. 福大软工1816 · 第四次作业 - 团队展示
  17. php相册照片批量修改,怎么批量修改图片尺寸 批量修改图片大小
  18. bak 安全牛 kali link
  19. 荣耀4a android art,荣耀4A黑科技大揭秘,真是给工程师给跪了!!
  20. c语言怎么让程序停止3秒,c语言暂停语句1秒

热门文章

  1. 全球及中国重型设备备件行业发展动态及未来趋势调研报告2021年版
  2. Codeforces Round #481 (Div. 3) A. Remove Duplicates
  3. windbg调试实例(4)--句柄泄露
  4. 转 知道这20个正则表达式,能让你少写1,000行代码
  5. C#鼠标控制控件移动的示例
  6. 《Linux下sed命令的使用》
  7. Openssl 之大数运算函数 BN
  8. 干货 | 深入浅出分销体系
  9. 导航菜单设计五步法——B端设计指南
  10. 如何增加儿童产品中的趣味性?