概述

加签本质上就是让多实例任务增加一个处理任务和新增处理人。在业务流程中常有一种情况是,如果A审批,那么直接通过,如果是B审批,则还需要C审批才能通过。后一类情况就特别适合使用加签。另外在转他人处理的问题上,普通任务节点可以通过自由跳转到本节点实现,但多实例任务中这样处理会导致该任务节点所有处理人重新开始处理任务,所以多实例任务的转他人处理不适合用自由跳转,更适合通过加签去实现。

实现

串行多实例任务加签

直接上源码

import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;public class SequenceContersignCmd implements Command {private String taskId;private Map<String, Object> variables;public SequenceContersignCmd(String taskId, Map<String, Object> variables) {this.taskId = taskId;this.variables = variables;}public Object execute(CommandContext commandContext) {TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();TaskEntity taskEntity = taskEntityManager.findTaskById(taskId);ExecutionEntity executionEntity = taskEntity.getExecution();//多实例任务总数加一Integer nrOfInstances = (Integer)executionEntity.getVariable("nrOfInstances");executionEntity.setVariable("nrOfInstances", nrOfInstances + 1);// 设置流程变量executionEntity.setVariables(variables);return null;}
}

串行多实例的加签非常简单,只需要调整任务总数变量nrOfInstances加一即可。因为串行多实例任务每个时刻最多只会有一个活动实例,所以当前活动实例数恒定是一,nrOfActiveInstances不用变。执行这个命令类,即可

并行多实例加签

import java.util.Date;
import java.util.Map;import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;public class ParallelContersign implements Command {private String taskId;private Map<String, Object> variables;public ParallelContersign(String taskId, Map<String, Object> variables) {this.taskId = taskId;this.variables = variables;}public Object execute(CommandContext commandContext) {ProcessEngineConfigurationImpl pec = commandContext.getProcessEngineConfiguration();TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();TaskEntity taskEntity = taskEntityManager.findTaskById(taskId);ExecutionEntity executionEntity = taskEntity.getExecution();// 设置流程变量executionEntity.setVariables(variables);ExecutionEntity parentExecutionEntity = executionEntity.getParent();ExecutionEntity newExecutionEntity = parentExecutionEntity.createExecution();newExecutionEntity.setActive(true);newExecutionEntity.setConcurrent(true);newExecutionEntity.setScope(false);TaskEntity newTaskEntity = new TaskEntity();newTaskEntity.setCreateTime(new Date());newTaskEntity.setTaskDefinition(taskEntity.getTaskDefinition());newTaskEntity.setProcessDefinitionId(taskEntity.getProcessDefinitionId());newTaskEntity.setTaskDefinitionKey(taskEntity.getTaskDefinitionKey());newTaskEntity.setProcessInstanceId(taskEntity.getProcessInstanceId());newTaskEntity.setExecutionId(newExecutionEntity.getId());newTaskEntity.setName(taskEntity.getName());newTaskEntity.setId(pec.getIdGenerator().getNextId());newTaskEntity.setExecution(newExecutionEntity);pec.getTaskService().saveTask(newTaskEntity);Integer nrOfInstances = (Integer)executionEntity.getVariable("nrOfInstances");Integer nrOfActiveInstances = (Integer)executionEntity.getVariable("nrOfActiveInstances");executionEntity.setVariable("nrOfInstances", nrOfInstances + 1);executionEntity.setVariable("nrOfActiveInstances", nrOfActiveInstances + 1);newExecutionEntity.setVariableLocal("loopCounter", nrOfInstances);return null;}
}

并行多实例的加签复杂一些,主要是并行多实例任务需要我们手动新建execution和对应的task。24-25行获取当前taskId对应的execution,30行再获取其父execution。31-34行通过父execution创建子execution并为其设置属性。35-45行新建task并设置其属性,最后进行保存。49行增加任务总数变量nrOfInstances,50行增加活动实例数,51行设置execution的局部变量loopCounter,loopCounter变量其实就是类似一个index,作为多实例任务的序列号。

转他人处理

文章开头提到了转他人处理的问题。这里准备以实例进行说明如何通过加签实现转他人处理。

首先创建命令类TransferToOtherCmd.java

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;import org.activiti.engine.TaskService;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import org.activiti.engine.impl.pvm.process.ActivityImpl;public class TransferToOtherCmd implements Command {private String taskId;private Map<String, Object> variables;private String assignee;public TransferToOtherCmd(String taskId, Map<String, Object> variables, String assignee) {this.taskId = taskId;this.variables = variables;this.assignee = assignee;}public Object execute(CommandContext commandContext) {ProcessEngineConfigurationImpl pec = commandContext.getProcessEngineConfiguration();TaskService taskService = pec.getTaskService();TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();TaskEntity taskEntity = taskEntityManager.findTaskById(taskId);ExecutionEntity executionEntity = taskEntity.getExecution();String processDefinitionId = executionEntity.getProcessDefinitionId();ProcessDefinitionEntity processDefinitionEntity = Context.getProcessEngineConfiguration().getDeploymentManager().findDeployedProcessDefinitionById(processDefinitionId);ActivityImpl curActivityImpl = processDefinitionEntity.findActivity(taskEntity.getName());List<String> assigneeList = (List<String>) executionEntity.getVariable("assigneeList");if (curActivityImpl.getActivityBehavior() instanceof SequentialMultiInstanceBehavior) {Integer nrOfInstances = (Integer) executionEntity.getVariable("nrOfInstances");Integer loopCounter = (Integer) executionEntity.getVariable("loopCounter");executionEntity.setVariableLocal("nrOfInstances", nrOfInstances + 1);List<String> newAssigneeList = new ArrayList<String>();int i=0;for(; i<loopCounter + 1; i++) {newAssigneeList.add(assigneeList.get(i));}newAssigneeList.add(assignee);for(; i<assigneeList.size(); i++) {newAssigneeList.add(assigneeList.get(i));}newAssigneeList.addAll(assigneeList);executionEntity.setVariable("assigneeList", newAssigneeList);} else {ExecutionEntity parentExecutionEntity = executionEntity.getParent();ExecutionEntity newExecutionEntity = parentExecutionEntity.createExecution();newExecutionEntity.setActive(true);newExecutionEntity.setConcurrent(true);newExecutionEntity.setScope(false);TaskEntity newTaskEntity = new TaskEntity();newTaskEntity.setCreateTime(new Date());newTaskEntity.setTaskDefinition(taskEntity.getTaskDefinition());newTaskEntity.setProcessDefinitionId(taskEntity.getProcessDefinitionId());newTaskEntity.setTaskDefinitionKey(taskEntity.getTaskDefinitionKey());newTaskEntity.setProcessInstanceId(taskEntity.getProcessInstanceId());newTaskEntity.setExecutionId(newExecutionEntity.getId());newTaskEntity.setName(taskEntity.getName());newTaskEntity.setId(pec.getIdGenerator().getNextId());newTaskEntity.setExecution(newExecutionEntity);newTaskEntity.setAssignee(assignee);pec.getTaskService().saveTask(newTaskEntity);Integer nrOfInstances = (Integer) executionEntity.getVariable("nrOfInstances");Integer nrOfActiveInstances = (Integer) executionEntity.getVariable("nrOfActiveInstances");executionEntity.setVariable("nrOfInstances", nrOfInstances + 1);executionEntity.setVariable("nrOfActiveInstances", nrOfActiveInstances + 1);newExecutionEntity.setVariableLocal("loopCounter", nrOfInstances);}// 设置流程变量executionEntity.setVariables(variables);taskService.complete(taskId);return null;}
}

类的属性assignee表示转给谁处理。这个转他人处理类基本融合了串行多实例和并行多实例加签的代码。这里假设多实例任务的collection属性为assigneeList,那么流程变量assigneeList的列表里面放置的是多实例任务的处理人。49-58行为串行多实例任务添加处理人。如果把转发目标处理人加到assigneeList尾部,那么任务提交后,下一个处理人不是目标处理人。例如我们原来指定张三、李四、王五作为三个处理人,张三处理的时候转给赵六去处理,如果把赵六加到assigneeList尾部,那么张三提交转他人处理后,下一个是李四处理,赵六放到最后了!而我们的预期应该是张三转赵六处理,张三提交后下一个处理人应该是赵六。所以要把赵六插到assigneeList中张三后面才对。如果张三是第一个人,那赵六就应该插在第一个元素后面。并行多实例则不需要这样做,只需要在75行对任务设置对应的处理人即可。最后在87行提交当前任务。

接下来测试一个串行多实例任务的实例,流程图如下:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"><process id="串行多实例任务" name="串行多实例任务" isExecutable="true"><startEvent id="startevent1" name="Start"></startEvent><userTask id="员工互评" name="员工互评" activiti:assignee="${assignee}"><multiInstanceLoopCharacteristics isSequential="true" activiti:collection="assigneeList" activiti:elementVariable="assignee"><loopCardinality>3</loopCardinality></multiInstanceLoopCharacteristics></userTask><sequenceFlow id="flow1" sourceRef="startevent1" targetRef="员工互评"></sequenceFlow><userTask id="领导审核" name="领导审核" activiti:assignee="丁总"></userTask><sequenceFlow id="flow2" sourceRef="员工互评" targetRef="领导审核"></sequenceFlow><endEvent id="endevent1" name="End"></endEvent><sequenceFlow id="flow3" sourceRef="领导审核" targetRef="endevent1"></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_串行多实例任务"><bpmndi:BPMNPlane bpmnElement="串行多实例任务" id="BPMNPlane_串行多实例任务"><bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"><omgdc:Bounds height="35.0" width="35.0" x="150.0" y="320.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="员工互评" id="BPMNShape_员工互评"><omgdc:Bounds height="55.0" width="105.0" x="230.0" y="310.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="领导审核" id="BPMNShape_领导审核"><omgdc:Bounds height="55.0" width="105.0" x="380.0" y="310.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"><omgdc:Bounds height="35.0" width="35.0" x="530.0" y="320.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"><omgdi:waypoint x="185.0" y="337.0"></omgdi:waypoint><omgdi:waypoint x="230.0" y="337.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"><omgdi:waypoint x="335.0" y="337.0"></omgdi:waypoint><omgdi:waypoint x="380.0" y="337.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"><omgdi:waypoint x="485.0" y="337.0"></omgdi:waypoint><omgdi:waypoint x="530.0" y="337.0"></omgdi:waypoint></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>

我们定义一个简单的串行多实例任务的流程,设定多实例任务数为3,处理人的列表读取流程变量assigneeList。然后我们初始化流程引擎并部署该流程。然后启动流程的时候设置流程变量:

public void startProcessByIdWithVar() {RuntimeService runtimeService = pe.getRuntimeService();List<String> assigneeList = new ArrayList<String>();assigneeList.add("张三");assigneeList.add("李四");assigneeList.add("王五");Map<String, Object> vars = new HashMap<String, Object>();vars.put("assigneeList", assigneeList);ProcessInstance pi = runtimeService.startProcessInstanceById("串行多实例任务:1:4", vars);System.out.println("流程定义ID:" + pi.getProcessInstanceId());System.out.println("流程实例ID:" + pi.getId());
}

之后观察数据库表

act_ru_execution表:

act_ru_task表:

act_ru_variable表:

通过act_ru_task表我们看到当前assignee是张三,接下来我们调用写好的TransferToOtherCmd命令:

public void TransferToOther() {String taskId = "2513";Map<String, Object> variables = new HashMap<String, Object>();TransferToOtherCmd TransferToOtherCmd = new TransferToOtherCmd(taskId, variables, "赵六");ServiceImpl service = (ServiceImpl)pe.getRepositoryService();CommandExecutor commandExecutor = service.getCommandExecutor();commandExecutor.execute(TransferToOtherCmd);
}

这个函数根据taskId,调用TransferToOtherCmd命令,把张三的任务提交给赵六。执行后,可以查看act_ru_task表:

此时任务处理人已经变味赵六。

至于并行多实例任务的情况,读者可以自行测试结果。

小结:

加签是业务流程中常见的情况,而是否需要用加签来作为转他人处理的手段,读者可以自行斟酌,本文只是抛砖引玉。除了加签之外,有时候可能还会有“减签”的业务情况。减签其实和加签原理差不多,对于串行多实例任务,无非就是减少任务总数变量nrOfInstances,对于并行多实例任务则除了修改任务总数变量nrOfInstances,还要修改活动实例数nrOfActiveInstances,并且被删除任务的loopCount变量以及为被删除任务loopCount大于被删除任务的都要减一,同时还要删除相关execution和task。不过本人暂时没遇到过业务上需要减签的情况。

activiti学习(二十八)——加签相关推荐

  1. Java多线程学习二十八:原子类和 volatile 有什么异同?

    原子类和 volatile 有什么异同 案例说明 volatile 和原子类的异同 我们首先看一个案例.如图所示,我们有两个线程. 在图中左上角可以看出,有一个公共的 boolean flag 标记位 ...

  2. cocos2d-x的初步学习二十八之爱消除一

    这篇文章中,我们将模仿某个游戏来实现,首页我们做首界面,这里都是一些UI元素的布局,其中会涉及到一些动画,比如贝塞尔曲线,还有cctableview来显示我们的得分,做个排行榜,OK,下面我直接上代码 ...

  3. ballerina 学习二十八 快速grpc 服务开发

    ballerina 的grpc 开发模型,对于开发者来说简单了好多,不是schema first 的方式,而是我们 只要编写简单的ballerina service 就可以了,proto 文件是自动帮 ...

  4. Java小白学习二十八——冒泡排序

    冒泡排序 比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置 每一次比较,都会产生一个最大,或者最小的数字 下一轮则可以少一次排序 依次循环,直到结束 package com. ...

  5. OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope

    OpenCV学习笔记(二十六)--小试SVM算法ml 总感觉自己停留在码农的初级阶段,要想更上一层,就得静下心来,好好研究一下算法的东西.OpenCV作为一个计算机视觉的开源库,肯定不会只停留在数字图 ...

  6. 深度学习之图像分类(二十八)-- Sparse-MLP(MoE)网络详解

    深度学习之图像分类(二十八)Sparse-MLP(MoE)网络详解 目录 深度学习之图像分类(二十八)Sparse-MLP(MoE)网络详解 1. 前言 2. Mixture of Experts 2 ...

  7. 【Vue学习】—Vue UI组件库(二十八)

    [Vue学习]-Vue UI组件库(二十八) 一.移动端常用的UI组件库 二.PC端常用的UI组件库 三.具体使用自行查看文档,这里就不做概述了

  8. JavaScript学习(二十八)—事件冒泡和事件捕获

    JavaScript学习(二十八)-事件冒泡和事件捕获 一.什么是事件流? 简单说,事件流就是指事件的执行顺序,他包含两种模式:事件冒泡.事件捕获. (一).事件冒泡 最常用的一种模式,就是指事件的执 ...

  9. 嵌入式系统设计师学习笔记二十八:嵌入式程序设计③——高级程序设计语言

    嵌入式系统设计师学习笔记二十八:嵌入式程序设计③--高级程序设计语言 解释程序和编译程序 编译器的工作阶段示意图 语法错误:非法字符,关键字或标识符拼写错误 语法错误:语法结构出错,if--endif ...

  10. 知识图谱论文阅读(八)【转】推荐系统遇上深度学习(二十六)--知识图谱与推荐系统结合之DKN模型原理及实现

    学习的博客: 推荐系统遇上深度学习(二十六)–知识图谱与推荐系统结合之DKN模型原理及实现 知识图谱特征学习的模型分类汇总 知识图谱嵌入(KGE):方法和应用的综述 论文: Knowledge Gra ...

最新文章

  1. 基于MATLAB FDATOOL的CIC滤波器设计
  2. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...
  3. 老对手 Intel 与 AMD 也开始合作打造新品了,Nvidia 怎么看?
  4. 春风app一直显示服务器内部错误,CHANGES.md
  5. OpenShift 4.6 新特性 - 用 Windows MachineConfig Operator 管理 Windows Container
  6. 关于 rm -rf * 你需要知道的
  7. Android开发 Facebook取得key-hashes
  8. 设计模式(26)-----享元模式
  9. 使用taskset命令来限制进程的CPU
  10. 函数对称性常见公式_初中函数公式大全
  11. unity材质球英文翻译
  12. 如何使用一键回录游戏视频
  13. LOJ#10064. 「一本通 3.1 例 1」黑暗城堡
  14. 5G网络测速,实在是太快了!
  15. 解决margin塌陷问题
  16. 爬虫案例:登录V2EX
  17. SAP 远程连接登录配置(SAP Router)
  18. Goproxy-优秀的开源代理工具
  19. 无线互联网:SP和移动运营商的新战场?
  20. 《算法分析与设计》第二周课堂笔记 孙晓 老湿

热门文章

  1. conda tricks
  2. 服务器虚拟网卡有mac地址吗,如何动态添加MAC地址可定义的虚拟网卡?
  3. Haar特征描述算子-人脸检测
  4. 十七届智能车竞赛线上比赛赛道长度是多少?
  5. 微信小程序for循环里面添加input事件
  6. OAF 增删改查/LOV/Button/Sequence 的完整案例开发
  7. 知道下载文件的地址,怎么用ie下载
  8. 百度开发者平台实现拍照识别
  9. 小程序页面分享(程序页面分享或者按钮分享)
  10. Treeset序列化到文件失败