Commit 9ff56403d6782d94a5d1fc75aea8a17b34cecaad

Authored by YunaiV
1 parent 8a5638bd

【代码评审】BPM:下一个审批人

yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -24,7 +24,7 @@ public interface ErrorCodeConstants { @@ -24,7 +24,7 @@ public interface ErrorCodeConstants {
24 ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); 24 ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件");
25 ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); 25 ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在");
26 ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); 26 ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员");
27 - ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); 27 + ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】");
28 28
29 // ========== 流程定义 1-009-003-000 ========== 29 // ========== 流程定义 1-009-003-000 ==========
30 ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); 30 ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
@@ -41,7 +41,7 @@ public interface ErrorCodeConstants { @@ -41,7 +41,7 @@ public interface ErrorCodeConstants {
41 ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); 41 ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
42 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); 42 ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
43 ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); 43 ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败");
44 - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); 44 + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置");
45 45
46 // ========== 流程任务 1-009-005-000 ========== 46 // ========== 流程任务 1-009-005-000 ==========
47 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); 47 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
@@ -24,8 +24,8 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> { @@ -24,8 +24,8 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable<Integer> {
24 MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), 24 MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
25 POST(22, "岗位"), 25 POST(22, "岗位"),
26 USER(30, "用户"), 26 USER(30, "用户"),
27 - APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人  
28 - START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 27 + APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人
  28 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人
29 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 29 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景
30 START_USER_DEPT_LEADER(37, "发起人部门负责人"), 30 START_USER_DEPT_LEADER(37, "发起人部门负责人"),
31 START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), 31 START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"),
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
@@ -27,12 +27,14 @@ public class BpmnVariableConstants { @@ -27,12 +27,14 @@ public class BpmnVariableConstants {
27 * 流程实例的变量 - 发起用户选择的审批人 Map 27 * 流程实例的变量 - 发起用户选择的审批人 Map
28 * 28 *
29 * @see ProcessInstance#getProcessVariables() 29 * @see ProcessInstance#getProcessVariables()
  30 + * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT
30 */ 31 */
31 public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; 32 public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
32 /** 33 /**
33 * 流程实例的变量 - 审批人选择的审批人 Map 34 * 流程实例的变量 - 审批人选择的审批人 Map
34 * 35 *
35 * @see ProcessInstance#getProcessVariables() 36 * @see ProcessInstance#getProcessVariables()
  37 + * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT
36 */ 38 */
37 public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; 39 public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES";
38 /** 40 /**
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -808,16 +808,16 @@ public class BpmnModelUtils { @@ -808,16 +808,16 @@ public class BpmnModelUtils {
808 // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 808 // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的
809 if (currentElement instanceof ExclusiveGateway) { 809 if (currentElement instanceof ExclusiveGateway) {
810 // 查找满足条件的 SequenceFlow 路径 810 // 查找满足条件的 SequenceFlow 路径
811 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); 811 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables);
812 // 遍历满足条件的 SequenceFlow 路径 812 // 遍历满足条件的 SequenceFlow 路径
813 if (matchSequenceFlow != null) { 813 if (matchSequenceFlow != null) {
814 simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); 814 simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements);
815 } 815 }
816 } 816 }
817 // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 817 // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的
818 - else if (currentElement instanceof InclusiveGateway) { 818 + else if (currentElement instanceof InclusiveGateway) {
819 // 查找满足条件的 SequenceFlow 路径 819 // 查找满足条件的 SequenceFlow 路径
820 - Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); 820 + Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables);
821 // 遍历满足条件的 SequenceFlow 路径 821 // 遍历满足条件的 SequenceFlow 路径
822 matchSequenceFlows.forEach( 822 matchSequenceFlows.forEach(
823 flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); 823 flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements));
@@ -889,7 +889,7 @@ public class BpmnModelUtils { @@ -889,7 +889,7 @@ public class BpmnModelUtils {
889 private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, 889 private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel,
890 Map<String, Object> variables, List<FlowNode> nextFlowNodes) { 890 Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
891 // 查找满足条件的 SequenceFlow 路径 891 // 查找满足条件的 SequenceFlow 路径
892 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); 892 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables);
893 // 遍历满足条件的 SequenceFlow 路径 893 // 遍历满足条件的 SequenceFlow 路径
894 if (matchSequenceFlow != null) { 894 if (matchSequenceFlow != null) {
895 FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); 895 FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef());
@@ -906,7 +906,7 @@ public class BpmnModelUtils { @@ -906,7 +906,7 @@ public class BpmnModelUtils {
906 * @param variables 流程变量 906 * @param variables 流程变量
907 * @return 符合条件的路径 907 * @return 符合条件的路径
908 */ 908 */
909 - private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map<String, Object> variables){ 909 + private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) {
910 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), 910 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
911 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) 911 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
912 && (evalConditionExpress(variables, flow.getConditionExpression()))); 912 && (evalConditionExpress(variables, flow.getConditionExpression())));
@@ -932,7 +932,7 @@ public class BpmnModelUtils { @@ -932,7 +932,7 @@ public class BpmnModelUtils {
932 private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, 932 private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel,
933 Map<String, Object> variables, List<FlowNode> nextFlowNodes) { 933 Map<String, Object> variables, List<FlowNode> nextFlowNodes) {
934 // 查找满足条件的 SequenceFlow 路径集合 934 // 查找满足条件的 SequenceFlow 路径集合
935 - Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlows(gateway, variables); 935 + Collection<SequenceFlow> matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables);
936 // 遍历满足条件的 SequenceFlow 路径,获取目标节点 936 // 遍历满足条件的 SequenceFlow 路径,获取目标节点
937 matchSequenceFlows.forEach(flow -> { 937 matchSequenceFlows.forEach(flow -> {
938 FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); 938 FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef());
@@ -949,7 +949,7 @@ public class BpmnModelUtils { @@ -949,7 +949,7 @@ public class BpmnModelUtils {
949 * @param variables 流程变量 949 * @param variables 流程变量
950 * @return 符合条件的路径 950 * @return 符合条件的路径
951 */ 951 */
952 - private static Collection<SequenceFlow> findMatchSequenceFlows(Gateway gateway, Map<String, Object> variables) { 952 + private static Collection<SequenceFlow> findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map<String, Object> variables) {
953 // 查找满足条件的 SequenceFlow 路径 953 // 查找满足条件的 SequenceFlow 路径
954 Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), 954 Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
955 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) 955 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -243,15 +243,15 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -243,15 +243,15 @@ public class BpmModelServiceImpl implements BpmModelService {
243 if (startEvent == null) { 243 if (startEvent == null) {
244 throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); 244 throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS);
245 } 245 }
246 - // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 246 + // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况
247 List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows(); 247 List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
248 - if (CollUtil.isNotEmpty(outgoingFlows)){  
249 - // 2.1 获取第一个用户任务节点 248 + // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。
  249 + // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务???
  250 + if (CollUtil.isNotEmpty(outgoingFlows)) {
250 FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); 251 FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
251 - // 2.2 获取审批人策略  
252 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); 252 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement);
253 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){  
254 - throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); 253 + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
  254 + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName());
