最近在用Camunda流程引擎做业务开发时,产品经理提了一个需求:共享任务
根据产品经理的描述:流程引擎首先会产生一个共享任务,然后多个人会看到此任务,如果有一个人执行了此任务之后,此任务就成已执行,其它人就不能执行,也看不到此任务了 ,流程流转到下一个节点
举个栗子,有下面这样一样流程:
在发起此流程后,执行完新增申请信息节点的任务后,在共享任务这个流程节点会产生一个任务,这个任务需要A、B 两人其中一个人来执行,要完成此功能
先来整理下思路:
-
流程产生任务
-
记录能完成此任务的用户(如果有2个用户,2个用户都能看到此任务)
-
如果用户1有强迫症,看到任务提醒的红点都要点开查看,看到任务并完成后,流程流程到下一节点
思路清楚了,Camunda流程引擎是如何完成此功能的呢?
在此流程中, 在完成【申请信息】节点产生的任务后,Camunda 流程引擎会帮助我们完成产生【共享任务】节点的任务
重点:在完成【申请信息】的任务时,会把A、B 两人信息通过参数提交,Camunda 流程引擎会把A、B信息存储起来,并产生任务,此任务只有A、B两人都能看见
那么问题来了,此信息存储在什么地方?
在Camunda的库中,有ACT_HI_IDENTITYLINK这样一张表,此表记录了任务与人员的信息
我提交的参数分别是:demo、8,然后在此表中产生了2条数据
那么问题又来了,没有写一句代码,是如何实现此功能的呢?
Camunda 之所以很强大,就是在这些细节方面,现在一起来看看绘制流程图时都干了什么:
相信大家都注意到了图片中的红色框,配制一个 【candidateUsers】这样的参数,Camunda 就是根据此参数的值存 把任务与人的信息绑定并存储到 ACT_HI_IDENTITYLINK 表中
只是查询任务的时候,去查询一下 ACT_HI_IDENTITYLINK 表就完成任务列表的功能了。
/**
* 查询任务列表
*/
@Override
public List<WorkflowTaskEntity> getTodoTaskList(WorkflowQueryDto workflowQueryDto) {
QueryWrapper<WorkflowTaskEntity> queryWrapper = new QueryWrapper<>();
if(ObjectUtils.isNotEmpty(workflowQueryDto.getTaskId()) && StringUtils.isNotBlank(workflowQueryDto.getTaskId())) {
queryWrapper.eq("ID_", workflowQueryDto.getTaskId());
}
if(ObjectUtils.isNotEmpty(workflowQueryDto.getProcessDefinitionName()) && StringUtils.isNotBlank(workflowQueryDto.getProcessDefinitionName())) {
queryWrapper.eq("PROC_DEF_NAME_", workflowQueryDto.getProcessDefinitionName());
}
queryWrapper.or(ObjectUtils.isNotEmpty(workflowQueryDto.getOwner()),
qw -> qw.eq("OWNER_", workflowQueryDto.getOwner()));
queryWrapper.or(ObjectUtils.isNotEmpty(workflowQueryDto.getAssignee()),
qw -> qw.eq("ASSIGNEE_", workflowQueryDto.getAssignee()));
boolean isEmpty = StringUtils.isBlank(workflowQueryDto.getCandidateUsers())
&& StringUtils.isBlank(workflowQueryDto.getCandidateGroups());
if (!isEmpty){
queryWrapper.or(qw -> {
if(StringUtils.isNotBlank(workflowQueryDto.getCandidateUsers())) {
qw.or().exists(String.format("select ID_ from ACT_HI_IDENTITYLINK " +
"where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
"and USER_ID_ = '%s'", workflowQueryDto.getCandidateUsers()));
}
if (StringUtils.isNotBlank(workflowQueryDto.getCandidateGroups())) {
String groupIds = StringUtils.getSqlInStringByArray(workflowQueryDto.getCandidateGroups());
qw.or().exists(String.format("select ID_ from ACT_HI_IDENTITYLINK " +
"where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
"and GROUP_ID_ in (%s)", groupIds));
}
});
}
queryWrapper.orderByDesc("START_TIME_");
return workflowTaskMapper.listTodoTaskByPage(queryWrapper);
}
// 对应 Mapper 接口里的查询方法
@Select("SELECT * from (" +
" SELECT t1.*, t3.NAME_ procDefName FROM ACT_HI_TASKINST t1 " +
"left JOIN ACT_RE_PROCDEF t2 on t1.PROC_DEF_ID_ = t2.ID_ " +
"left join ACT_RE_DEPLOYMENT t3 on t3.ID_ = t2.DEPLOYMENT_ID_ "+
" )t ${ew.customSqlSegment}")
List<WorkflowTaskEntity> listCompleteTaskByPage(
@Param(Constants.WRAPPER) QueryWrapper<WorkflowTaskEntity> queryWrapper);
到这里完成了参数设置、任务展示,是不是功能就完了呢?
NO!
其实还有很重要的一步需要做,那就是在完成任务的时候?
这里留个疑问,大家可以结合下面的代码想一想,为什么?
@Override
public String completeTask(String taskId, WorkflowVariableDto dto) {
// 获取完成前任务
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if(ObjectUtils.isEmpty(task)){
return "没有查找到当前任务";
}
// 对共享任务的处理
if(StringUtils.isBlank(task.getAssignee())){
if(StringUtils.isBlank(dto.getVariables().get("assignee").toString())){
return"当前任务为共享任务,请先设置执行人";
}else {
claimTask(taskId, dto.getVariables().get("assignee").toString());
}
}
// 完成任务填写的备注
if(StringUtils.isNotBlank(dto.getReason()) && StringUtils.isNotBlank(task.getProcessInstanceId())){
taskService.createComment(task.getId(), task.getProcessInstanceId(), dto.getReason());
}
// 完成当前任务
taskService.complete(taskId, dto.getVariables());
return null;
}
// taskId 任务ID
// claimUser 认领用户ID
@Override
public void claimTask(String taskId, String claimUser) {
taskService.claim(taskId, claimUser);
}
OK,今天就写到这里,如果你使用过或者正在使用Camunda ,大家可以一起交流!!