提交 | 用户 | 时间
|
e7c126
|
1 |
package com.iailab.module.bpm.framework.flowable.core.candidate; |
H |
2 |
|
|
3 |
import cn.hutool.core.collection.CollUtil; |
|
4 |
import cn.hutool.core.lang.Assert; |
bb2880
|
5 |
import cn.hutool.core.util.ObjectUtil; |
e7c126
|
6 |
import cn.hutool.core.util.StrUtil; |
bb2880
|
7 |
import cn.hutool.extra.spring.SpringUtil; |
e7c126
|
8 |
import com.iailab.framework.common.enums.CommonStatusEnum; |
bb2880
|
9 |
import com.iailab.framework.common.util.object.ObjectUtils; |
e7c126
|
10 |
import com.iailab.framework.datapermission.core.annotation.DataPermission; |
bb2880
|
11 |
import com.iailab.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; |
H |
12 |
import com.iailab.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; |
e7c126
|
13 |
import com.iailab.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
H |
14 |
import com.iailab.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
bb2880
|
15 |
import com.iailab.module.bpm.service.task.BpmProcessInstanceService; |
e7c126
|
16 |
import com.iailab.module.system.api.user.AdminUserApi; |
H |
17 |
import com.iailab.module.system.api.user.dto.AdminUserRespDTO; |
|
18 |
import com.google.common.annotations.VisibleForTesting; |
|
19 |
import lombok.extern.slf4j.Slf4j; |
|
20 |
import org.flowable.bpmn.model.BpmnModel; |
bb2880
|
21 |
import org.flowable.bpmn.model.FlowElement; |
e7c126
|
22 |
import org.flowable.bpmn.model.UserTask; |
H |
23 |
import org.flowable.engine.delegate.DelegateExecution; |
bb2880
|
24 |
import org.flowable.engine.runtime.ProcessInstance; |
e7c126
|
25 |
|
bb2880
|
26 |
import java.util.*; |
e7c126
|
27 |
|
H |
28 |
import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; |
|
29 |
import static com.iailab.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; |
|
30 |
import static com.iailab.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; |
|
31 |
|
|
32 |
/** |
|
33 |
* {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 |
|
34 |
* |
|
35 |
* @author iailab |
|
36 |
*/ |
|
37 |
@Slf4j |
|
38 |
public class BpmTaskCandidateInvoker { |
|
39 |
|
|
40 |
private final Map<BpmTaskCandidateStrategyEnum, BpmTaskCandidateStrategy> strategyMap = new HashMap<>(); |
|
41 |
|
|
42 |
private final AdminUserApi adminUserApi; |
|
43 |
|
|
44 |
public BpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList, |
|
45 |
AdminUserApi adminUserApi) { |
|
46 |
strategyList.forEach(strategy -> { |
|
47 |
BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy); |
|
48 |
Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy()); |
|
49 |
}); |
|
50 |
this.adminUserApi = adminUserApi; |
|
51 |
} |
|
52 |
|
|
53 |
/** |
|
54 |
* 校验流程模型的任务分配规则全部都配置了 |
|
55 |
* 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! |
|
56 |
* |
|
57 |
* @param bpmnBytes BPMN XML |
|
58 |
*/ |
|
59 |
public void validateBpmnConfig(byte[] bpmnBytes) { |
|
60 |
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); |
|
61 |
assert bpmnModel != null; |
|
62 |
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); |
|
63 |
// 遍历所有的 UserTask,校验审批人配置 |
|
64 |
userTaskList.forEach(userTask -> { |
bb2880
|
65 |
// 1.1 非人工审批,无需校验审批人配置 |
H |
66 |
Integer approveType = BpmnModelUtils.parseApproveType(userTask); |
|
67 |
if (ObjectUtils.equalsAny(approveType, |
|
68 |
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), |
|
69 |
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { |
|
70 |
return; |
|
71 |
} |
|
72 |
// 1.2 非空校验 |
e7c126
|
73 |
Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); |
H |
74 |
String param = BpmnModelUtils.parseCandidateParam(userTask); |
|
75 |
if (strategy == null) { |
|
76 |
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); |
|
77 |
} |
|
78 |
BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); |
|
79 |
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { |
|
80 |
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); |
|
81 |
} |
|
82 |
// 2. 具体策略校验 |
|
83 |
getCandidateStrategy(strategy).validateParam(param); |
|
84 |
}); |
|
85 |
} |
|
86 |
|
|
87 |
/** |
|
88 |
* 计算任务的候选人 |
|
89 |
* |
|
90 |
* @param execution 执行任务 |
|
91 |
* @return 用户编号集合 |
|
92 |
*/ |
|
93 |
@DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 |
bb2880
|
94 |
public Set<Long> calculateUsersByTask(DelegateExecution execution) { |
H |
95 |
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 |
|
96 |
FlowElement flowElement = execution.getCurrentFlowElement(); |
|
97 |
Integer approveType = BpmnModelUtils.parseApproveType(flowElement); |
|
98 |
if (ObjectUtils.equalsAny(approveType, |
|
99 |
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), |
|
100 |
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { |
|
101 |
return new HashSet<>(); |
|
102 |
} |
|
103 |
|
e7c126
|
104 |
// 1.1 计算任务的候选人 |
bb2880
|
105 |
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); |
H |
106 |
String param = BpmnModelUtils.parseCandidateParam(flowElement); |
|
107 |
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); |
e7c126
|
108 |
// 1.2 移除被禁用的用户 |
H |
109 |
removeDisableUsers(userIds); |
|
110 |
|
bb2880
|
111 |
// 2. 候选人为空时,根据“审批人为空”的配置补充 |
e7c126
|
112 |
if (CollUtil.isEmpty(userIds)) { |
bb2880
|
113 |
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) |
H |
114 |
.calculateUsersByTask(execution, param); |
|
115 |
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! |
e7c126
|
116 |
} |
bb2880
|
117 |
|
H |
118 |
// 3. 移除发起人的用户 |
|
119 |
ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class) |
|
120 |
.getProcessInstance(execution.getProcessInstanceId()); |
|
121 |
Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId()); |
|
122 |
removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId())); |
|
123 |
return userIds; |
|
124 |
} |
|
125 |
|
|
126 |
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, |
|
127 |
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { |
|
128 |
// 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 |
|
129 |
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); |
|
130 |
Integer approveType = BpmnModelUtils.parseApproveType(flowElement); |
|
131 |
if (ObjectUtils.equalsAny(approveType, |
|
132 |
BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), |
|
133 |
BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { |
|
134 |
return new HashSet<>(); |
|
135 |
} |
|
136 |
|
|
137 |
// 1.1 计算任务的候选人 |
|
138 |
Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); |
|
139 |
String param = BpmnModelUtils.parseCandidateParam(flowElement); |
|
140 |
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, |
|
141 |
startUserId, processDefinitionId, processVariables); |
|
142 |
// 1.2 移除被禁用的用户 |
|
143 |
removeDisableUsers(userIds); |
|
144 |
|
|
145 |
// 2. 候选人为空时,根据“审批人为空”的配置补充 |
|
146 |
if (CollUtil.isEmpty(userIds)) { |
|
147 |
userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) |
|
148 |
.calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables); |
|
149 |
// ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! |
|
150 |
} |
|
151 |
|
|
152 |
// 3. 移除发起人的用户 |
|
153 |
removeStartUserIfSkip(userIds, flowElement, startUserId); |
e7c126
|
154 |
return userIds; |
H |
155 |
} |
|
156 |
|
|
157 |
@VisibleForTesting |
|
158 |
void removeDisableUsers(Set<Long> assigneeUserIds) { |
|
159 |
if (CollUtil.isEmpty(assigneeUserIds)) { |
|
160 |
return; |
|
161 |
} |
|
162 |
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds); |
|
163 |
assigneeUserIds.removeIf(id -> { |
|
164 |
AdminUserRespDTO user = userMap.get(id); |
bb2880
|
165 |
return user == null || CommonStatusEnum.isDisable(user.getStatus()); |
e7c126
|
166 |
}); |
H |
167 |
} |
|
168 |
|
bb2880
|
169 |
/** |
H |
170 |
* 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人 |
|
171 |
* |
|
172 |
* 注意:如果只有一个候选人,则不处理,避免无法审批 |
|
173 |
* |
|
174 |
* @param assigneeUserIds 当前分配的候选人 |
|
175 |
* @param flowElement 当前节点 |
|
176 |
* @param startUserId 发起人 |
|
177 |
*/ |
|
178 |
@VisibleForTesting |
|
179 |
void removeStartUserIfSkip(Set<Long> assigneeUserIds, FlowElement flowElement, Long startUserId) { |
|
180 |
if (CollUtil.size(assigneeUserIds) <= 1) { |
|
181 |
return; |
|
182 |
} |
|
183 |
Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement); |
|
184 |
if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { |
|
185 |
return; |
|
186 |
} |
|
187 |
assigneeUserIds.remove(startUserId); |
|
188 |
} |
|
189 |
|
e7c126
|
190 |
private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { |
H |
191 |
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); |
|
192 |
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); |
|
193 |
BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum); |
|
194 |
Assert.notNull(strategyObj, "策略(%s) 不存在", strategy); |
|
195 |
return strategyObj; |
|
196 |
} |
|
197 |
|
|
198 |
} |