Commit 41b9ab2ba53b46d23cbc78b194639e8df70b23c2

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