255 } 255 }
256 } 256 }
257 // 3. 校验 UserTask 的 name 都配置了 257 // 3. 校验 UserTask 的 name 都配置了
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
@@ -97,7 +97,7 @@ public interface BpmProcessInstanceService { @@ -97,7 +97,7 @@ public interface BpmProcessInstanceService {
97 BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); 97 BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO);
98 98
99 /** 99 /**
100 - * 获取下一个执行节点信息。 100 + * 获取下一个执行节点信息
101 * 101 *
102 * @param loginUserId 登录人的用户编号 102 * @param loginUserId 登录人的用户编号
103 * @param reqVO 请求信息 103 * @param reqVO 请求信息
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -11,7 +11,6 @@ import cn.hutool.core.util.StrUtil; @@ -11,7 +11,6 @@ import cn.hutool.core.util.StrUtil;
11 import cn.iocoder.yudao.framework.common.pojo.PageResult; 11 import cn.iocoder.yudao.framework.common.pojo.PageResult;
12 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 12 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
13 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 13 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
14 -import cn.iocoder.yudao.framework.common.util.number.NumberUtils;  
15 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 14 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
16 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 15 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
17 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; 16 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
@@ -179,9 +178,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -179,9 +178,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
179 startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); 178 startUserId = Long.valueOf(historicProcessInstance.getStartUserId());
180 processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); 179 processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance);
181 // 合并 DB 和前端传递的流量变量,以前端的为主 180 // 合并 DB 和前端传递的流量变量,以前端的为主
182 - Map<String, Object> historicVariables = historicProcessInstance.getProcessVariables();  
183 - if (CollUtil.isNotEmpty(historicVariables)) {  
184 - processVariables.putAll(historicVariables); 181 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) {
  182 + processVariables.putAll(historicProcessInstance.getProcessVariables());
185 } 183 }
186 } 184 }
187 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { 185 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) {
@@ -222,9 +220,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -222,9 +220,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
222 List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, 220 List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
223 processDefinitionInfo, 221 processDefinitionInfo,
224 processVariables, activities); 222 processVariables, activities);
225 - // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点  
226 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){  
227 - simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); 223 + // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点
  224 + // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空
  225 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) {
  226 + simulateActivityNodes.removeIf(node ->
  227 + BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()));
228 } 228 }
229 229
230 // 4. 拼接最终数据 230 // 4. 拼接最终数据
@@ -234,46 +234,37 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -234,46 +234,37 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
234 234
235 @Override 235 @Override
236 public List<ActivityNode> getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { 236 public List<ActivityNode> getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) {
237 - // 1.1 校验任务存在  
238 - Task task = taskService.getTask(reqVO.getTaskId());  
239 - if (task == null) {  
240 - throw exception(TASK_NOT_EXISTS);  
241 - }  
242 - // 1.2 校验任务是否由当前用户审批  
243 - if (StrUtil.isNotBlank(task.getAssignee())  
244 - && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) {  
245 - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);  
246 - }  
247 - // 1.3 校验流程实例存在 237 + // 1.1 校验任务存在,且是当前用户的
  238 + Task task = taskService.validateTask(loginUserId, reqVO.getTaskId());
  239 + // 1.2 校验流程实例存在
248 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); 240 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId());
249 if (instance == null) { 241 if (instance == null) {
250 throw exception(PROCESS_INSTANCE_NOT_EXISTS); 242 throw exception(PROCESS_INSTANCE_NOT_EXISTS);
251 } 243 }
252 - // 1.4 校验BpmnModel  
253 - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId());  
254 - if (bpmnModel == null) {  
255 - return null;  
256 - }  
257 - //1.5 流程实例是否存在  
258 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); 244 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId());
259 if (historicProcessInstance == null) { 245 if (historicProcessInstance == null) {
260 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); 246 throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
261 } 247 }
  248 + // 1.3 校验BpmnModel
  249 + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId());
  250 + if (bpmnModel == null) {
  251 + return null;
  252 + }
262 253
263 - // 2 设置流程变量 254 + // 2. 设置流程变量
264 Map<String, Object> processVariables = new HashMap<>(); 255 Map<String, Object> processVariables = new HashMap<>();
265 // 2.1 获取历史中流程变量 256 // 2.1 获取历史中流程变量
266 - Map<String, Object> historicVariables = historicProcessInstance.getProcessVariables();  
267 - if (CollUtil.isNotEmpty(historicVariables)) {  
268 - processVariables.putAll(historicVariables); 257 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) {
  258 + processVariables.putAll(historicProcessInstance.getProcessVariables());
269 } 259 }
270 // 2.2 合并前端传递的流程变量,以前端为准 260 // 2.2 合并前端传递的流程变量,以前端为准
271 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { 261 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) {
272 processVariables.putAll(reqVO.getProcessVariables()); 262 processVariables.putAll(reqVO.getProcessVariables());
273 } 263 }
  264 +
274 // 3 获取当前任务节点的信息 265 // 3 获取当前任务节点的信息
275 - FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());  
276 // 3.1 获取下一个将要执行的节点集合 266 // 3.1 获取下一个将要执行的节点集合
  267 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
277 List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); 268 List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
278 return convertList(nextFlowNodes, node -> { 269 return convertList(nextFlowNodes, node -> {
279 List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), 270 List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),
@@ -290,6 +281,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -290,6 +281,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
290 .setName(node.getName()) 281 .setName(node.getName())
291 .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) 282 .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
292 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) 283 .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node))
  284 + // TODO @小北:先把 candidateUserIds 设置完,然后最后拼接 candidateUsers 信息。这样,如果有多个节点,就不用重复查询啦;类似 buildApprovalDetail 思路;
  285 + // TODO 先拼接处 List ActivityNode
  286 + // TODO 接着,再起一段,处理 adminUserApi.getUserMap(candidateUserIds)、deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId))
293 .setCandidateUsers(candidateUsers); 287 .setCandidateUsers(candidateUsers);
294 }); 288 });
295 } 289 }
@@ -745,16 +739,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -745,16 +739,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
745 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID 739 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
746 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 740 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
747 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); 741 BpmProcessInstanceStatusEnum.RUNNING.getStatus());
748 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为  
749 - // true,不影响没配置  
750 - // skipExpression 的节点 742 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点
751 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { 743 if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
752 // 设置流程变量,发起人自选审批人 744 // 设置流程变量,发起人自选审批人
753 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, 745 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
754 startUserSelectAssignees); 746 startUserSelectAssignees);
755 -// // 设置流程变量,审批人自选审批人  
756 -// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,  
757 -// startUserSelectAssignees);  
758 } 747 }
759 748
760 // 3. 创建流程 749 // 3. 创建流程
@@ -796,7 +785,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -796,7 +785,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
796 return; 785 return;
797 } 786 }
798 787
799 - // 2.1 移除掉不是发起人或者审批人自选审批人节点 788 + // 2.1 移除掉不是发起人自选审批人节点
800 activityNodes.removeIf(task -> 789 activityNodes.removeIf(task ->
801 ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); 790 ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()));
802 // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 791 // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
@@ -93,6 +93,14 @@ public interface BpmTaskService { @@ -93,6 +93,14 @@ public interface BpmTaskService {
93 List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); 93 List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId, Boolean asc);
94 94
95 /** 95 /**
  96 + * 校验任务是否存在,并且是否是分配给自己的任务
  97 + *
  98 + * @param userId 用户 id
  99 + * @param taskId task id
  100 + */
  101 + Task validateTask(Long userId, String taskId);
  102 +
  103 + /**
96 * 获取任务 104 * 获取任务
97 * 105 *
98 * @param id 任务编号 106 * @param id 任务编号
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; @@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
21 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; 21 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
22 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; 22 import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
23 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 23 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
24 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;  
25 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; 24 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
26 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 25 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
27 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 26 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
@@ -277,13 +276,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -277,13 +276,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
277 return query.list(); 276 return query.list();
278 } 277 }
279 278
280 - /**  
281 - * 校验任务是否存在,并且是否是分配给自己的任务  
282 - *  
283 - * @param userId 用户 id  
284 - * @param taskId task id  
285 - */  
286 - private Task validateTask(Long userId, String taskId) { 279 + @Override
  280 + public Task validateTask(Long userId, String taskId) {
287 Task task = validateTaskExist(taskId); 281 Task task = validateTaskExist(taskId);
288 // 为什么判断 assignee 非空的情况下? 282 // 为什么判断 assignee 非空的情况下?
289 // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 283 // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过
@@ -552,12 +546,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -552,12 +546,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
552 // 2.3 调用 BPM complete 去完成任务 546 // 2.3 调用 BPM complete 去完成任务
553 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 547 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
554 if (CollUtil.isNotEmpty(reqVO.getVariables())) { 548 if (CollUtil.isNotEmpty(reqVO.getVariables())) {
555 - // 校验传递的参数中是否为下一个将要执行的任务节点 549 + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑
556 Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), 550 Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(),
557 bpmnModel, reqVO.getNextAssignees(), instance); 551 bpmnModel, reqVO.getNextAssignees(), instance);
  552 + // 完成任务
558 runtimeService.setVariables(task.getProcessInstanceId(), variables); 553 runtimeService.setVariables(task.getProcessInstanceId(), variables);
559 taskService.complete(task.getId(), variables, true); 554 taskService.complete(task.getId(), variables, true);
560 } else { 555 } else {
  556 + // 完成任务
561 taskService.complete(task.getId()); 557 taskService.complete(task.getId());
562 } 558 }
563 559
@@ -579,54 +575,52 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -579,54 +575,52 @@ public class BpmTaskServiceImpl implements BpmTaskService {
579 * @param processInstance 流程实例 575 * @param processInstance 流程实例
580 */ 576 */
581 private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel, 577 private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel,
582 - Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) { 578 + Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) {
583 // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 579 // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人
584 - if (CollUtil.isEmpty(nextAssignees)){ 580 + // TODO @小北:会出现漏选,其实需要的情况哇?
  581 + if (CollUtil.isEmpty(nextAssignees)) {
585 return variables; 582 return variables;
586 } 583 }
587 - // 1. 获取当前任务节点的信息 584 + // 1. 获取下一个将要执行的节点集合
588 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); 585 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
589 - // 2. 获取下一个将要执行的节点集合  
590 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); 586 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
  587 +
  588 + // 2. 循环下一个将要执行的节点集合
