Merge branch 'master' into feature/bpm-delegate

# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
This commit is contained in:
youkehai
2023-09-25 09:15:59 +08:00
19 changed files with 501 additions and 517 deletions

View File

@ -78,14 +78,14 @@ public class BpmTaskController {
@GetMapping("/get-return-list")
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
@Parameter(name = "taskId", description = "当前任务ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:return')")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
return success(taskService.getReturnTaskList(taskId));
}
@PutMapping("/return")
@Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:return')")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
taskService.returnTask(reqVO);
return success(true);

View File

@ -14,10 +14,11 @@ public class BpmTaskReturnReqVO {
private String id;
@Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "回退到的任务Key不能为空")
@NotEmpty(message = "回退到的任务 Key 不能为空")
private String targetDefinitionKey;
@Schema(description = "回退意见", example = "")
@Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
@NotEmpty(message = "回退意见不能为空")
private String reason;
}

View File

@ -3,10 +3,7 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
*
*/
@Schema(description = "管理后台 - 流程任务的 可回退的节点 Response VO")
@Schema(description = "管理后台 - 流程任务的精简 Response VO")
@Data
public class BpmTaskSimpleRespVO {
@ -15,4 +12,5 @@ public class BpmTaskSimpleRespVO {
@Schema(description = "任务名词", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批")
private String name;
}

View File

@ -140,8 +140,7 @@ public interface BpmTaskConvert {
return reqDTO;
}
@Mapping(source = "taskDefinitionKey", target = "id")
default List<BpmTaskSimpleRespVO> convertList(List<FlowElement> elementList) {
default List<BpmTaskSimpleRespVO> convertList(List<? extends FlowElement> elementList) {
return CollectionUtils.convertList(elementList, element -> new BpmTaskSimpleRespVO()
.setName(element.getName())
.setDefinitionKey(element.getId()));

View File

@ -5,7 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
@ -200,7 +200,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
// 对比字节变化
if (!FlowableUtils.equals(oldModel, newModel)) {
if (!BpmnModelUtils.equals(oldModel, newModel)) {
return false;
}
// 最终发现都一致,则返回 true

View File

@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
@ -114,7 +114,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
return Collections.emptyList();
}
// 获得用户任务,只有用户任务才可以设置分配规则
List<UserTask> userTasks = FlowableUtils.getBpmnModelElements(model, UserTask.class);
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(model, UserTask.class);
if (CollUtil.isEmpty(userTasks)) {
return Collections.emptyList();
}

View File

@ -125,7 +125,7 @@ public interface BpmTaskService {
/**
* 获取当前任务的可回退的流程集合
*
* @param taskId 当前的任务ID
* @param taskId 当前的任务 ID
* @return 可以回退的节点列表
*/
List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId);

View File

@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.flowable.core.util.ModelUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
@ -65,22 +65,23 @@ public class BpmTaskServiceImpl implements BpmTaskService {
private TaskService taskService;
@Resource
private HistoryService historyService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmModelService bpmModelService;
@Resource
private BpmMessageService messageService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmTaskExtMapper taskExtMapper;
@Resource
private BpmMessageService messageService;
@Resource
private BpmModelService bpmModelService;
@Resource
private RuntimeService runtimeService;
@Override
public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
@ -359,144 +360,127 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return task;
}
private HistoricTaskInstance getHistoricTask(String id) {
return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
}
@Override
public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) {
/**
* 校验任务是否合法
*
* @param taskId 任务编号
* @return 任务
*/
private Task validateTask(String taskId) {
// 当前任务 task
Task task = getTask(taskId);
// 根据流程定义获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
// 查询该任务的前置任务节点的key集合
Set<String> historyTaksDefinitionKeySet = getPreTaskByCurrentTask(task, bpmnModel);
if (CollUtil.isEmpty(historyTaksDefinitionKeySet)) {
return Collections.emptyList();
if (task == null) {
throw exception(TASK_NOT_EXISTS);
}
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
List<FlowElement> elementList = new ArrayList<>();
for (String activityId : historyTaksDefinitionKeySet) {
FlowElement target = ModelUtils.getFlowElementById(bpmnModel, activityId);
//非 串行和子流程则加入返回节点 elementList
boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());
if (isSequential) {
elementList.add(target);
}
}
return BpmTaskConvert.INSTANCE.convertList(elementList);
}
/**
* 查询当前流程实例符合条件可回退的 taskDefinitionKey 集合
*
* @param task 当前任务
* @param bpmnModel 当前流程定义对应的流程模型
* @return 符合条件的已去重的 taskDefinitionKey集合
*/
private Set<String> getPreTaskByCurrentTask(Task task, BpmnModel bpmnModel) {
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
//拿到当前任务节点的前置节点集合
List<UserTask> preUserTaskList = ModelUtils.getPreUserTaskList(source, null, null);
//需要保证这些节点都是在该source节点之前的
if (CollUtil.isNotEmpty(preUserTaskList)) {
return convertSet(preUserTaskList, UserTask::getId);
}
return Collections.emptySet();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void returnTask(BpmTaskReturnReqVO reqVO) {
// 当前任务 task
Task task = validateReturnTask(reqVO.getId());
// 校验源头和目标节点的关系,并返回目标元素
FlowElement targetElement = validateReturnProcessDefinition(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());
//调用flowable框架的回退逻辑
this.handlerReturn(task, targetElement, reqVO);
// 更新任务扩展表
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.BACK.getResult())
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
}
/**
* 校验当前流程是否可以操作回退
*
* @param taskId 当前任务ID
* @return
*/
private Task validateReturnTask(String taskId) {
// 当前任务 task
Task task = getTask(taskId);
if (task.isSuspended()) {
throw exception(TASK_IS_PENDING);
}
return task;
}
private HistoricTaskInstance getHistoricTask(String id) {
return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
}
@Override
public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) {
// 1. 校验当前任务 task 存在
Task task = getTask(taskId);
if (task == null) {
throw exception(TASK_NOT_EXISTS);
}
// 根据流程定义获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
if (source == null) {
throw exception(TASK_NOT_EXISTS);
}
// 2.1 查询该任务的前置任务节点的 key 集合
List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);
if (CollUtil.isEmpty(previousUserList)) {
return Collections.emptyList();
}
// 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
return BpmTaskConvert.INSTANCE.convertList(previousUserList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void returnTask(BpmTaskReturnReqVO reqVO) {
// 1.1 当前任务 task
Task task = validateTask(reqVO.getId());
// 1.2 校验源头和目标节点的关系,并返回目标元素
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());
// 2. 调用 flowable 框架的回退逻辑
returnTask0(task, targetElement, reqVO);
// 3. 更新任务扩展表
taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
.setResult(BpmProcessInstanceResultEnum.BACK.getResult())
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
}
/**
* 回退流程节点时,校验源头节点和目标节点的关系
* 回退流程节点时,校验目标任务节点是否可回退
*
* @param sourceKey 当前任务节点Key
* @param targetKey 目标任务节点key
* @param processDefinitionId 当前流程定义ID
* @return 目标元素
* @param sourceKey 当前任务节点 Key
* @param targetKey 目标任务节点 key
* @param processDefinitionId 当前流程定义 ID
* @return 目标任务节点元素
*/
private FlowElement validateReturnProcessDefinition(String sourceKey, String targetKey, String processDefinitionId) {
// 获取流程模型信息
private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
// 1.1 获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey);
// 获取跳转的节点元素
FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey);
if (null == target) {
// 1.3 获取当前任务节点元素
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
// 1.3 获取跳转的节点元素
FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey);
if (target == null) {
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
}
// 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转
boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());
if (!isSequential) {
throw exception(TASK_SOURCE_TARGET_ERROR);
// 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
}
return target;
}
/**
* 处理回退逻辑
* 执行回退逻辑
*
* @param task 当前回退的任务
* @param currentTask 当前回退的任务
* @param targetElement 需要回退到的目标任务
* @param reqVO 前端参数封装
*/
public void handlerReturn(Task task, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
// 获取所有正常进行的任务节点 Key这些任务不能直接使用需要找出其中需要撤回的任务
List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> runTaskKeyList = convertList(runTaskList, Task::getTaskDefinitionKey);
// 通过 targetElement 的出口连线,结合 runTaskList 比对,获取需要撤回的任务 key
List<UserTask> returnUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
// 需退回任务 key
List<String> returnTaskDefinitionKeyList = convertList(returnUserTaskList, UserTask::getId);
public void returnTask0(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
// 1. 获得所有需要回撤的任务 taskDefinitionKey用于稍后的 moveActivityIdsToSingleActivityId 回撤
// 1.1 获取所有正常进行的任务节点 Key
List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey);
// 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回
// 为什么不直接使用 runTaskKeyList 呢因为可能存在多个审批分支例如说A -> B -> C 和 D -> F而只要 C 撤回到 A需要排除掉 F
List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
// 通过 key 和 runTaskList 中的key对比拿到任务ID设置设置回退意见
List<String> currentTaskIds = new ArrayList<>();
returnTaskDefinitionKeyList.forEach(currentId -> runTaskList.forEach(runTask -> {
if (currentId.equals(runTask.getTaskDefinitionKey())) {
currentTaskIds.add(runTask.getId());
// 2. 给当前要被回退的 task 数组,设置回退意见
taskList.forEach(task -> {
// 需要排除掉,不需要设置回退意见的任务
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
return;
}
}));
if (CollUtil.isEmpty(currentTaskIds)) {
throw exception(TASK_RETURN_FAIL);
}
// 设置回退意见
for (String currentTaskId : currentTaskIds) {
taskService.addComment(currentTaskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());
}
// 1 对 1 或 多 对 1 情况currentIds 当前要跳转的节点列表(1或多)targetKey 跳转到的节点(1)
taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());
});
// 3. 执行驳回
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(returnTaskDefinitionKeyList, reqVO.getTargetDefinitionKey())
.processInstanceId(currentTask.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
reqVO.getTargetDefinitionKey()) // targetKey 跳转到的节点(1)
.changeState();
}