目录
一、前言
二、改派实现
三、自由跳转实现
思路1
思路2
流程流转时,某个环节的任务处理人变更(即改派)是一种非常常见的需求,本来任务处理人可能是A,但是A因为有事处理不了,管理员需要将该单子改派给B临时处理下。
在中国式流程中,流程的 回退 也是必不可少的,流程流转到某个环节时,处理人一看:咦 这填的都是啥玩意儿,根本不符合要求,想直接打回给上一环节或者走过的任意环节,那么就需要支持回退功能;有时候流程方面还有特殊的需求,比如越级上报,某些处理人权限比较高运行跳过某些环节直接执行,甚至说可以一步走到底,这便依赖Activiti的自由流程
本文就简单介绍下Activiti中如何实现改派和回退。
在实际使用Activiti中,绝大多数公司都是有一套自己的用户管理系统,而且Activiti自身的用户和用户组功能其实也并不能满足部分需求,比如想分配给一个部门、一个角色等等。因此Activiti中的人员信息其实不是那么重要,在我们项目中甚至不会从里面去获取人员信息,仅仅只是设值。Activiti的任务处理人主要是存放在 ACT_RU_TASK 的 ASSIGNEE_ 字段中以及ACT_RU_IDENTITYLINK,前者主要是单个审核人的情况,后者主要是候选人的情况。
其实改派的实现很简单,就是为当前任务重新设置处理人,调用如下方法即可:
taskService.setAssignee("taskId", "userId");
当使用流程变量的方式设置处理人时,在业务上可能是多个处理人或处理组,因此在 ASSIGNEE_ 中存的可能是个用户数组,形如:[a,b,b] 。由于 ASSIGNEE_ 字段是varchar类型且仅为255,因此用户id如果是uuid格式的话,最多只能存储7个...
设置处理人的api还挺多,还有一种方式也行:
taskService.unclaim("taskId");
taskService.claim("taskId", "userId");
该方式与上面的主要区别是,claim方法会去检查当前是否已经有处理人,如果有则会抛错,需要先调用 unclaim 方法。
Activiti6的最大变化便是对代码的重构,其去掉了原先的pvm,直接针对bpmn处理,因此性能上会有些许提升(也没试验过,之前项目中使用的是Activiti5.22,因此原先的自由跳转与6的实现已经完全不一样了,题外话,pvm主要就是负责流程整个运行期的执行、流转等所有运行操作,其所有实现类都在org.activiti.engine.impl.pvm包里,但是6全部移除了...)
这里主要演示回退的场景,先看上面的流程图,假设当前任务已经流转到领导审批,领导一看,你这审批信息填的不正确鸭,需要驳回,啪一点,此时流程需回退到审批信息填写环节,如何实现呢?
在运行过程中,动态修改Activiti的流程定义,修改领导审批环节的出线,使之流转到审批信息填写,在流转完成后再恢领导审批原先的出线(即结束环节),该方式虽然也能实现自由跳转,但是存在并发的问题,假设A点击回退,此时流程定义已经被修改,领导审批的出线已经变为审批信息填写,同时B点击提交,却发现流程流转到了审批信息填写...B说简直见了鬼。因此不可取
使用执行计划直接指定当前流程实例执行所选环节,由于在并发情况下当前任务的执行id是唯一的,因此该方式也没有带来不安全性,下面基于此实现贴代码:
首先是两个命令,即删除当前任务命令以及执行自由跳转命令:
/**
* 继承NeedsActiveTaskCmd主要是为了在跳转时要求当前任务不能是挂起状态,也可以直接实现Command接口
* Created by xujia on 2020/2/10
*/
public class DeleteTaskCmd extends NeedsActiveTaskCmd {
public DeleteTaskCmd(String taskId){
super(taskId);
}
public String execute(CommandContext commandContext, TaskEntity currentTask){
TaskEntityManagerImpl taskEntityManager = (TaskEntityManagerImpl)commandContext.getTaskEntityManager();
// 获取当前任务的执行对象实例
ExecutionEntity executionEntity = currentTask.getExecution();
// 删除当前任务,来源任务
taskEntityManager.deleteTask(currentTask, "jumpReason", false, false);
// 返回当前任务的执行对象id
return executionEntity.getId();
}
public String getSuspendedTaskException() {
return "挂起的任务不能跳转";
}
}
/**
* 执行自由跳转命令
* Created by xujia on 2020/2/10
*/
public class SetFLowNodeAndGoCmd implements Command {
/**
* 目标节点对象
*/
private FlowNode flowElement;
/**
* 当前任务执行id
*/
private String executionId;
public SetFLowNodeAndGoCmd(FlowNode flowElement,String executionId){
this.flowElement = flowElement;
this.executionId = executionId;
}
public Void execute(CommandContext commandContext){
// 获取目标节点的来源连线
List flows = flowElement.getIncomingFlows();
if(flows==null || flows.size()<1){
throw new ActivitiException("回退错误,目标节点没有来源连线");
}
// 随便选一条目标节点的入线来执行,使当前执行计划为:从所选择的流程线流转到目标节点,实现跳转
ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findById(executionId);
executionEntity.setCurrentFlowElement(flows.get(0));
commandContext.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
return null;
}
}
然后是调用:
@Test
public void listTaskTest() {
// 获取当前任务
Task currentTask = taskService.createTaskQuery().taskId("132502").singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());
// 获取流程定义
Process process = bpmnModel.getMainProcess();
// 获取目标节点定义
FlowNode targetNode = (FlowNode) process.getFlowElement("sid-C24BA4F5-F744-4DD7-8D51-03C3698044D2");
// 删除当前运行任务,同时返回执行id,该id在并发情况下也是唯一的
String executionEntityId = managementService.executeCommand(new DeleteTaskCmd(currentTask.getId()));
// 流程执行到来源节点
managementService.executeCommand(new SetFLowNodeAndGoCmd(targetNode, executionEntityId));
}
运行之后就会发现流程直接流转到审批信息填写环节,且处理人也变成原先的处理人,灵活运用可实现多种多样的需求。