591 Map<String, List<Long>> processVariables = new HashMap<>(); 589 Map<String, List<Long>> processVariables = new HashMap<>();
592 - // 3. 循环下一个将要执行的节点集合  
593 for (FlowNode nextFlowNode : nextFlowNodes) { 590 for (FlowNode nextFlowNode : nextFlowNodes) {
594 - // 3.1 获取下一个将要执行节点中的审批人策略  
595 - Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);  
596 - // 3.2 判断节点是否为执行节点,仅校验节点  
597 if (!nextAssignees.containsKey(nextFlowNode.getId())) { 591 if (!nextAssignees.containsKey(nextFlowNode.getId())) {
598 throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); 592 throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName());
599 } 593 }
600 - // 3.3 获取节点中的审批人  
601 List<Long> assignees = nextAssignees.get(nextFlowNode.getId()); 594 List<Long> assignees = nextAssignees.get(nextFlowNode.getId());
602 - // 3.4 流程变量  
603 - // 3.5 如果节点中的审批人策略为 发起人自选 595 + // 2.1 情况一:如果节点中的审批人策略为 发起人自选
  596 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
604 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { 597 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
605 processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); 598 processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());
606 - if(processVariables == null){ 599 + if (processVariables == null) {
607 processVariables = new HashMap<>(); 600 processVariables = new HashMap<>();
608 } 601 }
609 List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); 602 List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId());
610 - // 如果当前节点已经存在审批人,则不允许覆盖 603 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖
611 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { 604 if (CollUtil.isNotEmpty(startUserSelectAssignee)) {
612 continue; 605 continue;
613 } 606 }
614 // 如果节点存在,但未配置审批人 607 // 如果节点存在,但未配置审批人
615 - if (CollUtil.isEmpty(assignees)){ 608 + if (CollUtil.isEmpty(assignees)) {
616 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 609 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
617 } 610 }
618 // 校验通过的全部节点和审批人 611 // 校验通过的全部节点和审批人
619 processVariables.put(nextFlowNode.getId(), assignees); 612 processVariables.put(nextFlowNode.getId(), assignees);
620 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); 613 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables);
621 } 614 }
622 - // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空  
623 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ 615 + // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
  616 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
624 // 如果节点存在,但未配置审批人 617 // 如果节点存在,但未配置审批人
625 if (CollUtil.isEmpty(assignees)) { 618 if (CollUtil.isEmpty(assignees)) {
626 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 619 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
627 } 620 }
628 // 校验通过的全部节点和审批人 621 // 校验通过的全部节点和审批人
629 processVariables.put(nextFlowNode.getId(), assignees); 622 processVariables.put(nextFlowNode.getId(), assignees);
  623 + // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖?
630 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); 624 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables);
631 } 625 }
632 } 626 }
@@ -1094,6 +1088,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -1094,6 +1088,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1094 1088
1095 @Override 1089 @Override
1096 @Transactional(rollbackFor = Exception.class) 1090 @Transactional(rollbackFor = Exception.class)
  1091 + @SuppressWarnings("DataFlowIssue")
1097 public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { 1092 public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) {
1098 // 1.1 校验 task 可以被减签 1093 // 1.1 校验 task 可以被减签
1099 Task task = validateTaskCanSignDelete(reqVO.getId()); 1094 Task task = validateTaskCanSignDelete(reqVO.getId());