diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index c9da0718df..44cba2bd15 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,6 +30,9 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), + CHILD_PROCESS(20, "子流程", "callActivity"), + ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), + // 50 ~ 条件分支 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java index d41c0c3133..bb6b244b02 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java @@ -26,6 +26,7 @@ public enum BpmReasonEnum { TIMEOUT_REJECT("审批超时,系统自动不通过"), ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 50c45a96a2..23371b19d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -10,6 +10,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.flowable.bpmn.model.IOParameter; import org.hibernate.validator.constraints.URL; import java.util.List; @@ -122,6 +123,8 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + private ChildProcessSetting childProcessSetting; + @Schema(description = "任务监听器") @Valid @Data @@ -397,4 +400,33 @@ public class BpmSimpleModelNodeVO { private Map updateFormFields; } } + + @Schema(description = "子流程节点配置") + @Data + @Valid + public static class ChildProcessSetting { + + @Schema(description = "被调用流程", example = "xxx") + @NotEmpty(message = "被调用流程不能为空") + private String calledElement; + + @Schema(description = "被调用流程名称", example = "xxx") + @NotEmpty(message = "被调用流程名称不能为空") + private String calledElementName; + + @Schema(description = "是否异步", example = "false") + @NotNull(message = "是否异步不能为空") + private Boolean async; + + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariable; + + @Schema(description = "输出参数(子->主)", example = "[]") + private List outVariable; + + @Schema(description = "是否自动跳过子流程发起节点", example = "false") + @NotNull(message = "是否自动跳过子流程发起节点不能为空") + private Boolean skipStartUserNode; + + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 8172cf59a5..119a441857 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -51,6 +51,13 @@ public class BpmnVariableConstants { */ public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + /** + * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; + /** * 流程实例的变量 - 流程开始时间 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e32d93ceb0..d41fc44242 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -22,6 +22,8 @@ import org.springframework.util.MultiValueMap; import java.util.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; import static java.util.Arrays.asList; @@ -42,7 +44,8 @@ public class SimpleModelUtils { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), - new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), + new ChildProcessConvert(), new AsyncChildProcessConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); } @@ -773,6 +776,66 @@ public class SimpleModelUtils { } + private static class ChildProcessConvert implements NodeConvert { + + @Override + public CallActivity convert(BpmSimpleModelNodeVO node) { + BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); + List inVariable = childProcessSetting.getInVariable() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); + CallActivity callActivity = new CallActivity(); + callActivity.setId(node.getId()); + callActivity.setName(node.getName()); + callActivity.setCalledElementType("key"); + // 1. 是否异步 + callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledElement()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); + // 3. 是否自动跳过子流程发起节点 + if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("true"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } else { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("false"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } + // 4. 主→子变量传递 + // 默认需要传递的一些变量 + // 4.1 流程状态 + IOParameter ioParameter = new IOParameter(); + ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS); + inVariable.add(ioParameter); + callActivity.setInParameters(inVariable); + // 5. 子→主变量传递 + if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { + callActivity.setOutParameters(childProcessSetting.getOutVariable()); + } + return callActivity; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; + } + + } + + private static class AsyncChildProcessConvert extends ChildProcessConvert { + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS; + } + + } + + private static String buildGatewayJoinId(String id) { return id + "_join"; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f57f088ef5..e104141965 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.*; import cn.hutool.extra.spring.SpringUtil; @@ -63,6 +64,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. 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; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; /** @@ -1212,19 +1214,30 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } - // 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 - Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + // 发起人节点 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); + return; + } + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 + // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), + PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + if (userTaskElement.getId().equals(START_USER_NODE_ID) && + (skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) && + !Boolean.TRUE.equals(returnTaskFlag)) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); + return; + } + // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 + if (!userTaskElement.getId().equals(START_USER_NODE_ID) && + StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); - return; - } - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); // 情况一:自动跳过