Commit 12108e7365a0655d53b486ffce9907210e0121e0
1 parent
0db7796c
仿钉钉流程设计- 基于服务任务实现会签下的拒绝需要全员
Showing
7 changed files
with
185 additions
and
35 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerType.java
| @@ -15,7 +15,7 @@ public enum BpmUserTaskRejectHandlerType { | @@ -15,7 +15,7 @@ public enum BpmUserTaskRejectHandlerType { | ||
| 15 | 15 | ||
| 16 | FINISH_PROCESS(1, "终止流程"), | 16 | FINISH_PROCESS(1, "终止流程"), |
| 17 | RETURN_PRE_USER_TASK(2, "驳回到指定任务节点"), | 17 | RETURN_PRE_USER_TASK(2, "驳回到指定任务节点"), |
| 18 | - FINISH_PROCESS_BY_REJECT_RATIO(3, "按拒绝人数比例终止流程"), // 用于会签 | 18 | + FINISH_PROCESS_BY_REJECT_NUMBER(3, "按拒绝人数终止流程"), // 用于会签 |
| 19 | FINISH_TASK(4, "结束任务"); // 待实现,可能会用于意见分支 | 19 | FINISH_TASK(4, "结束任务"); // 待实现,可能会用于意见分支 |
| 20 | 20 | ||
| 21 | private final Integer type; | 21 | private final Integer type; |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| @@ -41,6 +41,12 @@ public class BpmSimpleModelNodeVO { | @@ -41,6 +41,12 @@ public class BpmSimpleModelNodeVO { | ||
| 41 | 41 | ||
| 42 | @Schema(description = "节点的属性") | 42 | @Schema(description = "节点的属性") |
| 43 | private Map<String, Object> attributes; // TODO @jason:建议是字段分拆下;类似说: | 43 | private Map<String, Object> attributes; // TODO @jason:建议是字段分拆下;类似说: |
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 | ||
| 47 | + * 例如: 会签时需要按拒绝人数来终止流程。 需要 userTask + ServiceTask 两个节点配合完成。 serviceTask 由后端生成。 | ||
| 48 | + */ | ||
| 49 | + private String attachNodeId; | ||
| 44 | // Map<String, Integer> formPermissions; 表单权限;仅发起、审批、抄送节点会使用 | 50 | // Map<String, Integer> formPermissions; 表单权限;仅发起、审批、抄送节点会使用 |
| 45 | // Integer approveMethod; 审批方式;仅审批节点会使用 | 51 | // Integer approveMethod; 审批方式;仅审批节点会使用 |
| 46 | // TODO @jason 后面和前端一起调整一下 | 52 | // TODO @jason 后面和前端一起调整一下 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| @@ -51,6 +51,16 @@ public interface BpmnModelConstants { | @@ -51,6 +51,16 @@ public interface BpmnModelConstants { | ||
| 51 | String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; | 51 | String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| 54 | + * BPMN UserTask 的扩展属性,用于标记用户任务的审批方式 | ||
| 55 | + */ | ||
| 56 | + String USER_TASK_APPROVE_METHOD = "approveMethod"; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * BPMN ExtensionElement 的扩展属性,用于标记 服务任务附属的用户任务 Id | ||
| 60 | + */ | ||
| 61 | + String SERVICE_TASK_ATTACH_USER_TASK_ID = "attachUserTaskId"; | ||
| 62 | + | ||
| 63 | + /** | ||
| 54 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 | 64 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 |
| 55 | */ | 65 | */ |
| 56 | String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; | 66 | String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; |
| @@ -75,6 +85,4 @@ public interface BpmnModelConstants { | @@ -75,6 +85,4 @@ public interface BpmnModelConstants { | ||
| 75 | * 支持转仿钉钉设计模型的 Bpmn 节点 | 85 | * 支持转仿钉钉设计模型的 Bpmn 节点 |
| 76 | */ | 86 | */ |
| 77 | Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class); | 87 | Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class); |
| 78 | - | ||
| 79 | - String REJECT_POST_PROCESS_MESSAGE_NAME = "message_reject_post_process"; | ||
| 80 | } | 88 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/expression/CompleteByRejectCountExpression.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.expression; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||
| 5 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||
| 7 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; | ||
| 8 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; | ||
| 9 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | ||
| 10 | +import lombok.extern.slf4j.Slf4j; | ||
| 11 | +import org.flowable.bpmn.model.FlowElement; | ||
| 12 | +import org.flowable.engine.delegate.DelegateExecution; | ||
| 13 | +import org.springframework.stereotype.Component; | ||
| 14 | + | ||
| 15 | +import java.util.Objects; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 18 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum.ANY_APPROVE_ALL_REJECT; | ||
| 19 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_METHOD; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 按拒绝人数计算会签的完成条件的流程表达式实现 | ||
| 23 | + * | ||
| 24 | + * @author jason | ||
| 25 | + */ | ||
| 26 | +@Component | ||
| 27 | +@Slf4j | ||
| 28 | +public class CompleteByRejectCountExpression { | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 会签的完成条件 | ||
| 32 | + */ | ||
| 33 | + public boolean completionCondition(DelegateExecution execution) { | ||
| 34 | + FlowElement flowElement = execution.getCurrentFlowElement(); | ||
| 35 | + // 实例总数 | ||
| 36 | + Integer nrOfInstances = (Integer) execution.getVariable("nrOfInstances"); | ||
| 37 | + // 完成的实例数 | ||
| 38 | + Integer nrOfCompletedInstances = (Integer) execution.getVariable("nrOfCompletedInstances"); | ||
| 39 | + // 审批方式 | ||
| 40 | + Integer approveMethod = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_APPROVE_METHOD)); | ||
| 41 | + Assert.notNull(approveMethod, "审批方式不能空"); | ||
| 42 | + // 计算拒绝的人数 | ||
| 43 | + Integer rejectCount = CollectionUtils.getSumValue(execution.getExecutions(), | ||
| 44 | + item -> Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), item.getVariableLocal(BpmConstants.TASK_VARIABLE_STATUS, Integer.class)) ? 1 : 0, | ||
| 45 | + Integer::sum, 0); | ||
| 46 | + // 同意的人数为 完成人数 - 拒绝人数 | ||
| 47 | + int agreeCount = nrOfCompletedInstances - rejectCount; | ||
| 48 | + // 1. 多人会签(通过只需一人,拒绝需要全员) | ||
| 49 | + if (Objects.equals(ANY_APPROVE_ALL_REJECT.getMethod(), approveMethod)) { | ||
| 50 | + // 1.1 一人同意. 会签任务完成 | ||
| 51 | + if (agreeCount > 0) { | ||
| 52 | + return true; | ||
| 53 | + } else { | ||
| 54 | + // 1.2 所有人都拒绝了。设置任务拒绝变量, 会签任务完成。 后续终止流程在 ServiceTask【MultiInstanceServiceTaskExpression】处理 | ||
| 55 | + if (Objects.equals(nrOfInstances, rejectCount)) { | ||
| 56 | + execution.setVariable(String.format("%s_reject",flowElement.getId()), Boolean.TRUE); | ||
| 57 | + return true; | ||
| 58 | + } | ||
| 59 | + return false; | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + // TODO 多人会签(按比例投票) | ||
| 63 | + log.error("[completionCondition] 按拒绝人数计算会签的完成条件的审批方式[{}],配置有误", approveMethod); | ||
| 64 | + throw exception(GlobalErrorCodeConstants.ERROR_CONFIGURATION); | ||
| 65 | + } | ||
| 66 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/expression/MultiInstanceServiceTaskExpression.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.expression; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.hutool.core.util.BooleanUtil; | ||
| 5 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | ||
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||
| 7 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | ||
| 8 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | ||
| 9 | +import jakarta.annotation.Resource; | ||
| 10 | +import org.flowable.engine.delegate.DelegateExecution; | ||
| 11 | +import org.flowable.engine.delegate.JavaDelegate; | ||
| 12 | +import org.springframework.stereotype.Component; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 处理会签 Service Task 代理表达式 | ||
| 16 | + * | ||
| 17 | + * @author jason | ||
| 18 | + */ | ||
| 19 | +@Component | ||
| 20 | +public class MultiInstanceServiceTaskExpression implements JavaDelegate { | ||
| 21 | + | ||
| 22 | + @Resource | ||
| 23 | + private BpmProcessInstanceService processInstanceService; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void execute(DelegateExecution execution) { | ||
| 27 | + String attachUserTaskId = BpmnModelUtils.parseExtensionElement(execution.getCurrentFlowElement(), | ||
| 28 | + BpmnModelConstants.SERVICE_TASK_ATTACH_USER_TASK_ID); | ||
| 29 | + Assert.notNull(attachUserTaskId, "附属的用户任务 Id 不能为空"); | ||
| 30 | + // 获取会签任务是否被拒绝 | ||
| 31 | + Boolean userTaskRejected = execution.getVariable(String.format("%s_reject", attachUserTaskId), Boolean.class); | ||
| 32 | + // 如果会签任务被拒绝, 终止流程 | ||
| 33 | + if (BooleanUtil.isTrue(userTaskRejected)) { | ||
| 34 | + processInstanceService.updateProcessInstanceReject(execution.getProcessInstanceId(), | ||
| 35 | + BpmCommentTypeEnum.REJECT.formatComment("会签任务拒绝人数满足条件")); | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| @@ -26,6 +26,7 @@ import java.util.Objects; | @@ -26,6 +26,7 @@ import java.util.Objects; | ||
| 26 | 26 | ||
| 27 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT; | 27 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT; |
| 28 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; | 28 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; |
| 29 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER; | ||
| 29 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; | 30 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; |
| 30 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | 31 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 31 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; | 32 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; |
| @@ -55,6 +56,11 @@ public class SimpleModelUtils { | @@ -55,6 +56,11 @@ public class SimpleModelUtils { | ||
| 55 | */ | 56 | */ |
| 56 | public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }"; | 57 | public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }"; |
| 57 | 58 | ||
| 59 | + /** | ||
| 60 | + * 按拒绝人数计算多实例完成条件的表达式 | ||
| 61 | + */ | ||
| 62 | + public static final String COMPLETE_BY_REJECT_COUNT_EXPRESSION = "${completeByRejectCountExpression.completionCondition(execution)}"; | ||
| 63 | + | ||
| 58 | // TODO-DONE @jason:建议方法名,改成 buildBpmnModel | 64 | // TODO-DONE @jason:建议方法名,改成 buildBpmnModel |
| 59 | // TODO @yunai:注释需要完善下; | 65 | // TODO @yunai:注释需要完善下; |
| 60 | 66 | ||
| @@ -71,10 +77,6 @@ public class SimpleModelUtils { | @@ -71,10 +77,6 @@ public class SimpleModelUtils { | ||
| 71 | // 不加这个 解析 Message 会报 NPE 异常 . | 77 | // 不加这个 解析 Message 会报 NPE 异常 . |
| 72 | bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); // TODO @jason:待定:是不是搞个自定义的 namespace; | 78 | bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); // TODO @jason:待定:是不是搞个自定义的 namespace; |
| 73 | // TODO 芋艿:后续在 review | 79 | // TODO 芋艿:后续在 review |
| 74 | - // @芋艿 这个 Message 可以去掉 暂时用不上 | ||
| 75 | - Message rejectPostProcessMsg = new Message(); | ||
| 76 | - rejectPostProcessMsg.setName(REJECT_POST_PROCESS_MESSAGE_NAME); | ||
| 77 | - bpmnModel.addMessage(rejectPostProcessMsg); | ||
| 78 | 80 | ||
| 79 | Process process = new Process(); | 81 | Process process = new Process(); |
| 80 | process.setId(processId); | 82 | process.setId(processId); |
| @@ -107,19 +109,30 @@ public class SimpleModelUtils { | @@ -107,19 +109,30 @@ public class SimpleModelUtils { | ||
| 107 | if (nodeType == END_NODE) { | 109 | if (nodeType == END_NODE) { |
| 108 | return; | 110 | return; |
| 109 | } | 111 | } |
| 110 | - | ||
| 111 | // 2.1 情况一:普通节点 | 112 | // 2.1 情况一:普通节点 |
| 112 | BpmSimpleModelNodeVO childNode = node.getChildNode(); | 113 | BpmSimpleModelNodeVO childNode = node.getChildNode(); |
| 113 | if (!BpmSimpleModelNodeType.isBranchNode(node.getType())) { | 114 | if (!BpmSimpleModelNodeType.isBranchNode(node.getType())) { |
| 114 | if (!isValidNode(childNode)) { | 115 | if (!isValidNode(childNode)) { |
| 115 | // 2.1.1 普通节点且无孩子节点。分两种情况 | 116 | // 2.1.1 普通节点且无孩子节点。分两种情况 |
| 116 | // a.结束节点 b. 条件分支的最后一个节点.与分支节点的孩子节点或聚合节点建立连线。 | 117 | // a.结束节点 b. 条件分支的最后一个节点.与分支节点的孩子节点或聚合节点建立连线。 |
| 117 | - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), targetNodeId, null, null, null); | ||
| 118 | - process.addFlowElement(sequenceFlow); | 118 | + if (StrUtil.isNotEmpty(node.getAttachNodeId())) { |
| 119 | + // 2.1.1.1 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 | ||
| 120 | + List<SequenceFlow> sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), targetNodeId); | ||
| 121 | + sequenceFlows.forEach(process::addFlowElement); | ||
| 122 | + } else { | ||
| 123 | + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), targetNodeId, null, null, null); | ||
| 124 | + process.addFlowElement(sequenceFlow); | ||
| 125 | + } | ||
| 119 | } else { | 126 | } else { |
| 120 | // 2.1.2 普通节点且有孩子节点。建立连线 | 127 | // 2.1.2 普通节点且有孩子节点。建立连线 |
| 121 | - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null, null); | ||
| 122 | - process.addFlowElement(sequenceFlow); | 128 | + if (StrUtil.isNotEmpty(node.getAttachNodeId())) { |
| 129 | + // 2.1.1.2 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 | ||
| 130 | + List<SequenceFlow> sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), childNode.getId()); | ||
| 131 | + sequenceFlows.forEach(process::addFlowElement); | ||
| 132 | + } else { | ||
| 133 | + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null, null); | ||
| 134 | + process.addFlowElement(sequenceFlow); | ||
| 135 | + } | ||
| 123 | // 递归调用后续节点 | 136 | // 递归调用后续节点 |
| 124 | traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); | 137 | traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); |
| 125 | } | 138 | } |
| @@ -174,6 +187,18 @@ public class SimpleModelUtils { | @@ -174,6 +187,18 @@ public class SimpleModelUtils { | ||
| 174 | } | 187 | } |
| 175 | 188 | ||
| 176 | /** | 189 | /** |
| 190 | + * 构建有附加节点的连线 | ||
| 191 | + * @param nodeId 当前节点 Id | ||
| 192 | + * @param attachNodeId 附属节点 Id | ||
| 193 | + * @param targetNodeId 目标节点 Id | ||
| 194 | + */ | ||
| 195 | + private static List<SequenceFlow> buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { | ||
| 196 | + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); | ||
| 197 | + SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); | ||
| 198 | + return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + /** | ||
| 177 | * 构造条件表达式 | 202 | * 构造条件表达式 |
| 178 | * | 203 | * |
| 179 | * @param conditionNode 条件节点 | 204 | * @param conditionNode 条件节点 |
| @@ -331,9 +356,28 @@ public class SimpleModelUtils { | @@ -331,9 +356,28 @@ public class SimpleModelUtils { | ||
| 331 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); | 356 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); |
| 332 | flowElements.add(boundaryEvent); | 357 | flowElements.add(boundaryEvent); |
| 333 | } | 358 | } |
| 359 | + // 如果按拒绝人数终止流程。需要添加附加的 ServiceTask 处理 | ||
| 360 | + if (userTaskConfig.getRejectHandler() != null && | ||
| 361 | + Objects.equals(FINISH_PROCESS_BY_REJECT_NUMBER.getType(), userTaskConfig.getRejectHandler().getType())) { | ||
| 362 | + ServiceTask serviceTask = buildMultiInstanceServiceTask(node); | ||
| 363 | + flowElements.add(serviceTask); | ||
| 364 | + } | ||
| 334 | return flowElements; | 365 | return flowElements; |
| 335 | } | 366 | } |
| 336 | 367 | ||
| 368 | + private static ServiceTask buildMultiInstanceServiceTask(BpmSimpleModelNodeVO node) { | ||
| 369 | + ServiceTask serviceTask = new ServiceTask(); | ||
| 370 | + String id = String.format("Activity-%s", IdUtil.fastSimpleUUID()); | ||
| 371 | + serviceTask.setId(id); | ||
| 372 | + serviceTask.setName("会签服务任务"); | ||
| 373 | + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); | ||
| 374 | + serviceTask.setImplementation("${multiInstanceServiceTaskExpression}"); | ||
| 375 | + serviceTask.setAsynchronous(false); | ||
| 376 | + addExtensionElement(serviceTask, SERVICE_TASK_ATTACH_USER_TASK_ID, node.getId()); | ||
| 377 | + node.setAttachNodeId(id); | ||
| 378 | + return serviceTask; | ||
| 379 | + } | ||
| 380 | + | ||
| 337 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { | 381 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { |
| 338 | // 定时器边界事件 | 382 | // 定时器边界事件 |
| 339 | BoundaryEvent boundaryEvent = new BoundaryEvent(); | 383 | BoundaryEvent boundaryEvent = new BoundaryEvent(); |
| @@ -468,6 +512,9 @@ public class SimpleModelUtils { | @@ -468,6 +512,9 @@ public class SimpleModelUtils { | ||
| 468 | if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.SINGLE_PERSON_APPROVE) { | 512 | if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.SINGLE_PERSON_APPROVE) { |
| 469 | return; | 513 | return; |
| 470 | } | 514 | } |
| 515 | + // 添加审批方式的扩展属性 | ||
| 516 | + addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, | ||
| 517 | + approveMethod == null ? null : approveMethod.toString()); | ||
| 471 | MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); | 518 | MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); |
| 472 | // 设置 collectionVariable。本系统用不到。会在 仅仅为了校验。 | 519 | // 设置 collectionVariable。本系统用不到。会在 仅仅为了校验。 |
| 473 | multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); | 520 | multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); |
| @@ -484,8 +531,7 @@ public class SimpleModelUtils { | @@ -484,8 +531,7 @@ public class SimpleModelUtils { | ||
| 484 | multiInstanceCharacteristics.setLoopCardinality("1"); | 531 | multiInstanceCharacteristics.setLoopCardinality("1"); |
| 485 | userTask.setLoopCharacteristics(multiInstanceCharacteristics); | 532 | userTask.setLoopCharacteristics(multiInstanceCharacteristics); |
| 486 | } else if (bpmApproveMethodEnum == BpmApproveMethodEnum.ANY_APPROVE_ALL_REJECT) { | 533 | } else if (bpmApproveMethodEnum == BpmApproveMethodEnum.ANY_APPROVE_ALL_REJECT) { |
| 487 | - // 这种情况。拒绝任务时候,不会终止或者完成任务 参见 BpmTaskService#rejectTask 方法 | ||
| 488 | - multiInstanceCharacteristics.setCompletionCondition(ANY_OF_APPROVE_COMPLETE_EXPRESSION); | 534 | + multiInstanceCharacteristics.setCompletionCondition(COMPLETE_BY_REJECT_COUNT_EXPRESSION); |
| 489 | multiInstanceCharacteristics.setSequential(false); | 535 | multiInstanceCharacteristics.setSequential(false); |
| 490 | } | 536 | } |
| 491 | // TODO 会签(按比例投票 ) | 537 | // TODO 会签(按比例投票 ) |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -35,7 +35,6 @@ import org.flowable.engine.HistoryService; | @@ -35,7 +35,6 @@ import org.flowable.engine.HistoryService; | ||
| 35 | import org.flowable.engine.ManagementService; | 35 | import org.flowable.engine.ManagementService; |
| 36 | import org.flowable.engine.RuntimeService; | 36 | import org.flowable.engine.RuntimeService; |
| 37 | import org.flowable.engine.TaskService; | 37 | import org.flowable.engine.TaskService; |
| 38 | -import org.flowable.engine.runtime.Execution; | ||
| 39 | import org.flowable.engine.runtime.ProcessInstance; | 38 | import org.flowable.engine.runtime.ProcessInstance; |
| 40 | import org.flowable.task.api.DelegationState; | 39 | import org.flowable.task.api.DelegationState; |
| 41 | import org.flowable.task.api.Task; | 40 | import org.flowable.task.api.Task; |
| @@ -352,30 +351,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -352,30 +351,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 352 | .setReason(reqVO.getReason()); | 351 | .setReason(reqVO.getReason()); |
| 353 | returnTask(userId, returnReq); | 352 | returnTask(userId, returnReq); |
| 354 | return; | 353 | return; |
| 355 | - } else if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_RATIO) { | ||
| 356 | - // 3.3 按拒绝人数比例终止流程 | 354 | + } else if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER) { |
| 355 | + // 3.3 按拒绝人数终止流程 | ||
| 357 | if (!flowElement.hasMultiInstanceLoopCharacteristics()) { | 356 | if (!flowElement.hasMultiInstanceLoopCharacteristics()) { |
| 358 | log.error("[rejectTask] 用户任务拒绝处理类型配置错误, 按拒绝人数终止流程只能用于会签任务"); | 357 | log.error("[rejectTask] 用户任务拒绝处理类型配置错误, 按拒绝人数终止流程只能用于会签任务"); |
| 359 | throw exception(GlobalErrorCodeConstants.ERROR_CONFIGURATION); | 358 | throw exception(GlobalErrorCodeConstants.ERROR_CONFIGURATION); |
| 360 | } | 359 | } |
| 361 | - // 获取并行任务总数 | ||
| 362 | - Execution execution = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()) | ||
| 363 | - .executionId(task.getExecutionId()).singleResult(); | ||
| 364 | - Integer nrOfInstances = runtimeService.getVariable(execution.getParentId(), "nrOfInstances", Integer.class); | ||
| 365 | - // 获取未完成任务列表 | ||
| 366 | - List<Task> taskList = getTaskListByProcessInstanceIdAndAssigned(task.getProcessInstanceId(), null, | ||
| 367 | - task.getTaskDefinitionKey()); | ||
| 368 | - // 获取已经拒绝的任务数 | ||
| 369 | - Integer rejectNumber = getSumValue(taskList, | ||
| 370 | - item -> Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), FlowableUtils.getTaskStatus(item)) ? 1 : 0, | ||
| 371 | - Integer::sum, 0); | ||
| 372 | -// // TODO @jason:如果这样的话,后续会不会在【已完成】里面查询不到哈?【重要!!!!】 | ||
| 373 | -// // 拒绝任务后,任务分配人清空。但不能完成任务 | ||
| 374 | -// taskService.setAssignee(task.getId(), ""); | ||
| 375 | - // 不是所有人拒绝返回。 TODO 后续需要做按拒绝人数比例来判断 | ||
| 376 | - if (!Objects.equals(nrOfInstances, rejectNumber)) { | ||
| 377 | - return; | ||
| 378 | - } | 360 | + // 设置变量值为拒绝 |
| 361 | + runtimeService.setVariableLocal(task.getExecutionId(), BpmConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.REJECT.getStatus()); | ||
| 362 | + // 完成任务 | ||
| 363 | + taskService.complete(task.getId()); | ||
| 364 | + return; | ||
| 379 | } | 365 | } |
| 380 | // 3.4 其他情况 终止流程。 TODO 后续可能会增加处理类型 | 366 | // 3.4 其他情况 终止流程。 TODO 后续可能会增加处理类型 |
| 381 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), reqVO.getReason()); | 367 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), reqVO.getReason()); |