固定分配-任务办理人
在绘制流程定义模型时,针对节点固定分配办理人进行办理此节点任务。
如下图:针对领导审批 节点填写任务派遣(任务处理人)为 wj,也就是指定 wj为此节点办理人。
查询办理人或候选人的待办任务
@Autowired
private TaskService taskService;
/**
* 查询指定办理人的待办任务
*/
@Test
public void findWaitTask() {
//办理人
String assignee = "wj1";
// 指定个人任务查询
List<Task> taskList = taskService.createTaskQuery()
.taskAssignee(assignee) // 办理人
//.taskCandidateOrAssigned(assignee) // 作为办理人或候选人
.orderByTaskCreateTime().desc()
.list();
for (Task task : taskList) {
System.out.println("任务ID:" + task.getId());
System.out.println("任务名称:" + task.getName());
System.out.println("流程状态:" + (task.isSuspended() ? "已挂起": "已激活") );
System.out.println("任务的创建时间:" + task.getCreateTime());
System.out.println("任务的办理人:" + task.getAssignee());
System.out.println("================");
}
}
UEL 表达式分配-任务办理人
由于固定分配任务办理人,执行到每一个任务,按照的是绘制的 bpmn 流程定义模型中配置的去分配任务负责人,即每个节点任务都是固定办理人。
也可以使用 UEL 表达式动态分配办理人, UEL(Unified Expression Language)即统一表达式语言,UEL 是 java EE6 规范的一部分。
activiti 支持两种 UEL 表达式: UEL-value 和 UEL-method。
UEL-Value 流程变量表达式
方式一:指定属性名 ${属性名}:
如下图:针对 领导审批 节点指定办理人 ${assignee1} ,assignee1 就是一个流程变量。 在启动流程实例时,可以动态指定办理人。
方式二:指定对象属性名 ${对象名.属性名}:
如下图:针对 总监审批 节点指定办理人 ${user.username} ,user 是对象名,也是一个流程变量,通过调用 user 对象中的 getUsername 方法获取到动态值。
UEL-Method 方法表达式
针对 总经理审批 节点指定办理人 ${userService.getUsername()} , userService 是 Spring 容器中的一个 bean 实例,对应调用 userService 的 getUsername() 实例方法。
UEL-Method 和 UEL-Value 组合 使用
通过用户id查询上级领导作为任务办理人:
针对 行政审批 节点指定办理人 ${deptService.findManagerByUserId(userId)} 。
- deptService 是 Spring 容器的一个 bean 实例;
- findManagerByUserId 是 deptService 实例方法
- userId 是流程变量, 将 userId 作为参数传到 deptService.findManagerByUserId 方法中
表达式其他支持格式
表达式支持解析:基本数据类型、JavaBean、 Map集合、List集合、 Array数组等 。
也可作为条件判断(一般用于网关),如: ${leave.duration > 3 && leave.duration < 10}
注意事项
当使用了 UEL 流程变量表达式时,在执行到所需流程变量的任务时,必须保证在此任务前流程变量存在,否则 activiti会抛出异常。 比如:某个节点任务设置了表达式 ${leave.duration > 3 && leave.duration < 10} ,当执行到此任务时必须保证 leave 在 流程变量中存在。
代码实现 UEL 动态分配办理人
创建UserService
,并在类上加上 @Service 注解 在 getUsername
方法上打断点,观察:总监审批完成任务时,会调用 getUsername
获取 总经理审批 节点 办理人
@Service
public class UserService {
public String getUsername() {
System.out.println("UserService.getUsername获取用户信息");
return "www";
}
}
创建 DeptService
,并在类上加上 @Service 注解 在 findManagerByUserId
方法上打断点,观察:总经理审批完成任务时,会调用 findManagerByUserId
获 取 行政审批 节点办理人
@Service
public class DeptService {
public String findManagerByUserId(String userId) {
System.out.println("DeptService.findManagerByUserId 查询userId=" + userId + "上级领导作为办理人");
return "abc";
}
}
部署新的请假流程定义:
@Test
public void deploy() throws Exception {
// 模型id
String id = "64c30834-ccdb-11ec-844d-28d0ea3dce6b";
// 获取流程图 json 字节码
byte[] jsonBytes = repositoryService.getModelEditorSource(id);
if (jsonBytes == null) {
System.out.println("模型数据为空,请先设计流程并成功保存,再进行发布。");
return;
}
// 转xml字节数组
byte[] xmlBytes = bpmnJsonToXmlBytes(jsonBytes);
if(xmlBytes == null){
System.out.println("数据模型不符要求,请至少设计一条主线流程。");
return;
}
// 流程图片字节码
byte[] pngBytes = repositoryService.getModelEditorSourceExtra(id);
// 获取模型
Model model = repositoryService.getModel(id);
// 流程定义xml名称
String processName = model.getName() + ".bpmn20.xml";
// 流程定义png名称
String pngName = model.getName() +"." + model.getKey() + ".png";
// 流程部署
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.addString(processName, new String(xmlBytes, "UTF-8")) // xml文件
.addBytes(pngName, pngBytes ) // 图片
.deploy();
// 更新 部署id 到模型对象(将模型与部署数据绑定)
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
System.out.println("部署完成");
}
启动流程实例时:分配流程定义的流程变量:
@Test
public void startProcessSetAssigneeUEL(){
// 流程变量
Map<String,Object> variables = new HashMap<>();
variables.put("assignee1","wen");
// 使用 Map 作为 User对象
Map<String, Object> userMap = new HashMap<>();
userMap.put("username", "jie");
variables.put("user", userMap);
// 设置方法参数 userId 流程变量:${deptService.findManagerByUserId(userId)}
variables.put("userId", "123");
// 启动流程实例,设置流程定义的流程变量
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey("leave", variables);
System.out.println("流程实例ID:" + processInstance.getProcessInstanceId());
}
启动流程实例成功后,在 ACT_RU_VARIABLE 表会插入 variables 流程变量数据:
查询办理人 wen 的待办任务,是否有任务,进行完成任务
@Test
public void completeTask() {
taskService.complete("495a30e3-cce4-11ec-9c54-28d0ea3dce6b");
}
TaskListener 任务监听器
TaskListener 任务监听器可用于很多流程业务功能,比如,任务创建后:分配任务办理人;任务完成后:记录日志,发送提醒;
任务监听器相关触发事件名:
- create:任务创建后触发
- assignment:任务分配办理人后触发
- complete:任务完成后触发
- delete:任务删除后触发
自定义任务监听器
创建 TaskListener 实现类,用于指定处理人
public class CustomTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("任务节点名:" + delegateTask.getName() + ",触发事件名:" + delegateTask.getEventName());
if("总裁审批".equals(delegateTask.getName()) &&
"create".equalsIgnoreCase(delegateTask.getEventName())) {
//这里指定任务负责人
delegateTask.setAssignee("小学");
}
}
}
针对流程模型添加一个总裁审批节点,其中添加任务监听器,如下:
完成节点任务,在完成总裁审批的前一节点任务后,会创建总裁审批节点任务,创建任务后会执行 CustomTaskListener 监听器,然后设置 总裁审批节点任务的处理人。
获取当前任务的下一节点信息
获取当前任务的下一节点信息,判断下一节点是用户任务 UserTask 则获取,获取后方便动态设置一下节点任务办 理人。
@Autowired
private RepositoryService repositoryService;
/**
* 获取当前任务的下一节点用户任务信息,
* 为了动态设置一下节点任务办理人
*/
@Test
public void getNextNodeInfo() {
String taskId = "937b6556-cce4-11ec-a13e-28d0ea3dce6b";
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if(task == null) {
System.out.println("任务不存在");
return;
}
// 获取当前模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
// 根据任务节点id获取当前节点
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
// 获取当前节点的连线信息(可能会有多条分支,所以返回值是集合)
List<SequenceFlow> outgoingFlows = ((FlowNode)flowElement).getOutgoingFlows();
for (SequenceFlow outgoingFlow : outgoingFlows) {
// 下一节点
FlowElement nextFlowElement = outgoingFlow.getTargetFlowElement();
if(nextFlowElement instanceof UserTask) {
// 用户任务,获取节点id和名称
System.out.println("获取节点id:" + nextFlowElement.getId());
System.out.println("获取节点名称:" + nextFlowElement.getName());
}else if(nextFlowElement instanceof EndEvent) {
// 结束节点
break;
}
}
}
任务完成后设置一下任务办理人
不基于 UEL 表达式,当前任务完成后,查询下一任务,使用java代码动态指定一下节点任务办理人,这样更加灵活。
/**
* 完成当前任务,设置一下节点任务处理人
*/
@Test
public void completeTaskSetNextAssignee () {
String taskId = "xxx";
// 查询任务
Task task = taskService.createTaskQuery()
.taskId(taskId)
.singleResult();
// 完成任务
taskService.complete(taskId);
// 查询下一个任务
List<Task> taskList = taskService.createTaskQuery()
.processInstanceId(task.getProcessInstanceId()).list();
// 有下节点任务
if ( !CollectionUtils.isEmpty(taskList)) {
// 针对每个任务分配审批人
for (Task t : taskList) {
// 当前任务有审批人,则不设置新的审批人
if(StringUtils.isNotEmpty(t.getAssignee())) {
continue;
}
// 分配任务办理人
String assignees = "hahaha";
taskService.setAssignee(t.getId(), assignees);
}
}
}
候选人任务
假如现在有这么个需求:
一个电商平台, 每天订单量都很大, 在处理订单的时候分配了 user1, user2, user3 三个员工, 这时候就可以用上这个候选人功能了, 在一个任务里配置上可能会参与这个任务的候选人, 这样候选人员工就可以通过查询候选人任务知道自己可以领取哪些任务, 从而达到员工自动领取任务的功能。
绘制候选人流程模型
创建新流程模型 ,流程key: testGroupTask
指定任务多个候选人:点击领导审批 ,指定此节点任务一组候选人 zhangsan,wj1,wj2,使用英文逗号分隔,也可以指定 UEL 表达式。
查询候选任务
@Test
public void getGroupTaskList() {
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("testGroupTask")
.taskCandidateUser("wj1")
.list();
for (Task task : list) {
System.out.println("任务id:" + task.getId());
System.out.println("任务办理人(是候选人所以是null):" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
拾取组任务
拾取组任务 也可以是候选人自己领取的任务, 就是指定任务的一个办理人,候选人员拾取组任务后该任务变为自己 的个人任务。
@Test
public void claimTask(){
// 任务id
String taskId = "fb75f478-cd03-11ec-a20f-28d0ea3dce6b";
// 候选人id
String userId = "wj1";
//拾取组任务, 将候选人 wj1 变为个人任务
taskService.claim(taskId, userId);
System.out.println(userId + "任务拾取成功");
}
查询个人待办任务
@Test
public void getUserTask() {
// 流程定义 key
String processDefinitionKey = "testGroupTask";
// 任务办理2
String assignee = "wj1";
//查询组任务
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey(processDefinitionKey)
.taskAssignee(assignee)
.list();
for (Task task : tasks) {
System.out.println("任务ID: " + task.getId());
System.out.println("代理人: " + task.getAssignee());
System.out.println("任务名: " + task.getName());
System.out.println("--------------");
}
}
归还组任务
如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人,这时所有候选人就都可以拾取任务。
建议:归还任务前校验该用户是否是该任务的负责人
@Test
public void assigneeToGroupTask() {
// 当前待办任务
String taskId = "fb75f478-cd03-11ec-a20f-28d0ea3dce6b";
// 任务办理人
String assignee = "wj1";
// 校验 assignee 是否为该任务的负责人,如果是负责人才可以归还组任务
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(assignee)
.singleResult();
if (task != null) {
// 第2个参数代理人置为 null,则归还组任务
taskService.setAssignee(taskId, null);
System.out.println("归还任务完成:" + taskId);
}
}
办理个人任务
@Test
public void completeTask(){
// 任务ID
String taskId = "fb75f478-cd03-11ec-a20f-28d0ea3dce6b";
taskService.complete(taskId);
System.out.println("完成任务:" + taskId);
}
数据库表操作
- 查询当前任务执行表,当前是组任务,所有 assignee 为空,当拾取任务后该字段就是拾取用户的 id
SELECT * FROM act_ru_task
- 查询任务参与者,记录当前参考任务用户或组,当前任务如果设置了候选人,会向该表插入候选人记录,有几个候选人就插入几条记录
SELECT * FROM act_ru_identitylink
注意:当 act_ru_identitylink 插入记录的同时,也会向 act_hi_identitylink 历史表插入记录。