Commit 41b9ab2ba53b46d23cbc78b194639e8df70b23c2
1 parent
8585e057
【代码评审】BPM:review 快搭的实现
Showing
27 changed files
with
78 additions
and
89 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmApproveMethodEnum.java
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,8 +13,10 @@ 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】) | |
| 16 | 17 | FINISH_PROCESS(1, "终止流程"), |
| 17 | 18 | RETURN_PRE_USER_TASK(2, "驳回到指定任务节点"), |
| 19 | + | |
| 18 | 20 | FINISH_PROCESS_BY_REJECT_NUMBER(3, "按拒绝人数终止流程"), // 用于会签 |
| 19 | 21 | FINISH_TASK(4, "结束任务"); // 待实现,可能会用于意见分支 |
| 20 | 22 | |
| ... | ... | @@ -24,4 +26,5 @@ public enum BpmUserTaskRejectHandlerType { |
| 24 | 26 | public static BpmUserTaskRejectHandlerType typeOf(Integer type) { |
| 25 | 27 | return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); |
| 26 | 28 | } |
| 29 | + | |
| 27 | 30 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
| ... | ... | @@ -149,6 +149,7 @@ public class BpmModelController { |
| 149 | 149 | |
| 150 | 150 | // ========== 仿钉钉/飞书的精简模型 ========= |
| 151 | 151 | |
| 152 | + // TODO @jason:modelId => id 哈。一般属于自己的模块,可以简化命名。 | |
| 152 | 153 | @GetMapping("/simple/get") |
| 153 | 154 | @Operation(summary = "获得仿钉钉流程设计模型") |
| 154 | 155 | @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| ... | ... | @@ -43,6 +43,7 @@ public class BpmSimpleModelNodeVO { |
| 43 | 43 | @Schema(description = "节点的属性") |
| 44 | 44 | private Map<String, Object> attributes; // TODO @jason:建议是字段分拆下;类似说: |
| 45 | 45 | |
| 46 | + // TODO @jason:看看是不是可以简化; | |
| 46 | 47 | /** |
| 47 | 48 | * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 |
| 48 | 49 | * 例如: 会签时需要按拒绝人数来终止流程。 需要 userTask + ServiceTask 两个节点配合完成。 serviceTask 由后端生成。 |
| ... | ... | @@ -52,12 +53,13 @@ public class BpmSimpleModelNodeVO { |
| 52 | 53 | |
| 53 | 54 | // Map<String, Integer> formPermissions; 表单权限;仅发起、审批、抄送节点会使用 |
| 54 | 55 | // Integer approveMethod; 审批方式;仅审批节点会使用 |
| 55 | - // TODO @jason 后面和前端一起调整一下 | |
| 56 | - // TODO @芋艿:审批人的选择; | |
| 57 | - // TODO @芋艿:没有人的策略? | |
| 58 | - // TODO @芋艿:审批拒绝的策略? | |
| 59 | - // TODO @芋艿:配置的可操作列表? | |
| 60 | - // TODO @芋艿:超时配置;要支持指定时间点、指定时间间隔; | |
| 56 | + // TODO @jason 后面和前端一起调整一下;下面的 ①、②、③ 是优先级 | |
| 57 | + // TODO @芋艿:① 审批人的选择; | |
| 58 | + // TODO @芋艿:⑥ 没有人的策略? | |
| 59 | + // TODO @芋艿:② 审批拒绝的策略? | |
| 60 | + // TODO @芋艿:③ 配置的可操作列表?(操作权限) | |
| 61 | + // TODO @芋艿:④ 表单的权限列表? | |
| 62 | + // TODO @芋艿:⑨ 超时配置;要支持指定时间点、指定时间间隔; | |
| 61 | 63 | // TODO @芋艿:条件;建议可以固化的一些选项;然后有个表达式兜底;要支持 |
| 62 | 64 | |
| 63 | 65 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java
| ... | ... | @@ -11,6 +11,7 @@ import lombok.Data; |
| 11 | 11 | @Data |
| 12 | 12 | public class BpmSimpleModelUpdateReqVO { |
| 13 | 13 | |
| 14 | + // TODO @jason:=> id | |
| 14 | 15 | @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 15 | 16 | @NotEmpty(message = "流程模型编号不能为空") |
| 16 | 17 | private String modelId; // 对应 Flowable act_re_model 表 ID_ 字段 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java
| ... | ... | @@ -28,9 +28,6 @@ public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrate |
| 28 | 28 | return BpmTaskCandidateStrategyEnum.START_USER; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | - /** | |
| 32 | - * 无需校验参数 | |
| 33 | - */ | |
| 34 | 31 | @Override |
| 35 | 32 | public void validateParam(String param) {} |
| 36 | 33 | |
| ... | ... | @@ -40,11 +37,9 @@ public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrate |
| 40 | 37 | return SetUtils.asSet(Long.valueOf(startUserId)); |
| 41 | 38 | } |
| 42 | 39 | |
| 43 | - /** | |
| 44 | - * 不需要参数 | |
| 45 | - */ | |
| 46 | 40 | @Override |
| 47 | 41 | public boolean isParamRequired() { |
| 48 | 42 | return false; |
| 49 | 43 | } |
| 44 | + | |
| 50 | 45 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/delegate/CopyUserDelegate.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.custom.delegate; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 4 | 5 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService; |
| 5 | 6 | import jakarta.annotation.Resource; |
| ... | ... | @@ -10,23 +11,29 @@ import org.springframework.stereotype.Service; |
| 10 | 11 | |
| 11 | 12 | import java.util.Set; |
| 12 | 13 | |
| 14 | +// TODO @jason:类名可以改成 BpmCopyTaskDelegate | |
| 13 | 15 | /** |
| 14 | - * 处理抄送用户的代理 | |
| 16 | + * 处理抄送用户的 {@link JavaDelegate} 的实现类 | |
| 15 | 17 | * |
| 16 | 18 | * @author jason |
| 17 | 19 | */ |
| 18 | -@Service | |
| 20 | +@Service // TODO @jason:这种注解,建议用 @Component | |
| 19 | 21 | public class CopyUserDelegate implements JavaDelegate { |
| 20 | 22 | |
| 21 | 23 | @Resource |
| 22 | 24 | private BpmTaskCandidateInvoker taskCandidateInvoker; |
| 25 | + | |
| 23 | 26 | @Resource |
| 24 | 27 | private BpmProcessInstanceCopyService processInstanceCopyService; |
| 25 | 28 | |
| 26 | 29 | @Override |
| 27 | 30 | public void execute(DelegateExecution execution) { |
| 28 | - // TODO @芋艿:可能要考虑,系统抄送,没有 taskId 的情况。 | |
| 31 | + // 1. 获得抄送人 | |
| 29 | 32 | Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution); |
| 33 | + if (CollUtil.isEmpty(userIds)) { | |
| 34 | + return; | |
| 35 | + } | |
| 36 | + // 2. 执行抄送 | |
| 30 | 37 | FlowElement currentFlowElement = execution.getCurrentFlowElement(); |
| 31 | 38 | processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(), |
| 32 | 39 | currentFlowElement.getId(), currentFlowElement.getName()); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/delegate/MultiInstanceServiceTaskDelegate.java
| ... | ... | @@ -10,6 +10,7 @@ import org.flowable.engine.delegate.DelegateExecution; |
| 10 | 10 | import org.flowable.engine.delegate.JavaDelegate; |
| 11 | 11 | import org.springframework.stereotype.Component; |
| 12 | 12 | |
| 13 | +// TODO @jason:微信已经讨论,简化哈 | |
| 13 | 14 | /** |
| 14 | 15 | * 处理会签 Service Task 代理 |
| 15 | 16 | * |
| ... | ... | @@ -25,7 +26,7 @@ public class MultiInstanceServiceTaskDelegate implements JavaDelegate { |
| 25 | 26 | public void execute(DelegateExecution execution) { |
| 26 | 27 | |
| 27 | 28 | String attachUserTaskId = BpmnModelUtils.parseExtensionElement(execution.getCurrentFlowElement(), |
| 28 | - BpmnModelConstants.SERVICE_TASK_ATTACH_USER_TASK_ID); | |
| 29 | + BpmnModelConstants.SERVICE_TASK_ATTACH_USER_TASK_ID); // TODO @jason:上面不需要加空行哈; | |
| 29 | 30 | Assert.notNull(attachUserTaskId, "附属的用户任务 Id 不能为空"); |
| 30 | 31 | // 获取会签任务是否被拒绝 |
| 31 | 32 | Boolean userTaskRejected = execution.getVariable(String.format("%s_reject", attachUserTaskId), Boolean.class); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/custom/expression/CompleteByRejectCountExpression.java
| ... | ... | @@ -19,6 +19,7 @@ import static cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum. |
| 19 | 19 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_METHOD; |
| 20 | 20 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_RATIO; |
| 21 | 21 | |
| 22 | +// TODO @jason:微信已经讨论,简化哈 | |
| 22 | 23 | /** |
| 23 | 24 | * 按拒绝人数计算会签的完成条件的流程表达式实现 |
| 24 | 25 | * | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java
| ... | ... | @@ -4,6 +4,7 @@ import org.flowable.common.engine.api.variable.VariableContainer; |
| 4 | 4 | import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction; |
| 5 | 5 | import org.springframework.stereotype.Component; |
| 6 | 6 | |
| 7 | +// TODO @jason:这个自定义转换的原因是啥呀? | |
| 7 | 8 | /** |
| 8 | 9 | * 根据流程变量 variable 的类型, 转换参数的值 |
| 9 | 10 | * | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| ... | ... | @@ -40,6 +40,7 @@ public interface BpmnModelConstants { |
| 40 | 40 | */ |
| 41 | 41 | String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction"; |
| 42 | 42 | |
| 43 | + // TODO @jason:1)是不是上面的 timeoutAction 改成 timeoutHandler;2)rejectHandlerType 改成 rejectHandler 哇? | |
| 43 | 44 | /** |
| 44 | 45 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 |
| 45 | 46 | */ | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
| ... | ... | @@ -73,6 +73,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 73 | 73 | }); |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | + // TODO @jason:这块如果不需要,可以删除掉~~~ | |
| 76 | 77 | // @Override |
| 77 | 78 | // protected void activityMessageReceived(FlowableMessageEvent event) { |
| 78 | 79 | // BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(event.getProcessDefinitionId()); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java
| ... | ... | @@ -29,6 +29,7 @@ import org.springframework.stereotype.Component; |
| 29 | 29 | import java.util.List; |
| 30 | 30 | import java.util.Set; |
| 31 | 31 | |
| 32 | +// TODO @芋艿:这块需要仔细再瞅瞅 | |
| 32 | 33 | /** |
| 33 | 34 | * 监听定时器触发事件 |
| 34 | 35 | * |
| ... | ... | @@ -96,7 +97,6 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe |
| 96 | 97 | BpmTaskApproveReqVO req = new BpmTaskApproveReqVO().setId(task.getId()) |
| 97 | 98 | .setReason("超时系统自动同意"); |
| 98 | 99 | bpmTaskService.approveTask(Long.parseLong(task.getAssignee()), req); |
| 99 | - | |
| 100 | 100 | } |
| 101 | 101 | // 自动拒绝 |
| 102 | 102 | if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REJECT) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/mq/producer/task/TodoTaskReminderProducer.java
| ... | ... | @@ -7,6 +7,7 @@ import org.springframework.context.ApplicationContext; |
| 7 | 7 | import org.springframework.stereotype.Component; |
| 8 | 8 | import org.springframework.validation.annotation.Validated; |
| 9 | 9 | |
| 10 | +// TODO @jason:建议直接调用 BpmMessageService 哈;更简化一点~ | |
| 10 | 11 | /** |
| 11 | 12 | * 待办任务提醒 Producer |
| 12 | 13 | * |
| ... | ... | @@ -22,4 +23,5 @@ public class TodoTaskReminderProducer { |
| 22 | 23 | public void sendReminderMessage(@Valid TodoTaskReminderMessage message) { |
| 23 | 24 | applicationContext.publishEvent(message); |
| 24 | 25 | } |
| 26 | + | |
| 25 | 27 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/simple/SimpleModelConditionGroups.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/simplemodel/SimpleModelConditionGroups.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/simple/SimpleModelUserTaskConfig.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/simplemodel/SimpleModelUserTaskConfig.java
| 1 | -package cn.iocoder.yudao.module.bpm.framework.flowable.core.simple; | |
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; |
| ... | ... | @@ -18,12 +18,11 @@ public class SimpleModelUserTaskConfig { |
| 18 | 18 | /** |
| 19 | 19 | * 候选人策略 |
| 20 | 20 | */ |
| 21 | - private Integer candidateStrategy; | |
| 22 | - | |
| 21 | + private Integer candidateStrategy; | |
| 23 | 22 | /** |
| 24 | 23 | * 候选人参数 |
| 25 | 24 | */ |
| 26 | - private String candidateParam; | |
| 25 | + private String candidateParam; | |
| 27 | 26 | |
| 28 | 27 | /** |
| 29 | 28 | * 字段权限 |
| ... | ... | @@ -34,11 +33,11 @@ public class SimpleModelUserTaskConfig { |
| 34 | 33 | * 审批方式 {@link BpmApproveMethodEnum } |
| 35 | 34 | */ |
| 36 | 35 | private Integer approveMethod; |
| 37 | - | |
| 38 | 36 | /** |
| 39 | 37 | * 通过比例 当审批方式为 多人会签(按通过比例) 需设置 |
| 40 | 38 | */ |
| 41 | 39 | private Integer approveRatio; |
| 40 | + | |
| 42 | 41 | /** |
| 43 | 42 | * 超时处理 |
| 44 | 43 | */ | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnFormUtils.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -27,6 +27,7 @@ public class BpmnModelUtils { |
| 27 | 27 | // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 |
| 28 | 28 | if (candidateStrategy == null) { |
| 29 | 29 | ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); |
| 30 | + // TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? NumberUtils.parseInt(element.getElementText()) : null; | |
| 30 | 31 | candidateStrategy = NumberUtils.parseInt(Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null)); |
| 31 | 32 | } |
| 32 | 33 | return candidateStrategy; |
| ... | ... | @@ -37,6 +38,7 @@ public class BpmnModelUtils { |
| 37 | 38 | BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); |
| 38 | 39 | if (candidateParam == null) { |
| 39 | 40 | ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); |
| 41 | + // TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? element.getElementText() : null; | |
| 40 | 42 | candidateParam = Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null); |
| 41 | 43 | } |
| 42 | 44 | return candidateParam; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -12,9 +12,9 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 15 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; | |
| 16 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig; | |
| 17 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig.RejectHandler; | |
| 15 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelConditionGroups; | |
| 16 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelUserTaskConfig; | |
| 17 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.simplemodel.SimpleModelUserTaskConfig.RejectHandler; | |
| 18 | 18 | import org.flowable.bpmn.BpmnAutoLayout; |
| 19 | 19 | import org.flowable.bpmn.model.Process; |
| 20 | 20 | import org.flowable.bpmn.model.*; |
| ... | ... | @@ -253,33 +253,26 @@ public class SimpleModelUtils { |
| 253 | 253 | return sequenceFlow; |
| 254 | 254 | } |
| 255 | 255 | |
| 256 | - // TODO-DONE @jason:要不改成 recursionNode 递归节点,然后把 build 名字让出来,专门用于构建各种 Node | |
| 257 | - // @芋艿 改成了 traverseNodeToBuildFlowNode, 连线的叫 traverseNodeToBuildSequenceFlow | |
| 258 | - // TODO-DONE @jason:node 改成 node,process 改成 process;更符合递归的感觉哈,处理当前节点 | |
| 256 | + // TODO @芋艿 改成了 traverseNodeToBuildFlowNode, 连线的叫 traverseNodeToBuildSequenceFlow | |
| 259 | 257 | private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { |
| 260 | 258 | // 判断是否有效节点 |
| 261 | - // TODO-DONE @jason:是不是写个 isValidNode 方法:判断是否为有效节点; | |
| 262 | 259 | if (!isValidNode(node)) { |
| 263 | 260 | return; |
| 264 | 261 | } |
| 265 | 262 | BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType()); |
| 266 | 263 | Assert.notNull(nodeType, "模型节点类型不支持"); |
| 267 | 264 | |
| 268 | - // TODO-DONE @jason:要不抽个 buildNode 方法,然后返回一个 List<FlowElement>,之后这个方法 addFlowElement;原因是,让当前这个方法,有主干逻辑;不然现在太长了; | |
| 269 | 265 | List<FlowElement> flowElements = buildFlowNode(node, nodeType); |
| 270 | 266 | flowElements.forEach(process::addFlowElement); |
| 271 | 267 | |
| 272 | 268 | // 如果不是网关类型的接口, 并且chileNode为空退出 |
| 273 | - // TODO-DONE @jason:建议这个判断去掉,可以更简洁一点;因为往下走;如果不成功,本身也就会结束哈;主要是,这里多了一个这样的判断,增加了理解成本; | |
| 274 | 269 | // 如果是“分支”节点,则递归处理条件 |
| 275 | 270 | if (BpmSimpleModelNodeType.isBranchNode(node.getType()) |
| 276 | 271 | && ArrayUtil.isNotEmpty(node.getConditionNodes())) { |
| 277 | - // TODO-DONE @jason:可以搞成 stream 写成一行哈 | |
| 278 | 272 | node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process)); |
| 279 | 273 | } |
| 280 | 274 | |
| 281 | 275 | // 如果有“子”节点,则递归处理子节点 |
| 282 | - // TODO-DONE @jason:这个,是不是不写判断,直接继续调用;因为本身 buildAndAddBpmnFlowNode 就会最开始判断了哈,就不重复判断了; | |
| 283 | 276 | traverseNodeToBuildFlowNode(node.getChildNode(), process); |
| 284 | 277 | } |
| 285 | 278 | |
| ... | ... | @@ -291,16 +284,13 @@ public class SimpleModelUtils { |
| 291 | 284 | List<FlowElement> list = new ArrayList<>(); |
| 292 | 285 | switch (nodeType) { |
| 293 | 286 | case START_NODE: { |
| 294 | - // TODO-DONE @jason:每个 nodeType,buildXXX 方法要不更明确,并且去掉 Bpmn; | |
| 295 | 287 | // @芋艿 改成 convert 是不是好理解一点 |
| 296 | 288 | StartEvent startEvent = convertStartNode(node); |
| 297 | 289 | list.add(startEvent); |
| 298 | 290 | break; |
| 299 | 291 | } |
| 300 | 292 | case APPROVE_NODE: { |
| 301 | - // TODO-DONE @jason:这个,搞成一个 buildUserTask,然后把下面这 2 种节点,搞在一起实现类;这样 buildNode 里面可以更简洁; | |
| 302 | - // TODO-DONE @jason:这里还有个想法,是不是可以所有的都叫 buildXXXNode,然后里面有一些是 bpmn 相关的构建,叫做 buildBpmnUserTask,用于区分; | |
| 303 | - // @芋艿 改成 convertXXXNode, , 方面里面使用 buildBpmnXXXNode. 是否更好理解 | |
| 293 | + // TODO @芋艿 改成 convertXXXNode, , 方面里面使用 buildBpmnXXXNode. 是否更好理解 | |
| 304 | 294 | // 转换审批节点 |
| 305 | 295 | List<FlowElement> flowElements = convertApproveNode(node); |
| 306 | 296 | list.addAll(flowElements); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
| ... | ... | @@ -51,31 +51,6 @@ public interface BpmModelService { |
| 51 | 51 | byte[] getModelBpmnXML(String id); |
| 52 | 52 | |
| 53 | 53 | /** |
| 54 | - * 保存流程模型的 BPMN XML | |
| 55 | - * | |
| 56 | - * @param id 编号 | |
| 57 | - * @param xmlBytes BPMN XML bytes | |
| 58 | - */ | |
| 59 | - // TODO @芋艿:感觉可以不修改这个方法,而是额外加一个方法;传入 id,bpmn,json; | |
| 60 | - void saveModelBpmnXml(String id, byte[] xmlBytes); | |
| 61 | - | |
| 62 | - /** | |
| 63 | - * 获得仿钉钉快搭模型的 JSON 数据 | |
| 64 | - * | |
| 65 | - * @param id 编号 | |
| 66 | - * @return JSON bytes | |
| 67 | - */ | |
| 68 | - byte[] getModelSimpleJson(String id); | |
| 69 | - | |
| 70 | - /** | |
| 71 | - * 保存仿钉钉快搭模型的 JSON 数据 | |
| 72 | - * | |
| 73 | - * @param id 编号 | |
| 74 | - * @param jsonBytes JSON bytes | |
| 75 | - */ | |
| 76 | - void saveModelSimpleJson(String id, byte[] jsonBytes); | |
| 77 | - | |
| 78 | - /** | |
| 79 | 54 | * 修改流程模型 |
| 80 | 55 | * |
| 81 | 56 | * @param updateReqVO 更新信息 |
| ... | ... | @@ -129,6 +104,6 @@ public interface BpmModelService { |
| 129 | 104 | */ |
| 130 | 105 | void updateSimpleModel(@Valid BpmSimpleModelUpdateReqVO reqVO); |
| 131 | 106 | |
| 132 | - // TODO @jason:另外个问题,因为是存储到 modelExtra 里,那需要 deploy 存储出快照。和 bpmn xml 一样。目前我想到的,就是存储到 BpmProcessDefinitionInfoDO 加一个 simple_model 字段,text 类型。可以看看还有啥方案? | |
| 107 | + // TODO @jason:另外个问题,因为是存储到 modelExtra 里,那需要 deploy 存储出快照。和 bpmn xml 一样。目前我想到的,就是存储到 BpmProcessDefinitionInfoDO 加一个 simple_model 字段,text 类型。可以看看还有啥方案?【重要】 | |
| 133 | 108 | |
| 134 | 109 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
| ... | ... | @@ -107,7 +107,7 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 107 | 107 | // 保存流程定义 |
| 108 | 108 | repositoryService.saveModel(model); |
| 109 | 109 | // 保存 BPMN XML |
| 110 | - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(bpmnXml)); | |
| 110 | + saveModelBpmnXml(model.getId(), bpmnXml); | |
| 111 | 111 | return model.getId(); |
| 112 | 112 | } |
| 113 | 113 | |
| ... | ... | @@ -125,7 +125,7 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 125 | 125 | // 更新模型 |
| 126 | 126 | repositoryService.saveModel(model); |
| 127 | 127 | // 更新 BPMN XML |
| 128 | - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(updateReqVO.getBpmnXml())); | |
| 128 | + saveModelBpmnXml(model.getId(), updateReqVO.getBpmnXml()); | |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | @Override |
| ... | ... | @@ -218,23 +218,24 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 218 | 218 | if (model == null) { |
| 219 | 219 | throw exception(MODEL_NOT_EXISTS); |
| 220 | 220 | } |
| 221 | - // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ 获取 仿钉钉快搭模型的JSON 数据 | |
| 221 | + // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据 | |
| 222 | 222 | byte[] jsonBytes = getModelSimpleJson(model.getId()); |
| 223 | 223 | return JsonUtils.parseObject(jsonBytes, BpmSimpleModelNodeVO.class); |
| 224 | 224 | } |
| 225 | 225 | |
| 226 | 226 | @Override |
| 227 | 227 | public void updateSimpleModel(BpmSimpleModelUpdateReqVO reqVO) { |
| 228 | - // 1.1 校验流程模型存在 | |
| 228 | + // 1. 校验流程模型存在 | |
| 229 | 229 | Model model = getModel(reqVO.getModelId()); |
| 230 | 230 | if (model == null) { |
| 231 | 231 | throw exception(MODEL_NOT_EXISTS); |
| 232 | 232 | } |
| 233 | - // 1.2 JSON 转换成 bpmnModel | |
| 233 | + | |
| 234 | + // 2.1 JSON 转换成 bpmnModel | |
| 234 | 235 | BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel()); |
| 235 | - // 2.1 保存 Bpmn XML | |
| 236 | - saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(BpmnModelUtils.getBpmnXml(bpmnModel))); | |
| 237 | - // 2.2 保存 JSON 数据 | |
| 236 | + // 2.2 保存 Bpmn XML | |
| 237 | + saveModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); | |
| 238 | + // 2.3 保存 JSON 数据 | |
| 238 | 239 | saveModelSimpleJson(model.getId(), JsonUtils.toJsonByte(reqVO.getSimpleModel())); |
| 239 | 240 | } |
| 240 | 241 | |
| ... | ... | @@ -266,21 +267,18 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 266 | 267 | } |
| 267 | 268 | } |
| 268 | 269 | |
| 269 | - @Override | |
| 270 | - public void saveModelBpmnXml(String id, byte[] xmlBytes) { | |
| 271 | - if (ArrayUtil.isEmpty(xmlBytes)) { | |
| 270 | + private void saveModelBpmnXml(String id, String bpmnXml) { | |
| 271 | + if (StrUtil.isEmpty(bpmnXml)) { | |
| 272 | 272 | return; |
| 273 | 273 | } |
| 274 | - repositoryService.addModelEditorSource(id, xmlBytes); | |
| 274 | + repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml)); | |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | - @Override | |
| 278 | - public byte[] getModelSimpleJson(String id) { | |
| 277 | + private byte[] getModelSimpleJson(String id) { | |
| 279 | 278 | return repositoryService.getModelEditorSourceExtra(id); |
| 280 | 279 | } |
| 281 | 280 | |
| 282 | - @Override | |
| 283 | - public void saveModelSimpleJson(String id, byte[] jsonBytes) { | |
| 281 | + private void saveModelSimpleJson(String id, byte[] jsonBytes) { | |
| 284 | 282 | if (ArrayUtil.isEmpty(jsonBytes)) { |
| 285 | 283 | return; |
| 286 | 284 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java
| ... | ... | @@ -13,6 +13,8 @@ import java.util.Collection; |
| 13 | 13 | */ |
| 14 | 14 | public interface BpmProcessInstanceCopyService { |
| 15 | 15 | |
| 16 | + // TODO @jason:要不把 createProcessInstanceCopy 搞 2 个方法,一个方法参数是之前的 userIds、taskId;一个方法是现在 userIds、processInstanceId、taskId、taskName; | |
| 17 | + | |
| 16 | 18 | /** |
| 17 | 19 | * 流程实例的抄送 |
| 18 | 20 | * | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
| ... | ... | @@ -127,23 +127,23 @@ public interface BpmProcessInstanceService { |
| 127 | 127 | void updateProcessInstanceWhenCancel(FlowableCancelledEvent event); |
| 128 | 128 | |
| 129 | 129 | /** |
| 130 | - * 更新 ProcessInstance 拓展记录为完成 | |
| 130 | + * 更新 ProcessInstance 为完成 | |
| 131 | 131 | * |
| 132 | 132 | * @param instance 流程任务 |
| 133 | 133 | */ |
| 134 | 134 | void updateProcessInstanceWhenApprove(ProcessInstance instance); |
| 135 | 135 | |
| 136 | 136 | /** |
| 137 | - * 更新 ProcessInstance 拓展记录为不通过 | |
| 137 | + * 更新 ProcessInstance 为不通过 | |
| 138 | 138 | * |
| 139 | 139 | * @param id 流程编号 |
| 140 | - * @param currentActivityId 当前的活动Id | |
| 140 | + * @param currentActivityId 当前的活动编号 | |
| 141 | 141 | * @param reason 理由。例如说,审批不通过时,需要传递该值 |
| 142 | 142 | */ |
| 143 | 143 | void updateProcessInstanceReject(String id, String currentActivityId, String reason); |
| 144 | 144 | |
| 145 | 145 | /** |
| 146 | - * 当流程结束时候。 更新 ProcessInstance | |
| 146 | + * 当流程结束时候,更新 ProcessInstance 为通过 | |
| 147 | 147 | * |
| 148 | 148 | * @param instance 流程任务 |
| 149 | 149 | */ | ... | ... |
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 节点, 结束流程 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/BpmTaskService.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -335,14 +335,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 335 | 335 | // 2.2 添加评论 |
| 336 | 336 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| 337 | 337 | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); |
| 338 | + | |
| 338 | 339 | // 3.1 解析用户任务的拒绝处理类型 |
| 339 | 340 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 341 | + // TODO @jason:342 到 344 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy | |
| 340 | 342 | UserTask flowElement = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| 341 | 343 | Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_HANDLER_TYPE)); |
| 342 | 344 | BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); |
| 343 | - // 3.2 类型为驳回到指定的任务节点 | |
| 345 | + // 3.2 类型为驳回到指定的任务节点 TODO @jason:下面这种判断,最好是 JSON | |
| 344 | 346 | if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) { |
| 347 | + // TODO @jason:348 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy | |
| 345 | 348 | String returnTaskId = BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); |
| 349 | + // TODO @jason:这里如果找不到,直接抛出系统异常;因为说白了,已经不是业务异常啦。 | |
| 346 | 350 | if (returnTaskId == null) { |
| 347 | 351 | throw exception(TASK_RETURN_NOT_ASSIGN_TARGET_TASK_ID); |
| 348 | 352 | } |
| ... | ... | @@ -351,6 +355,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 351 | 355 | returnTask(userId, returnReq); |
| 352 | 356 | return; |
| 353 | 357 | } else if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER) { |
| 358 | + // TODO @jason:微信沟通,去掉类似的逻辑; | |
| 354 | 359 | // 3.3 按拒绝人数终止流程 |
| 355 | 360 | if (!flowElement.hasMultiInstanceLoopCharacteristics()) { |
| 356 | 361 | log.error("[rejectTask] 按拒绝人数终止流程类型,只能用于会签任务. 当前任务【{}】不是会签任务", task.getId()); |
| ... | ... | @@ -362,7 +367,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 362 | 367 | return; |
| 363 | 368 | } |
| 364 | 369 | // 3.4 其他情况 终止流程。 |
| 365 | - processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), task.getTaskDefinitionKey(), reqVO.getReason()); | |
| 370 | + processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), | |
| 371 | + task.getTaskDefinitionKey(), reqVO.getReason()); | |
| 366 | 372 | } |
| 367 | 373 | |
| 368 | 374 | /** | ... | ... |