潘志宝
9 天以前 9d5be382e52f9ac57199d5ef75cc23f925a4cdb0
提交 | 用户 | 时间
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 }