【同步】BOOT 和 CLOUD 的功能
This commit is contained in:
@@ -72,6 +72,9 @@ public class BpmModelMetaInfoVO {
|
||||
@Schema(description = "允许撤销审批中的申请", example = "true")
|
||||
private Boolean allowCancelRunningProcess;
|
||||
|
||||
@Schema(description = "允许允许审批人撤回任务", example = "false")
|
||||
private Boolean allowWithdrawTask;
|
||||
|
||||
@Schema(description = "流程 ID 规则", example = "{}")
|
||||
private ProcessIdRule processIdRule;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -172,6 +172,11 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
|
||||
*/
|
||||
private Boolean allowCancelRunningProcess;
|
||||
|
||||
/**
|
||||
* 是否允许审批人撤回任务
|
||||
*/
|
||||
private Boolean allowWithdrawTask;
|
||||
|
||||
/**
|
||||
* 流程 ID 规则
|
||||
*/
|
||||
|
||||
@@ -797,9 +797,9 @@ public class BpmnModelUtils {
|
||||
|
||||
// 情况:StartEvent/EndEvent/UserTask/ServiceTask
|
||||
if (currentElement instanceof StartEvent
|
||||
|| currentElement instanceof EndEvent
|
||||
|| currentElement instanceof UserTask
|
||||
|| currentElement instanceof ServiceTask) {
|
||||
|| currentElement instanceof EndEvent
|
||||
|| currentElement instanceof UserTask
|
||||
|| currentElement instanceof ServiceTask) {
|
||||
// 添加节点
|
||||
FlowNode flowNode = (FlowNode) currentElement;
|
||||
resultElements.add(flowNode);
|
||||
@@ -908,6 +908,49 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理排它网关
|
||||
*
|
||||
@@ -938,8 +981,8 @@ public class BpmnModelUtils {
|
||||
*/
|
||||
private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) {
|
||||
SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||
&& (evalConditionExpress(variables, flow.getConditionExpression())));
|
||||
flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
|
||||
&& (evalConditionExpress(variables, flow.getConditionExpression())));
|
||||
if (matchSequenceFlow == null) {
|
||||
matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
|
||||
flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
|
||||
|
||||
@@ -67,7 +67,6 @@ import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
|
||||
@@ -221,11 +220,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
|
||||
processDefinitionInfo,
|
||||
processVariables, activities);
|
||||
// 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点
|
||||
if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) {
|
||||
simulateActivityNodes.removeIf(node ->
|
||||
BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()));
|
||||
}
|
||||
|
||||
// 4. 拼接最终数据
|
||||
return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
|
||||
@@ -415,7 +409,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
endActivities.forEach(activity -> {
|
||||
// StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点
|
||||
if (ELEMENT_EVENT_START.equals(activity.getActivityType())
|
||||
&& BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) {
|
||||
&& BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())
|
||||
&& !CollUtil.contains(activities, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
|
||||
historicActivity -> historicActivity.getActivityId().equals(START_USER_NODE_ID))) {
|
||||
ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID)
|
||||
.setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus());
|
||||
ActivityNode startNode = new ActivityNode().setId(startTask.getId())
|
||||
@@ -555,7 +551,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
// 情况一:BPMN 设计器
|
||||
if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
|
||||
List<FlowElement> flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables);
|
||||
return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel,
|
||||
return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(
|
||||
startUserId, bpmnModel, flowElements,
|
||||
processDefinitionInfo, processVariables, flowElement, runActivityIds));
|
||||
}
|
||||
// 情况二:SIMPLE 设计器
|
||||
@@ -563,7 +560,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),
|
||||
BpmSimpleModelNodeVO.class);
|
||||
List<BpmSimpleModelNodeVO> simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);
|
||||
return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel,
|
||||
return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(
|
||||
startUserId, bpmnModel,
|
||||
processDefinitionInfo, processVariables, simpleNode, runActivityIds));
|
||||
}
|
||||
throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType());
|
||||
@@ -618,8 +616,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
|
||||
private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, List<FlowElement> flowElements,
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo,
|
||||
Map<String, Object> processVariables,
|
||||
FlowElement node, Set<String> runActivityIds) {
|
||||
if (runActivityIds.contains(node.getId())) {
|
||||
return null;
|
||||
@@ -634,6 +633,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
|
||||
|
||||
// 1. 开始节点
|
||||
if (node instanceof StartEvent) {
|
||||
if (CollUtil.contains(flowElements, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
|
||||
flowElement -> flowElement.getId().equals(START_USER_NODE_ID))) {
|
||||
return null;
|
||||
}
|
||||
return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
|
||||
.setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType());
|
||||
}
|
||||
|
||||
@@ -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 事件相关方法 ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -196,7 +196,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
/**
|
||||
* 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userId 用户编号
|
||||
* @param processInstanceId 流程编号
|
||||
* @return 任务
|
||||
*/
|
||||
@@ -599,15 +599,15 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
|
||||
/**
|
||||
* 校验选择的下一个节点的审批人,是否合法
|
||||
*
|
||||
* <p>
|
||||
* 1. 是否有漏选:没有选择审批人
|
||||
* 2. 是否有多选:非下一个节点
|
||||
*
|
||||
* @param taskDefinitionKey 当前任务节点标识
|
||||
* @param variables 流程变量
|
||||
* @param bpmnModel 流程模型
|
||||
* @param nextAssignees 下一个节点审批人集合(参数)
|
||||
* @param processInstance 流程实例
|
||||
* @param variables 流程变量
|
||||
* @param bpmnModel 流程模型
|
||||
* @param nextAssignees 下一个节点审批人集合(参数)
|
||||
* @param processInstance 流程实例
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel,
|
||||
@@ -659,7 +659,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
approveUserSelectAssignees = new HashMap<>();
|
||||
}
|
||||
approveUserSelectAssignees.put(nextFlowNode.getId(), assignees);
|
||||
Map<String,List<Long>> existingApproveUserSelectAssignees = (Map<String,List<Long>>) variables.get(
|
||||
Map<String, List<Long>> existingApproveUserSelectAssignees = (Map<String, List<Long>>) variables.get(
|
||||
BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);
|
||||
if (CollUtil.isNotEmpty(existingApproveUserSelectAssignees)) {
|
||||
approveUserSelectAssignees.putAll(existingApproveUserSelectAssignees);
|
||||
@@ -1177,6 +1177,63 @@ 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.1 查询本人已办任务
|
||||
HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery()
|
||||
.taskId(taskId).taskAssignee(userId.toString()).finished().singleResult();
|
||||
if (ObjUtil.isNull(taskInstance)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS);
|
||||
}
|
||||
// 1.2 校验流程是否结束
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(taskInstance.getProcessInstanceId());
|
||||
if (ObjUtil.isNull(processInstance)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING);
|
||||
}
|
||||
// 1.3 判断此流程是否允许撤回
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(
|
||||
processInstance.getProcessDefinitionId());
|
||||
if (ObjUtil.isNull(processDefinitionInfo) || !Boolean.TRUE.equals(processDefinitionInfo.getAllowWithdrawTask())) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NOT_ALLOW);
|
||||
}
|
||||
// 1.4 判断下一个节点是否被审批过,如果是则无法撤回
|
||||
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskInstance.getProcessDefinitionId());
|
||||
UserTask userTask = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, taskInstance.getTaskDefinitionKey());
|
||||
List<String> nextUserTaskKeys = convertList(BpmnModelUtils.getNextUserTasks(userTask), UserTask::getId);
|
||||
if (CollUtil.isEmpty(nextUserTaskKeys)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
// TODO @芋艿:是否选择升级flowable版本解决taskCreatedAfter、taskCreatedBefore问题,升级7.1.0可以;包括 todo 和 done 那边的查询哇??? 是的!
|
||||
long nextUserTaskFinishedCount = historyService.createHistoricTaskInstanceQuery()
|
||||
.processInstanceId(processInstance.getProcessInstanceId()).taskDefinitionKeys(nextUserTaskKeys)
|
||||
.taskCreatedAfter(taskInstance.getEndTime()).finished().count();
|
||||
if (nextUserTaskFinishedCount > 0) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
// 1.5 获取需要撤回的运行任务
|
||||
List<Task> runningTasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId())
|
||||
.taskDefinitionKeys(nextUserTaskKeys).active().list();
|
||||
if (CollUtil.isEmpty(runningTasks)) {
|
||||
throw exception(TASK_WITHDRAW_FAIL_NEXT_TASK_NOT_ALLOW);
|
||||
}
|
||||
|
||||
// 2.1 取消当前任务
|
||||
List<String> withdrawExecutionIds = new ArrayList<>();
|
||||
for (Task task : runningTasks) {
|
||||
// 标记撤回任务为取消
|
||||
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());
|
||||
}
|
||||
// 2.2 执行撤回操作
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(processInstance.getProcessInstanceId())
|
||||
.moveExecutionsToSingleActivityId(withdrawExecutionIds, taskInstance.getTaskDefinitionKey())
|
||||
.changeState();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验任务是否能被减签
|
||||
*
|
||||
@@ -1223,7 +1280,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
}
|
||||
|
||||
// 2. 任务前置通知
|
||||
if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())){
|
||||
if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())) {
|
||||
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting();
|
||||
BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,
|
||||
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
|
||||
@@ -1350,7 +1407,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
.taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
|
||||
.finished();
|
||||
if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
|
||||
&& sameAssigneeQuery.count() > 0) {
|
||||
&& sameAssigneeQuery.count() > 0) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
|
||||
.setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
|
||||
return;
|
||||
@@ -1362,7 +1419,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
return;
|
||||
}
|
||||
List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点
|
||||
BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
|
||||
BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
|
||||
SequenceFlow::getSourceRef);
|
||||
if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
|
||||
@@ -1387,7 +1444,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
|
||||
if (userTaskElement.getId().equals(START_USER_NODE_ID)
|
||||
&& (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
|
||||
|| BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
|
||||
|| BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
|
||||
&& ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
|
||||
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
|
||||
.setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
|
||||
@@ -1456,7 +1513,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
}
|
||||
|
||||
// 任务后置通知
|
||||
if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())){
|
||||
if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())) {
|
||||
BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting();
|
||||
BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,
|
||||
setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
|
||||
|
||||
Reference in New Issue
Block a user