Commit 633a7c50ae151c1a5047fa73758f5cd5e02d6726
1 parent
41b9ab2b
仿钉钉流程设计- 简化审批拒绝流程, code review 修改
Showing
18 changed files
with
61 additions
and
259 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| ... | ... | @@ -51,9 +51,6 @@ public interface ErrorCodeConstants { |
| 51 | 51 | ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); |
| 52 | 52 | ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); |
| 53 | 53 | ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); |
| 54 | - ErrorCode TASK_RETURN_NOT_ASSIGN_TARGET_TASK_ID = new ErrorCode(1_009_005_015, "回退任务未指定目标任务编号"); | |
| 55 | - ErrorCode TASK_REJECT_HANDLER_TYPE_BY_REJECT_RATIO_ERROR = new ErrorCode(1_009_005_016, "按拒绝人数比例终止流程只能用于会签任务"); | |
| 56 | - | |
| 57 | 54 | ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); |
| 58 | 55 | |
| 59 | 56 | // ========== 动态表单模块 1-009-010-000 ========== | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerType.java
| ... | ... | @@ -13,12 +13,8 @@ import lombok.Getter; |
| 13 | 13 | @AllArgsConstructor |
| 14 | 14 | public enum BpmUserTaskRejectHandlerType { |
| 15 | 15 | |
| 16 | - // TODO @jason:是不是收敛成 2 个:FINISH_PROCESS => 1. 直接结束流程;RETURN_PRE_USER_TASK => 2. 驳回到指定节点(RETURN_USER_TASK【去掉 PRE】) | |
| 17 | 16 | FINISH_PROCESS(1, "终止流程"), |
| 18 | - RETURN_PRE_USER_TASK(2, "驳回到指定任务节点"), | |
| 19 | - | |
| 20 | - FINISH_PROCESS_BY_REJECT_NUMBER(3, "按拒绝人数终止流程"), // 用于会签 | |
| 21 | - FINISH_TASK(4, "结束任务"); // 待实现,可能会用于意见分支 | |
| 17 | + RETURN_USER_TASK(2, "驳回到指定任务节点"); | |
| 22 | 18 | |
| 23 | 19 | private final Integer type; |
| 24 | 20 | private final String name; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
| ... | ... | @@ -149,11 +149,10 @@ public class BpmModelController { |
| 149 | 149 | |
| 150 | 150 | // ========== 仿钉钉/飞书的精简模型 ========= |
| 151 | 151 | |
| 152 | - // TODO @jason:modelId => id 哈。一般属于自己的模块,可以简化命名。 | |
| 153 | 152 | @GetMapping("/simple/get") |
| 154 | 153 | @Operation(summary = "获得仿钉钉流程设计模型") |
| 155 | 154 | @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") |
| 156 | - public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("modelId") String modelId){ | |
| 155 | + public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("id") String modelId){ | |
| 157 | 156 | return success(modelService.getSimpleModel(modelId)); |
| 158 | 157 | } |
| 159 | 158 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| ... | ... | @@ -43,10 +43,9 @@ public class BpmSimpleModelNodeVO { |
| 43 | 43 | @Schema(description = "节点的属性") |
| 44 | 44 | private Map<String, Object> attributes; // TODO @jason:建议是字段分拆下;类似说: |
| 45 | 45 | |
| 46 | - // TODO @jason:看看是不是可以简化; | |
| 46 | + // TODO @jason:看看是不是可以简化;@芋艿: 暂时先放着。不知道后面是否会用到 | |
| 47 | 47 | /** |
| 48 | 48 | * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 |
| 49 | - * 例如: 会签时需要按拒绝人数来终止流程。 需要 userTask + ServiceTask 两个节点配合完成。 serviceTask 由后端生成。 | |
| 50 | 49 | */ |
| 51 | 50 | @JsonIgnore |
| 52 | 51 | private String attachNodeId; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java
| ... | ... | @@ -11,10 +11,9 @@ import lombok.Data; |
| 11 | 11 | @Data |
| 12 | 12 | public class BpmSimpleModelUpdateReqVO { |
| 13 | 13 | |
| 14 | - // TODO @jason:=> id | |
| 15 | 14 | @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 16 | 15 | @NotEmpty(message = "流程模型编号不能为空") |
| 17 | - private String modelId; // 对应 Flowable act_re_model 表 ID_ 字段 | |
| 16 | + private String id; // 对应 Flowable act_re_model 表 ID_ 字段 | |
| 18 | 17 | |
| 19 | 18 | @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED) |
| 20 | 19 | @NotNull(message = "仿钉钉流程设计模型对象不能为空") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/delegate/CopyUserDelegate.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/delegate/BpmCopyTaskDelegate.java
| ... | ... | @@ -7,18 +7,17 @@ import jakarta.annotation.Resource; |
| 7 | 7 | import org.flowable.bpmn.model.FlowElement; |
| 8 | 8 | import org.flowable.engine.delegate.DelegateExecution; |
| 9 | 9 | import org.flowable.engine.delegate.JavaDelegate; |
| 10 | -import org.springframework.stereotype.Service; | |
| 10 | +import org.springframework.stereotype.Component; | |
| 11 | 11 | |
| 12 | 12 | import java.util.Set; |
| 13 | 13 | |
| 14 | -// TODO @jason:类名可以改成 BpmCopyTaskDelegate | |
| 15 | 14 | /** |
| 16 | 15 | * 处理抄送用户的 {@link JavaDelegate} 的实现类 |
| 17 | 16 | * |
| 18 | 17 | * @author jason |
| 19 | 18 | */ |
| 20 | -@Service // TODO @jason:这种注解,建议用 @Component | |
| 21 | -public class CopyUserDelegate implements JavaDelegate { | |
| 19 | +@Component | |
| 20 | +public class BpmCopyTaskDelegate implements JavaDelegate { | |
| 22 | 21 | |
| 23 | 22 | @Resource |
| 24 | 23 | private BpmTaskCandidateInvoker taskCandidateInvoker; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/delegate/MultiInstanceServiceTaskDelegate.java deleted
| 1 | -package cn.iocoder.yudao.module.bpm.framework.flowable.core.custom.delegate; | |
| 2 | - | |
| 3 | -import cn.hutool.core.lang.Assert; | |
| 4 | -import cn.hutool.core.util.BooleanUtil; | |
| 5 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | |
| 6 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 7 | -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 8 | -import jakarta.annotation.Resource; | |
| 9 | -import org.flowable.engine.delegate.DelegateExecution; | |
| 10 | -import org.flowable.engine.delegate.JavaDelegate; | |
| 11 | -import org.springframework.stereotype.Component; | |
| 12 | - | |
| 13 | -// TODO @jason:微信已经讨论,简化哈 | |
| 14 | -/** | |
| 15 | - * 处理会签 Service Task 代理 | |
| 16 | - * | |
| 17 | - * @author jason | |
| 18 | - */ | |
| 19 | -@Component | |
| 20 | -public class MultiInstanceServiceTaskDelegate implements JavaDelegate { | |
| 21 | - | |
| 22 | - @Resource | |
| 23 | - private BpmProcessInstanceService processInstanceService; | |
| 24 | - | |
| 25 | - @Override | |
| 26 | - public void execute(DelegateExecution execution) { | |
| 27 | - | |
| 28 | - String attachUserTaskId = BpmnModelUtils.parseExtensionElement(execution.getCurrentFlowElement(), | |
| 29 | - BpmnModelConstants.SERVICE_TASK_ATTACH_USER_TASK_ID); // TODO @jason:上面不需要加空行哈; | |
| 30 | - Assert.notNull(attachUserTaskId, "附属的用户任务 Id 不能为空"); | |
| 31 | - // 获取会签任务是否被拒绝 | |
| 32 | - Boolean userTaskRejected = execution.getVariable(String.format("%s_reject", attachUserTaskId), Boolean.class); | |
| 33 | - // 如果会签任务被拒绝, 终止流程, 跳转到 EndEvent 节点 | |
| 34 | - if (BooleanUtil.isTrue(userTaskRejected)) { | |
| 35 | - processInstanceService.updateProcessInstanceReject(execution.getProcessInstanceId(), | |
| 36 | - execution.getCurrentActivityId(), "会签任务未达到通过比例" ); | |
| 37 | - } | |
| 38 | - } | |
| 39 | - | |
| 40 | -} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/expression/CompleteByRejectCountExpression.java deleted
| 1 | -package cn.iocoder.yudao.module.bpm.framework.flowable.core.custom.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.APPROVE_BY_RATIO; | |
| 19 | -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_METHOD; | |
| 20 | -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_RATIO; | |
| 21 | - | |
| 22 | -// TODO @jason:微信已经讨论,简化哈 | |
| 23 | -/** | |
| 24 | - * 按拒绝人数计算会签的完成条件的流程表达式实现 | |
| 25 | - * | |
| 26 | - * @author jason | |
| 27 | - */ | |
| 28 | -@Component | |
| 29 | -@Slf4j | |
| 30 | -public class CompleteByRejectCountExpression { | |
| 31 | - | |
| 32 | - /** | |
| 33 | - * 会签的完成条件 | |
| 34 | - */ | |
| 35 | - public boolean completionCondition(DelegateExecution execution) { | |
| 36 | - FlowElement flowElement = execution.getCurrentFlowElement(); | |
| 37 | - // 实例总数 | |
| 38 | - Integer nrOfInstances = (Integer) execution.getVariable("nrOfInstances"); | |
| 39 | - // 完成的实例数 | |
| 40 | - Integer nrOfCompletedInstances = (Integer) execution.getVariable("nrOfCompletedInstances"); | |
| 41 | - // 审批方式 | |
| 42 | - Integer approveMethod = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_APPROVE_METHOD)); | |
| 43 | - Assert.notNull(approveMethod, "审批方式不能空"); | |
| 44 | - if (!Objects.equals(APPROVE_BY_RATIO.getMethod(), approveMethod)) { | |
| 45 | - log.error("[completionCondition] the execution is [{}] 审批方式[{}] 不匹配", execution, approveMethod); | |
| 46 | - throw exception(GlobalErrorCodeConstants.ERROR_CONFIGURATION); | |
| 47 | - } | |
| 48 | - // 获取拒绝人数 | |
| 49 | - // TODO @jason:CollUtil.filter().size();貌似可以更简洁 @芋艿 CollUtil.filter().size() 使用这个会报错,好坑了. | |
| 50 | - Integer rejectCount = CollectionUtils.getSumValue(execution.getExecutions(), | |
| 51 | - item -> Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), item.getVariableLocal(BpmConstants.TASK_VARIABLE_STATUS, Integer.class)) ? 1 : 0, | |
| 52 | - Integer::sum, 0); | |
| 53 | - // 同意人数: 完成人数 - 拒绝人数 | |
| 54 | - int agreeCount = nrOfCompletedInstances - rejectCount; | |
| 55 | - // 多人会签(按通过比例) | |
| 56 | - Integer approveRatio = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_APPROVE_RATIO)); | |
| 57 | - Assert.notNull(approveRatio, "通过比例不能空"); | |
| 58 | - // 判断通过比例 | |
| 59 | - double approvePct = approveRatio / (double) 100; | |
| 60 | - double realApprovePct = (double) agreeCount / nrOfInstances; | |
| 61 | - if (realApprovePct >= approvePct) { | |
| 62 | - return true; | |
| 63 | - } | |
| 64 | - double rejectPct = (100 - approveRatio) / (double) 100; | |
| 65 | - double realRejectPct = (double) rejectCount / nrOfInstances; | |
| 66 | - // 判断拒绝比例 | |
| 67 | - if (realRejectPct > rejectPct) { | |
| 68 | - execution.setVariable(String.format("%s_reject", flowElement.getId()), Boolean.TRUE); | |
| 69 | - return true; | |
| 70 | - } | |
| 71 | - return false; | |
| 72 | - } | |
| 73 | - | |
| 74 | -} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| ... | ... | @@ -57,16 +57,6 @@ public interface BpmnModelConstants { |
| 57 | 57 | String USER_TASK_APPROVE_METHOD = "approveMethod"; |
| 58 | 58 | |
| 59 | 59 | /** |
| 60 | - * BPMN UserTask 的扩展属性,当审批方式为按通过比例时, 标记会签通过比例 | |
| 61 | - */ | |
| 62 | - String USER_TASK_APPROVE_RATIO = "approveRatio"; | |
| 63 | - | |
| 64 | - /** | |
| 65 | - * BPMN ExtensionElement 的扩展属性,用于标记 服务任务附属的用户任务 Id | |
| 66 | - */ | |
| 67 | - String SERVICE_TASK_ATTACH_USER_TASK_ID = "attachUserTaskId"; | |
| 68 | - | |
| 69 | - /** | |
| 70 | 60 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 |
| 71 | 61 | */ |
| 72 | 62 | String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
| ... | ... | @@ -73,48 +73,4 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 73 | 73 | }); |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | - // TODO @jason:这块如果不需要,可以删除掉~~~ | |
| 77 | -// @Override | |
| 78 | -// protected void activityMessageReceived(FlowableMessageEvent event) { | |
| 79 | -// BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(event.getProcessDefinitionId()); | |
| 80 | -// FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, event.getActivityId()); | |
| 81 | -// if (element instanceof BoundaryEvent) { | |
| 82 | -// BoundaryEvent boundaryEvent = (BoundaryEvent) element; | |
| 83 | -// String boundaryEventType = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); | |
| 84 | -// // 如果自定义类型为拒绝后处理,进行拒绝处理 | |
| 85 | -// if (Objects.equals(USER_TASK_REJECT_POST_PROCESS.getType(), NumberUtils.parseInt(boundaryEventType))) { | |
| 86 | -// String rejectHandlerType = parseBoundaryEventExtensionElement((BoundaryEvent) element, BpmnModelConstants.USER_TASK_REJECT_HANDLER_TYPE); | |
| 87 | -// rejectHandler(boundaryEvent, event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), NumberUtils.parseInt(rejectHandlerType)); | |
| 88 | -// } | |
| 89 | -// } | |
| 90 | -// } | |
| 91 | -// | |
| 92 | -// private void rejectHandler(BoundaryEvent boundaryEvent, String processInstanceId, String taskDefineKey, Integer rejectHandlerType) { | |
| 93 | -// BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); | |
| 94 | -// if (userTaskRejectHandlerType != null) { | |
| 95 | -// List<Task> taskList = taskService.getAssignedTaskListByConditions(processInstanceId, null, taskDefineKey); | |
| 96 | -// taskList.forEach(task -> { | |
| 97 | -// Integer taskStatus = FlowableUtils.getTaskStatus(task); | |
| 98 | -// // 只有处于拒绝状态下才处理 | |
| 99 | -// if (Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), taskStatus)) { | |
| 100 | -// // 终止流程 | |
| 101 | -// if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.TERMINATION) { | |
| 102 | -// processInstanceService.updateProcessInstanceReject(task.getProcessInstanceId(), FlowableUtils.getTaskReason(task)); | |
| 103 | -// return; | |
| 104 | -// } | |
| 105 | -// // 驳回 | |
| 106 | -// if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) { | |
| 107 | -// String returnTaskId = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.USER_TASK_REJECT_RETURN_TASK_ID); | |
| 108 | -// if (returnTaskId != null) { | |
| 109 | -// BpmTaskReturnReqVO reqVO = new BpmTaskReturnReqVO().setId(task.getId()) | |
| 110 | -// .setTargetTaskDefinitionKey(returnTaskId) | |
| 111 | -// .setReason("任务拒绝回退"); | |
| 112 | -// taskService.returnTask(getLoginUserId(), reqVO); | |
| 113 | -// } | |
| 114 | -// } | |
| 115 | -// } | |
| 116 | -// }); | |
| 117 | -// } | |
| 118 | -// } | |
| 119 | - | |
| 120 | 76 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 9 | 10 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 10 | 11 | import org.flowable.bpmn.model.Process; |
| ... | ... | @@ -27,8 +28,7 @@ public class BpmnModelUtils { |
| 27 | 28 | // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 |
| 28 | 29 | if (candidateStrategy == null) { |
| 29 | 30 | ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); |
| 30 | - // TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? NumberUtils.parseInt(element.getElementText()) : null; | |
| 31 | - candidateStrategy = NumberUtils.parseInt(Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null)); | |
| 31 | + candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; | |
| 32 | 32 | } |
| 33 | 33 | return candidateStrategy; |
| 34 | 34 | } |
| ... | ... | @@ -38,18 +38,26 @@ public class BpmnModelUtils { |
| 38 | 38 | BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); |
| 39 | 39 | if (candidateParam == null) { |
| 40 | 40 | ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); |
| 41 | - // TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? element.getElementText() : null; | |
| 42 | - candidateParam = Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null); | |
| 41 | + candidateParam = element != null ? element.getElementText() : null; | |
| 43 | 42 | } |
| 44 | 43 | return candidateParam; |
| 45 | 44 | } |
| 46 | 45 | |
| 46 | + public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) { | |
| 47 | + Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); | |
| 48 | + return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); | |
| 49 | + } | |
| 50 | + | |
| 51 | + public static String parseReturnTaskId(FlowElement flowElement) { | |
| 52 | + return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); | |
| 53 | + } | |
| 54 | + | |
| 47 | 55 | public static String parseExtensionElement(FlowElement flowElement, String elementName) { |
| 48 | 56 | if (flowElement == null) { |
| 49 | 57 | return null; |
| 50 | 58 | } |
| 51 | 59 | ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName)); |
| 52 | - return Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null); | |
| 60 | + return element != null ? element.getElementText() : null; | |
| 53 | 61 | } |
| 54 | 62 | |
| 55 | 63 | // TODO @jason:貌似这个没地方调用??? @芋艿 在 BpmTaskConvert里面。暂时注释掉了。 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -26,7 +26,6 @@ import java.util.Objects; |
| 26 | 26 | |
| 27 | 27 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT; |
| 28 | 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; | |
| 30 | 29 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; |
| 31 | 30 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 32 | 31 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; |
| ... | ... | @@ -55,9 +54,9 @@ public class SimpleModelUtils { |
| 55 | 54 | public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }"; |
| 56 | 55 | |
| 57 | 56 | /** |
| 58 | - * 按拒绝人数计算多实例完成条件的表达式 | |
| 57 | + * 按通过比例完成表达式 | |
| 59 | 58 | */ |
| 60 | - public static final String COMPLETE_BY_REJECT_COUNT_EXPRESSION = "${completeByRejectCountExpression.completionCondition(execution)}"; | |
| 59 | + public static final String APPROVE_BY_RATIO_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances/nrOfInstances >= %s}"; | |
| 61 | 60 | |
| 62 | 61 | // TODO-DONE @jason:建议方法名,改成 buildBpmnModel |
| 63 | 62 | // TODO @yunai:注释需要完善下; |
| ... | ... | @@ -185,8 +184,9 @@ public class SimpleModelUtils { |
| 185 | 184 | } |
| 186 | 185 | |
| 187 | 186 | /** |
| 188 | - * 构建有附加节点的连线 | |
| 189 | - * @param nodeId 当前节点 Id | |
| 187 | + * 构建有附加节点的连线 | |
| 188 | + * | |
| 189 | + * @param nodeId 当前节点 Id | |
| 190 | 190 | * @param attachNodeId 附属节点 Id |
| 191 | 191 | * @param targetNodeId 目标节点 Id |
| 192 | 192 | */ |
| ... | ... | @@ -344,28 +344,9 @@ public class SimpleModelUtils { |
| 344 | 344 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); |
| 345 | 345 | flowElements.add(boundaryEvent); |
| 346 | 346 | } |
| 347 | - // 如果按拒绝人数终止流程。需要添加附加的 ServiceTask 处理 | |
| 348 | - if (userTaskConfig.getRejectHandler() != null && | |
| 349 | - Objects.equals(FINISH_PROCESS_BY_REJECT_NUMBER.getType(), userTaskConfig.getRejectHandler().getType())) { | |
| 350 | - ServiceTask serviceTask = buildMultiInstanceServiceTask(node); | |
| 351 | - flowElements.add(serviceTask); | |
| 352 | - } | |
| 353 | 347 | return flowElements; |
| 354 | 348 | } |
| 355 | 349 | |
| 356 | - private static ServiceTask buildMultiInstanceServiceTask(BpmSimpleModelNodeVO node) { | |
| 357 | - ServiceTask serviceTask = new ServiceTask(); | |
| 358 | - String id = String.format("Activity-%s", IdUtil.fastSimpleUUID()); | |
| 359 | - serviceTask.setId(id); | |
| 360 | - serviceTask.setName("会签服务任务"); | |
| 361 | - serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); | |
| 362 | - serviceTask.setImplementation("${multiInstanceServiceTaskDelegate}"); | |
| 363 | - serviceTask.setAsynchronous(false); | |
| 364 | - addExtensionElement(serviceTask, SERVICE_TASK_ATTACH_USER_TASK_ID, node.getId()); | |
| 365 | - node.setAttachNodeId(id); | |
| 366 | - return serviceTask; | |
| 367 | - } | |
| 368 | - | |
| 369 | 350 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { |
| 370 | 351 | // 定时器边界事件 |
| 371 | 352 | BoundaryEvent boundaryEvent = new BoundaryEvent(); |
| ... | ... | @@ -406,7 +387,7 @@ public class SimpleModelUtils { |
| 406 | 387 | serviceTask.setId(node.getId()); |
| 407 | 388 | serviceTask.setName(node.getName()); |
| 408 | 389 | serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| 409 | - serviceTask.setImplementation("${copyUserDelegate}"); | |
| 390 | + serviceTask.setImplementation("${bpmCopyTaskDelegate}"); | |
| 410 | 391 | |
| 411 | 392 | // 添加抄送候选人元素 |
| 412 | 393 | addCandidateElements(MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY), |
| ... | ... | @@ -515,11 +496,10 @@ public class SimpleModelUtils { |
| 515 | 496 | multiInstanceCharacteristics.setLoopCardinality("1"); |
| 516 | 497 | userTask.setLoopCharacteristics(multiInstanceCharacteristics); |
| 517 | 498 | } else if (bpmApproveMethodEnum == BpmApproveMethodEnum.APPROVE_BY_RATIO) { |
| 518 | - multiInstanceCharacteristics.setCompletionCondition(COMPLETE_BY_REJECT_COUNT_EXPRESSION); | |
| 519 | - multiInstanceCharacteristics.setSequential(false); | |
| 520 | 499 | Assert.notNull(approveRatio, "通过比例不能为空"); |
| 521 | - // 添加通过比例的扩展属性 | |
| 522 | - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_RATIO, approveRatio.toString()); | |
| 500 | + double approvePct = approveRatio / (double) 100; | |
| 501 | + multiInstanceCharacteristics.setCompletionCondition(String.format(APPROVE_BY_RATIO_COMPLETE_EXPRESSION, String.format("%.2f", approvePct))); | |
| 502 | + multiInstanceCharacteristics.setSequential(false); | |
| 523 | 503 | } |
| 524 | 504 | userTask.setLoopCharacteristics(multiInstanceCharacteristics); |
| 525 | 505 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
| ... | ... | @@ -226,7 +226,7 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 226 | 226 | @Override |
| 227 | 227 | public void updateSimpleModel(BpmSimpleModelUpdateReqVO reqVO) { |
| 228 | 228 | // 1. 校验流程模型存在 |
| 229 | - Model model = getModel(reqVO.getModelId()); | |
| 229 | + Model model = getModel(reqVO.getId()); | |
| 230 | 230 | if (model == null) { |
| 231 | 231 | throw exception(MODEL_NOT_EXISTS); |
| 232 | 232 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java
| ... | ... | @@ -13,7 +13,13 @@ import java.util.Collection; |
| 13 | 13 | */ |
| 14 | 14 | public interface BpmProcessInstanceCopyService { |
| 15 | 15 | |
| 16 | - // TODO @jason:要不把 createProcessInstanceCopy 搞 2 个方法,一个方法参数是之前的 userIds、taskId;一个方法是现在 userIds、processInstanceId、taskId、taskName; | |
| 16 | + /** | |
| 17 | + * 流程实例的抄送 | |
| 18 | + * | |
| 19 | + * @param userIds 抄送的用户编号 | |
| 20 | + * @param taskId 流程任务编号 | |
| 21 | + */ | |
| 22 | + void createProcessInstanceCopy(Collection<Long> userIds, String taskId); | |
| 17 | 23 | |
| 18 | 24 | /** |
| 19 | 25 | * 流程实例的抄送 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.service.task; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; |
| ... | ... | @@ -10,6 +11,7 @@ import jakarta.annotation.Resource; |
| 10 | 11 | import lombok.extern.slf4j.Slf4j; |
| 11 | 12 | import org.flowable.engine.repository.ProcessDefinition; |
| 12 | 13 | import org.flowable.engine.runtime.ProcessInstance; |
| 14 | +import org.flowable.task.api.Task; | |
| 13 | 15 | import org.springframework.context.annotation.Lazy; |
| 14 | 16 | import org.springframework.stereotype.Service; |
| 15 | 17 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -44,21 +46,25 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy |
| 44 | 46 | @Lazy // 延迟加载,避免循环依赖 |
| 45 | 47 | private BpmProcessDefinitionService processDefinitionService; |
| 46 | 48 | |
| 49 | + @Override | |
| 50 | + public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) { | |
| 51 | + Task task = taskService.getTask(taskId); | |
| 52 | + if (ObjectUtil.isNull(task)) { | |
| 53 | + throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); | |
| 54 | + } | |
| 55 | + String processInstanceId = task.getProcessInstanceId(); | |
| 56 | + createProcessInstanceCopy(userIds, processInstanceId, task.getId(), task.getName()); | |
| 57 | + } | |
| 58 | + | |
| 47 | 59 | // TODO @芋艿:这里多加了一个 name; |
| 48 | 60 | @Override |
| 49 | 61 | public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) { |
| 50 | - // 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(UserTask) TODO jason:抄送节点,会没有来源的 taskId 么? @芋艿 是否校验一下 传递进来的 id 不为空就行 | |
| 51 | -// Task task = taskService.getTask(taskId); | |
| 52 | -// if (ObjectUtil.isNull(task)) { | |
| 53 | -// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); | |
| 54 | -// } | |
| 55 | - // 1.2 校验流程实例存在 | |
| 56 | -// String processInstanceId = task.getProcessInstanceId(); | |
| 62 | + // 1.1 校验流程实例存在 | |
| 57 | 63 | ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); |
| 58 | 64 | if (processInstance == null) { |
| 59 | 65 | throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); |
| 60 | 66 | } |
| 61 | - // 1.3 校验流程定义存在 | |
| 67 | + // 1.2 校验流程定义存在 | |
| 62 | 68 | ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( |
| 63 | 69 | processInstance.getProcessDefinitionId()); |
| 64 | 70 | if (processDefinition == null) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .processInstanceTenantId(FlowableUtils.getTenantId()) .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. 这种情况不会发生了。 拒绝时候不会删除流程 // // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 // if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { // return; // } // 1. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 2. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String currentActivityId, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 跳转到流程结束 EndEvent 节点,结束流程 // TODO @jason:需要测试下,如果这么走。当前其它审批任务,会不会结束,以及它们的状态是什么?!【重要】 ProcessInstance processInstance = getProcessInstance(id); BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); Assert.notNull(endEvent, "结束节点不能为空"); runtimeService.createChangeActivityStateBuilder() .processInstanceId(id) .moveActivityIdTo(currentActivityId, endEvent.getId()) // 当前节点 => 结束节点 .changeState(); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); // 当流程状态还是审批状态中, 更新为审批通过 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { updateProcessInstanceWhenApprove(instance); } // 审批不通过状态。已经在 updateProcessInstanceReject 处理 } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } } | |
| 2 | 1 | \ No newline at end of file |
| 2 | +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .processInstanceTenantId(FlowableUtils.getTenantId()) .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. 这种情况不会发生了。 拒绝时候不会删除流程 // // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 // if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { // return; // } // 1. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 2. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String currentActivityId, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 跳转到流程结束 EndEvent 节点,结束流程 // TODO @jason:需要测试下,如果这么走。当前其它审批任务,会不会结束,以及它们的状态是什么?!【重要】 // @芋艿,如果是未完成的会签任务。会被取消, 流程会结束。 但是貌似并行任务的时候。流程是不会结束的。任务还是在进行中的 ProcessInstance processInstance = getProcessInstance(id); BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); Assert.notNull(endEvent, "结束节点不能为空"); runtimeService.createChangeActivityStateBuilder() .processInstanceId(id) .moveActivityIdTo(currentActivityId, endEvent.getId()) // 当前节点 => 结束节点 .changeState(); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); // 当流程状态还是审批状态中, 更新为审批通过 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { updateProcessInstanceWhenApprove(instance); } // 审批不通过状态。已经在 updateProcessInstanceReject 处理 } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } } | |
| 3 | 3 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -54,8 +54,6 @@ import java.util.stream.Stream; |
| 54 | 54 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 55 | 55 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 56 | 56 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 57 | -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_REJECT_HANDLER_TYPE; | |
| 58 | -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_REJECT_RETURN_TASK_ID; | |
| 59 | 57 | |
| 60 | 58 | /** |
| 61 | 59 | * 流程任务实例 Service 实现类 |
| ... | ... | @@ -189,8 +187,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 189 | 187 | |
| 190 | 188 | // 2. 抄送用户 |
| 191 | 189 | if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) { |
| 192 | - processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(), | |
| 193 | - reqVO.getId(), task.getName()); | |
| 190 | + processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId()); | |
| 194 | 191 | } |
| 195 | 192 | |
| 196 | 193 | // 情况一:被委派的任务,不调用 complete 去完成任务 |
| ... | ... | @@ -338,35 +335,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 338 | 335 | |
| 339 | 336 | // 3.1 解析用户任务的拒绝处理类型 |
| 340 | 337 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 341 | - // TODO @jason:342 到 344 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy | |
| 342 | - UserTask flowElement = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | |
| 343 | - Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_HANDLER_TYPE)); | |
| 344 | - BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); | |
| 345 | - // 3.2 类型为驳回到指定的任务节点 TODO @jason:下面这种判断,最好是 JSON | |
| 346 | - if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) { | |
| 347 | - // TODO @jason:348 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy | |
| 348 | - String returnTaskId = BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); | |
| 349 | - // TODO @jason:这里如果找不到,直接抛出系统异常;因为说白了,已经不是业务异常啦。 | |
| 350 | - if (returnTaskId == null) { | |
| 351 | - throw exception(TASK_RETURN_NOT_ASSIGN_TARGET_TASK_ID); | |
| 352 | - } | |
| 338 | + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | |
| 339 | + BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); | |
| 340 | + // 3.2 类型为驳回到指定的任务节点 | |
| 341 | + if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) { | |
| 342 | + String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement); | |
| 343 | + Assert.notNull(returnTaskId, "回退的节点不能为空"); | |
| 353 | 344 | BpmTaskReturnReqVO returnReq = new BpmTaskReturnReqVO().setId(task.getId()).setTargetTaskDefinitionKey(returnTaskId) |
| 354 | 345 | .setReason(reqVO.getReason()); |
| 355 | 346 | returnTask(userId, returnReq); |
| 356 | 347 | return; |
| 357 | - } else if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER) { | |
| 358 | - // TODO @jason:微信沟通,去掉类似的逻辑; | |
| 359 | - // 3.3 按拒绝人数终止流程 | |
| 360 | - if (!flowElement.hasMultiInstanceLoopCharacteristics()) { | |
| 361 | - log.error("[rejectTask] 按拒绝人数终止流程类型,只能用于会签任务. 当前任务【{}】不是会签任务", task.getId()); | |
| 362 | - throw new IllegalStateException("按拒绝人数终止流程类型,只能用于会签任务"); | |
| 363 | - } | |
| 364 | - // 设置变量值为拒绝 | |
| 365 | - runtimeService.setVariableLocal(task.getExecutionId(), BpmConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.REJECT.getStatus()); | |
| 366 | - taskService.complete(task.getId()); | |
| 367 | - return; | |
| 368 | 348 | } |
| 369 | - // 3.4 其他情况 终止流程。 | |
| 349 | + // 3.3 其他情况 终止流程。 | |
| 370 | 350 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), |
| 371 | 351 | task.getTaskDefinitionKey(), reqVO.getReason()); |
| 372 | 352 | } | ... | ... |