Commit 5679b4ef95b8a62ceeeef3256353239c04ef9e78
1 parent
3a433e82
仿钉钉流程设计- 新增发起人节点
Showing
6 changed files
with
128 additions
and
66 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java
| @@ -19,18 +19,20 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { | @@ -19,18 +19,20 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { | ||
| 19 | 19 | ||
| 20 | // TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批; | 20 | // TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批; |
| 21 | // @芋艿 感觉还是用 START_NODE . END_NODE 比较好. | 21 | // @芋艿 感觉还是用 START_NODE . END_NODE 比较好. |
| 22 | + // 0 1 开始和结束 | ||
| 22 | START_NODE(0, "开始节点"), | 23 | START_NODE(0, "开始节点"), |
| 23 | - END_NODE(-2, "结束节点"), // TODO @jaosn:挪到 START_EVENT_NODE 后; | 24 | + END_NODE(1, "结束节点"), // TODO @jaosn:挪到 START_EVENT_NODE 后; |
| 24 | 25 | ||
| 25 | - APPROVE_NODE(1, "审批人节点"), // TODO @jaosn:是不是这里从 10 开始好点;相当于说,0-9 给开始和结束;10-19 给各种节点;20-29 给各种条件; TODO 后面改改 | ||
| 26 | - COPY_NODE(2, "抄送人节点"), | 26 | + // 10 ~ 49 各种节点 |
| 27 | + START_USER_NODE(10, "发起人节点"), // 发起人节点。前端的开始节点,Id 固定 | ||
| 28 | + APPROVE_NODE(11, "审批人节点"), // TODO @jaosn:是不是这里从 10 开始好点;相当于说,0-9 给开始和结束;10-19 给各种节点;20-29 给各种条件; TODO 后面改改 | ||
| 29 | + COPY_NODE(12, "抄送人节点"), | ||
| 27 | 30 | ||
| 28 | - CONDITION_NODE(3, "条件节点"), // 用于构建流转条件的表达式 | ||
| 29 | - CONDITION_BRANCH_NODE(4, "条件分支节点"), // TODO @jason:是不是改成叫 条件分支? | ||
| 30 | - PARALLEL_BRANCH_NODE(5, "并行分支节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关 | ||
| 31 | -// PARALLEL_BRANCH_JOIN_NODE(6, "并行分支聚合节点"), | ||
| 32 | - INCLUSIVE_BRANCH_FORK_NODE(7, "包容网关分叉节点"), | ||
| 33 | - INCLUSIVE_BRANCH_JOIN_NODE(8, "包容网关聚合节点"), | 31 | + // 50 ~ 条件分支 |
| 32 | + CONDITION_NODE(50, "条件节点"), // 用于构建流转条件的表达式 | ||
| 33 | + CONDITION_BRANCH_NODE(51, "条件分支节点"), // TODO @jason:是不是改成叫 条件分支? | ||
| 34 | + PARALLEL_BRANCH_NODE(52, "并行分支节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关 | ||
| 35 | + INCLUSIVE_BRANCH_NODE(53, "包容分叉节点"), | ||
| 34 | // TODO @jason:建议整合 join,最终只有 条件分支、并行分支、包容分支,三种~ | 36 | // TODO @jason:建议整合 join,最终只有 条件分支、并行分支、包容分支,三种~ |
| 35 | // TODO @芋艿。 感觉还是分开好理解一点,也好处理一点。前端结构中把聚合节点显示并传过来。 | 37 | // TODO @芋艿。 感觉还是分开好理解一点,也好处理一点。前端结构中把聚合节点显示并传过来。 |
| 36 | ; | 38 | ; |
| @@ -48,7 +50,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { | @@ -48,7 +50,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { | ||
| 48 | public static boolean isBranchNode(Integer type) { | 50 | public static boolean isBranchNode(Integer type) { |
| 49 | return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) | 51 | return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) |
| 50 | || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) | 52 | || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) |
| 51 | - || Objects.equals(INCLUSIVE_BRANCH_FORK_NODE.getType(), type) ; | 53 | + || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type) ; |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | public static BpmSimpleModelNodeType valueOf(Integer type) { | 56 | public static BpmSimpleModelNodeType valueOf(Integer type) { |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderMultiStrategy.java
| 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 5 | import cn.iocoder.yudao.framework.common.util.string.StrUtils; | 4 | import cn.iocoder.yudao.framework.common.util.string.StrUtils; |
| 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; |
| @@ -9,7 +8,6 @@ import cn.iocoder.yudao.module.system.api.dept.DeptApi; | @@ -9,7 +8,6 @@ import cn.iocoder.yudao.module.system.api.dept.DeptApi; | ||
| 9 | import org.flowable.engine.delegate.DelegateExecution; | 8 | import org.flowable.engine.delegate.DelegateExecution; |
| 10 | import org.springframework.stereotype.Component; | 9 | import org.springframework.stereotype.Component; |
| 11 | 10 | ||
| 12 | -import java.util.List; | ||
| 13 | import java.util.Set; | 11 | import java.util.Set; |
| 14 | 12 | ||
| 15 | /** | 13 | /** |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| @@ -99,4 +99,14 @@ public interface BpmnModelConstants { | @@ -99,4 +99,14 @@ public interface BpmnModelConstants { | ||
| 99 | */ | 99 | */ |
| 100 | String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; | 100 | String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; |
| 101 | 101 | ||
| 102 | + /** | ||
| 103 | + * BPMN Start Event Node Id | ||
| 104 | + */ | ||
| 105 | + String START_EVENT_NODE_ID = "StartEvent"; | ||
| 106 | + | ||
| 107 | + /** | ||
| 108 | + * BPMN Start Event Node Name | ||
| 109 | + */ | ||
| 110 | + String START_EVENT_NODE_NAME = "开始"; | ||
| 111 | + | ||
| 102 | } | 112 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
| @@ -31,6 +31,12 @@ public class BpmnVariableConstants { | @@ -31,6 +31,12 @@ public class BpmnVariableConstants { | ||
| 31 | public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; | 31 | public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; |
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | + * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} | ||
| 35 | + * | ||
| 36 | + * @see ProcessInstance#getProcessVariables() | ||
| 37 | + */ | ||
| 38 | + public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; | ||
| 39 | + /** | ||
| 34 | * 任务的变量 - 状态 | 40 | * 任务的变量 - 状态 |
| 35 | * | 41 | * |
| 36 | * @see org.flowable.task.api.Task#getTaskLocalVariables() | 42 | * @see org.flowable.task.api.Task#getTaskLocalVariables() |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| @@ -9,7 +9,10 @@ import cn.hutool.core.util.*; | @@ -9,7 +9,10 @@ import cn.hutool.core.util.*; | ||
| 9 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 9 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 11 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler; | 11 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler; |
| 12 | -import cn.iocoder.yudao.module.bpm.enums.definition.*; | 12 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; |
| 13 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; | ||
| 14 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | ||
| 15 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum; | ||
| 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 16 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; | 17 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; |
| 15 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelConditionGroups; | 18 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelConditionGroups; |
| @@ -17,12 +20,18 @@ import org.flowable.bpmn.BpmnAutoLayout; | @@ -17,12 +20,18 @@ import org.flowable.bpmn.BpmnAutoLayout; | ||
| 17 | import org.flowable.bpmn.model.Process; | 20 | import org.flowable.bpmn.model.Process; |
| 18 | import org.flowable.bpmn.model.*; | 21 | import org.flowable.bpmn.model.*; |
| 19 | 22 | ||
| 20 | -import java.util.*; | 23 | +import java.util.ArrayList; |
| 24 | +import java.util.List; | ||
| 25 | +import java.util.Map; | ||
| 26 | +import java.util.Objects; | ||
| 21 | 27 | ||
| 22 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; | 28 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; |
| 23 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; | 29 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; |
| 24 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; | 30 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; |
| 31 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER; | ||
| 32 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP; | ||
| 25 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER; | 33 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER; |
| 34 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER; | ||
| 26 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | 35 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 27 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; | 36 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; |
| 28 | import static org.flowable.bpmn.constants.BpmnXMLConstants.*; | 37 | import static org.flowable.bpmn.constants.BpmnXMLConstants.*; |
| @@ -76,19 +85,29 @@ public class SimpleModelUtils { | @@ -76,19 +85,29 @@ public class SimpleModelUtils { | ||
| 76 | process.setExecutable(Boolean.TRUE); // TODO @jason:这个是必须设置的么? | 85 | process.setExecutable(Boolean.TRUE); // TODO @jason:这个是必须设置的么? |
| 77 | bpmnModel.addProcess(process); | 86 | bpmnModel.addProcess(process); |
| 78 | 87 | ||
| 79 | - // 前端模型数据结构 | ||
| 80 | - // 从 SimpleModel 构建 FlowNode 并添加到 Main Process | ||
| 81 | - traverseNodeToBuildFlowNode(simpleModelNode, process); | 88 | + // 目前前端的第一个节点是 发起人节点。这里构建一个StartNode. 用于创建 Bpmn 的 StartEvent 节点 |
| 89 | + BpmSimpleModelNodeVO startNode = buildStartSimpleModelNode(); | ||
| 90 | + startNode.setChildNode(simpleModelNode); | ||
| 91 | + // 从 前端模型数据结构 SimpleModel 构建 FlowNode 并添加到 Main Process | ||
| 92 | + traverseNodeToBuildFlowNode(startNode, process); | ||
| 82 | // 找到 end event | 93 | // 找到 end event |
| 83 | EndEvent endEvent = (EndEvent) CollUtil.findOne(process.getFlowElements(), item -> item instanceof EndEvent); | 94 | EndEvent endEvent = (EndEvent) CollUtil.findOne(process.getFlowElements(), item -> item instanceof EndEvent); |
| 84 | 95 | ||
| 85 | // 构建并添加节点之间的连线 Sequence Flow | 96 | // 构建并添加节点之间的连线 Sequence Flow |
| 86 | - traverseNodeToBuildSequenceFlow(process, simpleModelNode, endEvent.getId()); | 97 | + traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); |
| 87 | // 自动布局 | 98 | // 自动布局 |
| 88 | new BpmnAutoLayout(bpmnModel).execute(); | 99 | new BpmnAutoLayout(bpmnModel).execute(); |
| 89 | return bpmnModel; | 100 | return bpmnModel; |
| 90 | } | 101 | } |
| 91 | 102 | ||
| 103 | + private static BpmSimpleModelNodeVO buildStartSimpleModelNode() { | ||
| 104 | + BpmSimpleModelNodeVO startNode = new BpmSimpleModelNodeVO(); | ||
| 105 | + startNode.setId(START_EVENT_NODE_ID); | ||
| 106 | + startNode.setName(START_EVENT_NODE_NAME); | ||
| 107 | + startNode.setType(START_NODE.getType()); | ||
| 108 | + return startNode; | ||
| 109 | + } | ||
| 110 | + | ||
| 92 | // TODO @芋艿:在优化下这个注释 | 111 | // TODO @芋艿:在优化下这个注释 |
| 93 | private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { | 112 | private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { |
| 94 | // 1.1 无效节点返回 | 113 | // 1.1 无效节点返回 |
| @@ -288,6 +307,11 @@ public class SimpleModelUtils { | @@ -288,6 +307,11 @@ public class SimpleModelUtils { | ||
| 288 | list.add(endEvent); | 307 | list.add(endEvent); |
| 289 | break; | 308 | break; |
| 290 | } | 309 | } |
| 310 | + case START_USER_NODE: { // 发起人节点 | ||
| 311 | + UserTask userTask = convertStartUserNode(node); | ||
| 312 | + list.add(userTask); | ||
| 313 | + break; | ||
| 314 | + } | ||
| 291 | case APPROVE_NODE: { // 审批节点 | 315 | case APPROVE_NODE: { // 审批节点 |
| 292 | List<FlowElement> flowElements = convertApproveNode(node); | 316 | List<FlowElement> flowElements = convertApproveNode(node); |
| 293 | list.addAll(flowElements); | 317 | list.addAll(flowElements); |
| @@ -309,14 +333,8 @@ public class SimpleModelUtils { | @@ -309,14 +333,8 @@ public class SimpleModelUtils { | ||
| 309 | break; | 333 | break; |
| 310 | } | 334 | } |
| 311 | 335 | ||
| 312 | - case INCLUSIVE_BRANCH_FORK_NODE: { | ||
| 313 | - InclusiveGateway inclusiveGateway = convertInclusiveBranchNode(node, Boolean.TRUE); | ||
| 314 | - list.add(inclusiveGateway); | ||
| 315 | - break; | ||
| 316 | - } | ||
| 317 | - case INCLUSIVE_BRANCH_JOIN_NODE: { | ||
| 318 | - InclusiveGateway inclusiveGateway = convertInclusiveBranchNode(node, Boolean.FALSE); | ||
| 319 | - list.add(inclusiveGateway); | 336 | + case INCLUSIVE_BRANCH_NODE: { |
| 337 | + // TODO jason 待实现 | ||
| 320 | break; | 338 | break; |
| 321 | } | 339 | } |
| 322 | default: { | 340 | default: { |
| @@ -326,6 +344,11 @@ public class SimpleModelUtils { | @@ -326,6 +344,11 @@ public class SimpleModelUtils { | ||
| 326 | return list; | 344 | return list; |
| 327 | } | 345 | } |
| 328 | 346 | ||
| 347 | + private static UserTask convertStartUserNode(BpmSimpleModelNodeVO node) { | ||
| 348 | + return buildBpmnStartUserTask(node); | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | + | ||
| 329 | private static List<FlowElement> convertApproveNode(BpmSimpleModelNodeVO node) { | 352 | private static List<FlowElement> convertApproveNode(BpmSimpleModelNodeVO node) { |
| 330 | List<FlowElement> flowElements = new ArrayList<>(); | 353 | List<FlowElement> flowElements = new ArrayList<>(); |
| 331 | UserTask userTask = buildBpmnUserTask(node); | 354 | UserTask userTask = buildBpmnUserTask(node); |
| @@ -444,7 +467,7 @@ public class SimpleModelUtils { | @@ -444,7 +467,7 @@ public class SimpleModelUtils { | ||
| 444 | 467 | ||
| 445 | // 如果不是审批人节点,则直接返回 | 468 | // 如果不是审批人节点,则直接返回 |
| 446 | addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType())); | 469 | addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType())); |
| 447 | - if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) { | 470 | + if (ObjectUtil.notEqual(node.getApproveType(), USER.getType())) { |
| 448 | return userTask; | 471 | return userTask; |
| 449 | } | 472 | } |
| 450 | 473 | ||
| @@ -576,17 +599,32 @@ public class SimpleModelUtils { | @@ -576,17 +599,32 @@ public class SimpleModelUtils { | ||
| 576 | 599 | ||
| 577 | // ========== 各种 build 节点的方法 ========== | 600 | // ========== 各种 build 节点的方法 ========== |
| 578 | 601 | ||
| 579 | - private static StartEvent convertStartNode(BpmSimpleModelNodeVO node) { | 602 | + private static StartEvent convertStartNode(BpmSimpleModelNodeVO node) { |
| 580 | StartEvent startEvent = new StartEvent(); | 603 | StartEvent startEvent = new StartEvent(); |
| 581 | startEvent.setId(node.getId()); | 604 | startEvent.setId(node.getId()); |
| 582 | startEvent.setName(node.getName()); | 605 | startEvent.setName(node.getName()); |
| 583 | - | ||
| 584 | // TODO 芋艿 + jason:要不要在开启节点后面,加一个“发起人”任务节点,然后自动审批通过 | 606 | // TODO 芋艿 + jason:要不要在开启节点后面,加一个“发起人”任务节点,然后自动审批通过 |
| 585 | // @芋艿 这个是不是由前端来实现。 默认开始节点后面跟一个 “发起人”的审批节点(审批人是发起人自己)。 | 607 | // @芋艿 这个是不是由前端来实现。 默认开始节点后面跟一个 “发起人”的审批节点(审批人是发起人自己)。 |
| 586 | - // 我看有些平台这个审批节点允许删除,有些不允许。由用户决定 | ||
| 587 | return startEvent; | 608 | return startEvent; |
| 588 | } | 609 | } |
| 589 | 610 | ||
| 611 | + private static UserTask buildBpmnStartUserTask(BpmSimpleModelNodeVO node) { | ||
| 612 | + UserTask userTask = new UserTask(); | ||
| 613 | + userTask.setId(node.getId()); | ||
| 614 | + userTask.setName(node.getName()); | ||
| 615 | + // 人工审批 | ||
| 616 | + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, USER.getType().toString()); | ||
| 617 | + // 候选人策略为发起人自己 | ||
| 618 | + addCandidateElements(START_USER.getStrategy(),null, userTask); | ||
| 619 | + // 添加表单字段权限属性元素 | ||
| 620 | + addFormFieldsPermission(node.getFieldsPermission(), userTask); | ||
| 621 | + // 添加操作按钮配置属性元素. | ||
| 622 | + addButtonsSetting(node.getButtonsSetting(), userTask); | ||
| 623 | + // 使用自动通过策略。TODO @芋艿 复用了SKIP, 是否需要新加一个策略 | ||
| 624 | + addAssignStartUserHandlerType(SKIP.getType(), userTask); | ||
| 625 | + | ||
| 626 | + return userTask; | ||
| 627 | + } | ||
| 590 | private static EndEvent convertEndNode(BpmSimpleModelNodeVO node) { | 628 | private static EndEvent convertEndNode(BpmSimpleModelNodeVO node) { |
| 591 | EndEvent endEvent = new EndEvent(); | 629 | EndEvent endEvent = new EndEvent(); |
| 592 | endEvent.setId(node.getId()); | 630 | endEvent.setId(node.getId()); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -59,6 +59,7 @@ import java.util.stream.Stream; | @@ -59,6 +59,7 @@ import java.util.stream.Stream; | ||
| 59 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 59 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 60 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | 60 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 61 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | 61 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 62 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; | ||
| 62 | 63 | ||
| 63 | /** | 64 | /** |
| 64 | * 流程任务实例 Service 实现类 | 65 | * 流程任务实例 Service 实现类 |
| @@ -194,7 +195,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -194,7 +195,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 194 | // 为什么判断 assignee 非空的情况下? | 195 | // 为什么判断 assignee 非空的情况下? |
| 195 | // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 | 196 | // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 |
| 196 | if (StrUtil.isNotBlank(task.getAssignee()) | 197 | if (StrUtil.isNotBlank(task.getAssignee()) |
| 197 | - && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { | 198 | + && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { |
| 198 | throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); | 199 | throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); |
| 199 | } | 200 | } |
| 200 | return task; | 201 | return task; |
| @@ -618,6 +619,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -618,6 +619,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 618 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); | 619 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); |
| 619 | }); | 620 | }); |
| 620 | 621 | ||
| 622 | + // 设置流程变量节点驳回标记。用于驳回到节点。不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略 而自动通过 | ||
| 623 | + runtimeService.setVariable(currentTask.getProcessInstanceId(), | ||
| 624 | + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); | ||
| 621 | // 3. 执行驳回 | 625 | // 3. 执行驳回 |
| 622 | runtimeService.createChangeActivityStateBuilder() | 626 | runtimeService.createChangeActivityStateBuilder() |
| 623 | .processInstanceId(currentTask.getProcessInstanceId()) | 627 | .processInstanceId(currentTask.getProcessInstanceId()) |
| @@ -894,7 +898,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -894,7 +898,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 894 | 898 | ||
| 895 | /** | 899 | /** |
| 896 | * 重要补充说明:该方法目前主要有两个情况会调用到: | 900 | * 重要补充说明:该方法目前主要有两个情况会调用到: |
| 897 | - * | 901 | + * <p> |
| 898 | * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 | 902 | * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 |
| 899 | * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 | 903 | * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 |
| 900 | */ | 904 | */ |
| @@ -933,46 +937,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -933,46 +937,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 933 | log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); | 937 | log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); |
| 934 | return; | 938 | return; |
| 935 | } | 939 | } |
| 936 | - | ||
| 937 | // 审批人与提交人为同一人时,根据策略进行处理 | 940 | // 审批人与提交人为同一人时,根据策略进行处理 |
| 938 | if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { | 941 | if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { |
| 939 | - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); | ||
| 940 | - if (bpmnModel == null) { | ||
| 941 | - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); | ||
| 942 | - return; | ||
| 943 | - } | ||
| 944 | - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||
| 945 | - Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); | ||
| 946 | - | ||
| 947 | - // 情况一:自动跳过 | ||
| 948 | - if (ObjectUtils.equalsAny(assignStartUserHandlerType, | ||
| 949 | - BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { | ||
| 950 | - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) | ||
| 951 | - .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); | ||
| 952 | - return; | ||
| 953 | - } | ||
| 954 | - // 情况二:转交给部门负责人审批 | ||
| 955 | - if (ObjectUtils.equalsAny(assignStartUserHandlerType, | ||
| 956 | - BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { | ||
| 957 | - AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); | ||
| 958 | - Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); | ||
| 959 | - DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null; | ||
| 960 | - Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId()); | ||
| 961 | - // 找不到部门负责人的情况下,自动审批通过 | ||
| 962 | - // noinspection DataFlowIssue | ||
| 963 | - if (dept.getLeaderUserId() == null) { | ||
| 964 | - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) | ||
| 965 | - .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); | 942 | + // 判断是否为回退或者驳回 |
| 943 | + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), | ||
| 944 | + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); | ||
| 945 | + if (!BooleanUtil.isTrue(returnTaskFlag)) { // 如果是回退或者驳回不走这个策略 | ||
| 946 | + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); | ||
| 947 | + if (bpmnModel == null) { | ||
| 948 | + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); | ||
| 966 | return; | 949 | return; |
| 967 | } | 950 | } |
| 968 | - // 找得到部门负责人的情况下,修改负责人 | ||
| 969 | - if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { | ||
| 970 | - getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() | ||
| 971 | - .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) | ||
| 972 | - .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); | 951 | + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| 952 | + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); | ||
| 953 | + | ||
| 954 | + // 情况一:自动跳过 | ||
| 955 | + if (ObjectUtils.equalsAny(assignStartUserHandlerType, | ||
| 956 | + BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { | ||
| 957 | + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) | ||
| 958 | + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); | ||
| 973 | return; | 959 | return; |
| 974 | } | 960 | } |
| 975 | - // 如果部门负责人是自己,还是自己审批吧~ | 961 | + // 情况二:转交给部门负责人审批 |
| 962 | + if (ObjectUtils.equalsAny(assignStartUserHandlerType, | ||
| 963 | + BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { | ||
| 964 | + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); | ||
| 965 | + Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); | ||
| 966 | + DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null; | ||
| 967 | + Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId()); | ||
| 968 | + // 找不到部门负责人的情况下,自动审批通过 | ||
| 969 | + // noinspection DataFlowIssue | ||
| 970 | + if (dept.getLeaderUserId() == null) { | ||
| 971 | + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) | ||
| 972 | + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); | ||
| 973 | + return; | ||
| 974 | + } | ||
| 975 | + // 找得到部门负责人的情况下,修改负责人 | ||
| 976 | + if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { | ||
| 977 | + getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() | ||
| 978 | + .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) | ||
| 979 | + .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); | ||
| 980 | + return; | ||
| 981 | + } | ||
| 982 | + // 如果部门负责人是自己,还是自己审批吧~ | ||
| 983 | + } | ||
| 976 | } | 984 | } |
| 977 | } | 985 | } |
| 978 | 986 |