mirror of
https://github.com/YunaiV/ruoyi-vue-pro.git
synced 2025-10-30 01:57:37 +08:00
!1395 feat: 工作流支持审批人撤回
Merge pull request !1395 from Lesan/feature/bpm-审批人撤回
This commit is contained in:
@ -97,6 +97,9 @@ public class BpmModelMetaInfoVO {
|
||||
@Schema(description = "任务后置通知设置", example = "{}")
|
||||
private HttpRequestSetting taskAfterTriggerSetting;
|
||||
|
||||
@Schema(description = "允许允许审批人撤回任务", example = "false")
|
||||
private Boolean allowWithdrawTask;
|
||||
|
||||
@Schema(description = "流程 ID 规则")
|
||||
@Data
|
||||
@Valid
|
||||
|
||||
@ -219,6 +219,14 @@ public class BpmTaskController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/withdraw")
|
||||
@Operation(summary = "撤回任务")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> withdrawTask(@RequestParam("taskId") String taskId) {
|
||||
taskService.withdrawTask(getLoginUserId(), taskId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-parent-task-id")
|
||||
@Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表
|
||||
@Parameter(name = "parentTaskId", description = "父级任务编号", required = true)
|
||||
|
||||
@ -219,4 +219,9 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting;
|
||||
|
||||
/**
|
||||
* 是否允许审批人撤回任务
|
||||
*/
|
||||
private Boolean allowWithdrawTask;
|
||||
|
||||
}
|
||||
|
||||
@ -61,6 +61,10 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
|
||||
ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!");
|
||||
ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!");
|
||||
ErrorCode TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING = new ErrorCode(1_009_005_017, "撤回失败,流程实例未运行!");
|
||||
ErrorCode TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS = new ErrorCode(1_009_005_018, "撤回失败,未查询到用户已办任务!");
|
||||
ErrorCode TASK_WITHDRAW_FAIL_NOT_ALLOW = new ErrorCode(1_009_005_019, "撤回失败,此流程不允许撤回操作!");
|
||||
ErrorCode TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW = new ErrorCode(1_009_005_019, "撤回失败,下一节点不满足撤回条件!");
|
||||
|
||||
// ========== 动态表单模块 1-009-010-000 ==========
|
||||
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
|
||||
|
||||
@ -35,6 +35,7 @@ public enum BpmReasonEnum {
|
||||
APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"),
|
||||
APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"),
|
||||
CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"),
|
||||
CANCEL_BY_WITHDRAW("前一任务撤回,系统自动取消"),
|
||||
;
|
||||
|
||||
private final String reason;
|
||||
|
||||
@ -908,6 +908,48 @@ public class BpmnModelUtils {
|
||||
return nextFlowNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找起始节点下一个用户任务列表列表
|
||||
* @param source 起始节点
|
||||
* @return 结果
|
||||
*/
|
||||
public static List<UserTask> getNextUserTasks(FlowElement source) {
|
||||
return getNextUserTasks(source, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找起始节点下一个用户任务列表列表
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 用户任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static List<UserTask> getNextUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>());
|
||||
userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>());
|
||||
// 获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (!sequenceFlows.isEmpty()) {
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
|
||||
if (targetFlowElement instanceof UserTask) {
|
||||
// 若节点为用户任务,加入到结果列表中
|
||||
userTaskList.add((UserTask) targetFlowElement);
|
||||
} else {
|
||||
// 若节点非用户任务,继续递归查找下一个节点
|
||||
getNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
}
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理排它网关
|
||||
*
|
||||
|
||||
@ -250,6 +250,14 @@ public interface BpmTaskService {
|
||||
*/
|
||||
void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 撤回任务
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param taskId 任务编号
|
||||
*/
|
||||
void withdrawTask(Long userId, String taskId);
|
||||
|
||||
// ========== Event 事件相关方法 ==========
|
||||
|
||||
/**
|
||||
|
||||
@ -40,10 +40,9 @@ import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.ManagementService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.common.engine.api.FlowableException;
|
||||
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
|
||||
import org.flowable.engine.*;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.runtime.ActivityInstance;
|
||||
import org.flowable.engine.runtime.Execution;
|
||||
@ -62,6 +61,7 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -599,7 +599,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
|
||||
/**
|
||||
* 校验选择的下一个节点的审批人,是否合法
|
||||
*
|
||||
* <p>
|
||||
* 1. 是否有漏选:没有选择审批人
|
||||
* 2. 是否有多选:非下一个节点
|
||||
*
|
||||
@ -1177,6 +1177,70 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void withdrawTask(Long userId, String taskId) {
|
||||
// 1.查询本人已办任务
|
||||
HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery()
|
||||
.taskId(taskId).taskAssignee(userId.toString()).finished().singleResult();
|
||||
if (ObjectUtil.isNull(taskInstance)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS);
|
||||
}
|
||||
// 2.校验流程是否结束
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceId(taskInstance.getProcessInstanceId())
|
||||
.active()
|
||||
.singleResult();
|
||||
if (ObjectUtil.isNull(processInstance)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING);
|
||||
}
|
||||
// 3.判断此流程是否允许撤回
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService
|
||||
.getProcessDefinitionInfo(processInstance.getProcessDefinitionId());
|
||||
if (ObjectUtil.isNull(processDefinitionInfo) || !Boolean.TRUE.equals(processDefinitionInfo.getAllowWithdrawTask())) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NOT_ALLOW);
|
||||
}
|
||||
// 4.判断此任务下一节点是否满足撤回
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskInstance.getProcessDefinitionId());
|
||||
UserTask userTask = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, taskInstance.getTaskDefinitionKey());
|
||||
List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(userTask);
|
||||
List<String> nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).toList();
|
||||
if (CollUtil.isEmpty(nextUserTaskKeys)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
long nextUserTaskFinishedCount = historyService.createHistoricTaskInstanceQuery()
|
||||
.processInstanceId(processInstance.getProcessInstanceId())
|
||||
.taskDefinitionKeys(nextUserTaskKeys)
|
||||
.taskCreatedAfter(taskInstance.getEndTime()) // TODO @芋艿:是否选择升级flowable版本解决taskCreatedAfter、taskCreatedBefore问题,升级7.1.0可以
|
||||
.finished()
|
||||
.count();
|
||||
if (nextUserTaskFinishedCount > 0) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
// 5.获取需要撤回的运行任务
|
||||
List<Task> runningTaskList = taskService.createTaskQuery()
|
||||
.processInstanceId(processInstance.getProcessInstanceId())
|
||||
.taskDefinitionKeys(nextUserTaskKeys)
|
||||
.active().list();
|
||||
if (CollUtil.isEmpty(runningTaskList)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
List<String> withdrawExecutionIds = new ArrayList<>();
|
||||
for (Task task : runningTaskList) {
|
||||
// 标记撤回任务为取消
|
||||
// TODO @芋艿:是否需要添加被撤回状态?
|
||||
taskService.addComment(task.getId(), taskInstance.getProcessInstanceId(), BpmCommentTypeEnum.CANCEL.getType(),
|
||||
BpmCommentTypeEnum.CANCEL.formatComment("前一节点撤回"));
|
||||
updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_WITHDRAW.getReason());
|
||||
withdrawExecutionIds.add(task.getExecutionId());
|
||||
}
|
||||
// 6.执行撤回操作
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(processInstance.getProcessInstanceId())
|
||||
.moveExecutionsToSingleActivityId(withdrawExecutionIds, taskInstance.getTaskDefinitionKey())
|
||||
.changeState();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验任务是否能被减签
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user