流程实例(ProcessInstance)代表流程定义的执行实例
一个流程实例包括了所有的运行节点,我们可以利用这个对象来了解当前流程实例的进度等信息
例如:用户或者程序安装流程定义的内容发起了一个流程,这个就是一个流程实例
流程定义部署在Activiti后,我们就可以在系统中通过Activiti去管理流程的执行,但是如果我们要将我们的 流程实例
和 业务数据
关联,这时我们需要使用到Activiti中预留的 BusinessKey (业务标识) 来关联(就是activiti通过预设定好的流程帮助我们自动管理流程,但是流程实例有时需要和具体的业务挂钩,所以可以使用activiti给我们预留的businessKey)
先回顾一下部署流程定义的代码(自己手动的)
@Test
public void test01() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
Deployment deploy = processEngine.getRepositoryService().createDeployment()
.addClasspathResource("bpmn/evection.png")
.addClasspathResource("bpmn/evection.bpmn")
.name("流程申请单阿")
.key("evectionah")
.category("haa")
.deploy();
/*
流程部署的id: 1
流程部署的name: 流程申请单阿
流程部署的key: evectionah
流程部署的category: haa
*/
log.info("流程部署的id: {}", deploy.getId());
log.info("流程部署的name: {}", deploy.getName());
log.info("流程部署的key: {}", deploy.getKey());
log.info("流程部署的category: {}", deploy.getCategory());
// 根据流程部署id,去查询刚刚部署的流程定义
ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery()
.deploymentId(deploy.getId()).singleResult();
/*
流程定义的id: evection2:1:4 -
流程定义的key: evection2 - 创建bpmn文件时,点击空白位置时,填入的id
流程定义的name: 出差申请单1 - 创建bpmn文件时,点击空白位置时,填入的name
*/
log.info("流程定义的id: {}", processDefinition.getId());
log.info("流程定义的key: {}", processDefinition.getKey());
log.info("流程定义的name: {}", processDefinition.getName());
}
开启1个流程实例, 并且指定businessKey(案例中的)
/**
* 启动流程实例,添加businessKey
*/
@Test
public void test01() {
// 1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RuntimeService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3.启动流程实例
// (第 1 个参数是:创建bpmn流程图文件时,点击空白位置时,填入的流程定义id)
// (第 2 个参数是:一般是先把业务数据保存好后, 把业务数据的主键放到这, 将activiti的流程实例与业务关联起来)
// (这个businessKey保存在act_ru_execution表的BUSINESS_KEY_字段中)
// (这个businessKey也会保存在act_hi_procinst表的BUSINESS_KEY_字段中)
ProcessInstance instance = runtimeService.startProcessInstanceByKey("evection", "1001");
// 4.输出processInstance相关属性
System.out.println("businessKey = " + instance.getBusinessKey());
}
(下图:act_ru_execution表中的BUSINESS_KEY_字段)
在实际场景中可能由于流程变更需要将当前运行的流程暂停,而不是删除,流程暂停后将不能继续执行。
操作流程的定义为挂起状态,该流程定义下边所有的流程实例全部暂停。
流程定义为挂起状态,该流程定义将不允许启动新的流程实例
,同时该流程定义下的所有的流程实例都 将全部挂起暂停执行
。
/**
* 全部流程挂起实例与激活
*/
@Test
public void test02() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RepositoryService对象
RepositoryService repositoryService = engine.getRepositoryService();
// 3.查询流程定义的对象(根据流程定义key,去查询流程定义对象)
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("evection")
.singleResult();
// 4.获取当前流程定义的状态
boolean suspended = processDefinition.isSuspended();
String id = processDefinition.getId();
// 5.如果挂起就激活,如果激活就挂起
if (suspended) {
// 表示当前定义的流程状态是 挂起的
repositoryService.activateProcessDefinitionById(
id // 流程定义的id
, true // 是否激活
, null // 激活时间
);
System.out.println("流程定义:" + id + ",已激活");
} else {
// 非挂起状态,激活状态 那么需要挂起流程定义
repositoryService.suspendProcessDefinitionById(
id // 流程id
, true // 是否挂起
, null // 挂起时间
);
System.out.println("流程定义:" + id + ",已挂起");
}
}
挂起流程定义后,对于的实例对象中的状态会修改为2
(补充:对某个流程定义挂起后,表act_re_procdef表的SUSPENSION_STATE_字段会修改为2,同时,表act_ru_task表的SUSPENSION_STATE_字段也会修改为2)
然后再去操作对于的流程实例的话(完成该实例的当前任务),会抛如下异常信息
我们再将挂起的流程转变为激活状态,对于的状态值会从2更新为1
然后就是业务流程可以正常处理了
操作流程实例对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续执行,当前流程 定义的其他流程实例是不受干扰的。完成该流程实例的当前任务会抛异常
/**
* 单个流程实例挂起与激活
*/
@Test
public void test03() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RuntimeService
RuntimeService runtimeService = engine.getRuntimeService();
// 3.获取流程实例对象(根据流程实例id,去查询指定的流程实例对象)
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId("25001")
.singleResult();
// 4.获取相关的状态操作
boolean suspended = processInstance.isSuspended();
String id = processInstance.getId();
if (suspended) {
// 挂起--》激活
runtimeService.activateProcessInstanceById(id);
System.out.println("流程定义:" + id + ",已激活");
} else {
// 激活--》挂起
// (taskService.createTaskQuery().processInstanceId("10001").active().list(); 查询时, 是不包括挂起的流程实例的)
// (可以观察到act_ru_task表中SUSPENSION_STATE_字段由1改为了2, 该流程实例对应的任务状态为从激活状态变为了挂起状态)
runtimeService.suspendProcessInstanceById(id);
System.out.println("流程定义:" + id + ",已挂起");
}
}
在进行业务流程建模的时候指定固定的任务负责人:
在Properties视图中,填写Assiginee项为任务负责人
在Activiti中支持使用 UEL表达式,UEL表达式是Java EE6 规范的一部分, UEL(Unified Expression Language) 即 统一表达式语言
, Activiti支持两种UEL表达式: UEL-value
和 UEL-method
首先我们需要将定义的流程部署到Activiti数据库中
/**
* 先将新定义的流程部署到Activiti中数据库中
*/
@Test
public void test01() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RepositoryService进行部署操作
RepositoryService service = engine.getRepositoryService();
// 3.使用RepositoryService进行部署操作
Deployment deploy = service.createDeployment()
.addClasspathResource("bpmn/evection-uel.bpmn") // 添加bpmn资源
.addClasspathResource("bpmn/evection-uel.png") // 添加png资源
.name("出差申请流程-UEL")
.deploy();// 部署流程
// 4.输出流程部署的信息
System.out.println("流程部署的id:" + deploy.getId());
System.out.println("流程部署的名称:" + deploy.getName());
}
部署成功后我们需要启动一个新的流程实例,然后在流程实例创建的其实关联UEL表达式
/**
* 创建一个流程实例
* 给流程定义中的 UEL表达式赋值
*/
@Test
public void test02() {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RuntimeService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
// 设 置 assignee 的 取 值 ,
Map<String, Object> map = new HashMap<>();
map.put("assignee0", "张三");
map.put("assignee1", "李四");
map.put("assignee2", "王五");
map.put("assignee3", "赵财务");
// 创建流程实例
runtimeService.startProcessInstanceByKey("evection-uel", map);
}
启动成功后我们在 act_ru_variable中可以看到UEL表达式对应的赋值信息
userBean 是 spring 容器中的一个 bean,表示调用该 bean 的 getUserId()方法。
UEL-method 与 UEL-value 结合
再比如:${ldapService.findManagerForEmployee(emp)}
ldapService 是 spring 容器的一个 bean,findManagerForEmployee 是该 bean 的一个方法,emp 是activiti流程变量, emp 作为参数传到 ldapService.findManagerForEmployee 方法中。
其它
表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。如下:${order.price > 100 && order.price < 250}
可以使用监听器来完成很多Activiti的流程业务。
我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定assignee Event选项
create:任务创建后触发
assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件都触发
自定义的监听器
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
if("创建请假单".equals(delegateTask.getName())
&& "create".equals(delegateTask.getEventName())){
// 指定任务的负责人
delegateTask.setAssignee("张三-Listener");
}
}
}
测试代码
/**
* 先将新定义的流程部署到Activiti中数据库中
*/
@Test
public void test01() {
// 1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RepositoryService进行部署操作
RepositoryService service = engine.getRepositoryService();
// 3.使用RepositoryService进行部署操作
Deployment deploy = service.createDeployment()
.addClasspathResource("bpmn/evection-listener.bpmn") // 添加bpmn资源
.addClasspathResource("bpmn/evection-listener.png") // 添加png资源
.name("出差申请流程-UEL")
.deploy();// 部署流程
// 4.输出流程部署的信息
System.out.println("流程部署的id:" + deploy.getId());
System.out.println("流程部署的名称:" + deploy.getName());
}
/**
* 创建一个流程实例
* 给流程定义中的 UEL表达式赋值
*/
@Test
public void test02() {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RuntimeService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
// 创建流程实例
runtimeService.startProcessInstanceByKey("evection-listener");
}
代码如下:
// 查询当前个人待执行的任务
@Test
public void findPersonalTaskList() {
// 流程定义key
String processDefinitionKey = "myEvection1";
// 任务负责人
String assignee = "张三";
// 获取TaskService
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());
}
}
需求:
在 activiti 实际应用时,查询待办任务可能要显示出业务系统的一些相关信息。
比如:
查询待审批出差任务列表需要将出差单的日期、 出差天数等信息显示出来。出差天数等信息在业务系统中存在,而并没有在 activiti 数据库中存在,所以是无法通过 activiti 的 api查询到出差天数等信息。
实现:
在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。
@Test
public void findProcessInstance() {
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取TaskService
TaskService taskService = processEngine.getTaskService();
// 获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 查询流程定义的对象
Task task = taskService.createTaskQuery()
.processDefinitionKey("myEvection1")
.taskAssignee("张三")
.singleResult();
// 使用task对象获取实例id
String processInstanceId = task.getProcessInstanceId();
// 使用实例id,获取流程实例对象
ProcessInstance processInstance =
runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 使用processInstance,得到 businessKey
String businessKey = processInstance.getBusinessKey();
System.out.println("businessKey==" + businessKey);
}
注意:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限 。
/**
* 完成任务,判断当前用户是否有权限
*/
@Test
public void completTask() {
//任务id
String taskId = "15005";
// 任务负责人
String assingee = "张三";
//获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 完成任务前,需要校验该负责人可以完成当前任务
// 校验方法:
// 根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(assingee)
.singleResult();
if(task != null){
taskService.complete(taskId); System.out.println("完成任务");
}
}