Commit 149cab1dac170df649be1c0d08e446b58bb42a11

Authored by lizhixian
2 parents c2789f62 24261cf7

Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
@@ -45,10 +45,6 @@ public class BpmModelMetaInfoVO { @@ -45,10 +45,6 @@ public class BpmModelMetaInfoVO {
45 @Schema(description = "表单编号", example = "1024") 45 @Schema(description = "表单编号", example = "1024")
46 private Long formId; // formType 为 NORMAL 使用,必须非空 46 private Long formId; // formType 为 NORMAL 使用,必须非空
47 47
48 - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)  
49 - private String formConf;  
50 - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)  
51 - private List<String> formFields;  
52 @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") 48 @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
53 private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 49 private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
54 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") 50 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java
@@ -21,9 +21,6 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @@ -21,9 +21,6 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO {
21 @NotEmpty(message = "流程名称不能为空") 21 @NotEmpty(message = "流程名称不能为空")
22 private String name; 22 private String name;
23 23
24 - @Schema(description = "表单名字", example = "请假表单")  
25 - private String formName;  
26 -  
27 @Schema(description = "流程分类", example = "1") 24 @Schema(description = "流程分类", example = "1")
28 private String category; 25 private String category;
29 26
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
@@ -20,9 +20,6 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @@ -20,9 +20,6 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
20 @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") 20 @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
21 private String name; 21 private String name;
22 22
23 - @Schema(description = "表单名字", example = "请假表单")  
24 - private String formName;  
25 -  
26 @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") 23 @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao")
27 private String key; 24 private String key;
28 25
@@ -37,6 +34,13 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @@ -37,6 +34,13 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
37 @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") 34 @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
38 private String modelId; 35 private String modelId;
39 36
  37 + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private String formConf;
  39 + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + private List<String> formFields;
  41 + @Schema(description = "表单名字", example = "请假表单")
  42 + private String formName;
  43 +
40 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 44 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
41 private Integer suspensionState; // 参见 SuspensionState 枚举 45 private Integer suspensionState; // 参见 SuspensionState 枚举
42 46
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -907,9 +907,17 @@ public class BpmnModelUtils { @@ -907,9 +907,17 @@ public class BpmnModelUtils {
907 * @return 符合条件的路径 907 * @return 符合条件的路径
908 */ 908 */
909 private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(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 + // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值,
  911 + // TODO @小北:是不是还是保证,编辑的时候,如果计算下一个节点,还是 variables 是完整体?而不是空的!!!(可以微信讨论下)
  912 + SequenceFlow matchSequenceFlow;
  913 + if (CollUtil.isNotEmpty(variables)) {
  914 + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
911 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) 915 flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
912 && (evalConditionExpress(variables, flow.getConditionExpression()))); 916 && (evalConditionExpress(variables, flow.getConditionExpression())));
  917 + } else {
  918 + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
  919 + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()));
  920 + }
913 if (matchSequenceFlow == null) { 921 if (matchSequenceFlow == null) {
914 matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), 922 matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
915 flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); 923 flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
@@ -194,7 +194,7 @@ public class FlowableUtils { @@ -194,7 +194,7 @@ public class FlowableUtils {
194 @SuppressWarnings("unchecked") 194 @SuppressWarnings("unchecked")
195 public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) { 195 public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) {
196 if (processVariables == null) { 196 if (processVariables == null) {
197 - return null; 197 + return new HashMap<>();
198 } 198 }
199 return (Map<String, List<Long>>) processVariables.get( 199 return (Map<String, List<Long>>) processVariables.get(
200 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); 200 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
@@ -219,7 +219,7 @@ public class FlowableUtils { @@ -219,7 +219,7 @@ public class FlowableUtils {
219 @SuppressWarnings("unchecked") 219 @SuppressWarnings("unchecked")
220 public static Map<String, List<Long>> getApproveUserSelectAssignees(Map<String, Object> processVariables) { 220 public static Map<String, List<Long>> getApproveUserSelectAssignees(Map<String, Object> processVariables) {
221 if (processVariables == null) { 221 if (processVariables == null) {
222 - return null; 222 + return new HashMap<>();
223 } 223 }
224 return (Map<String, List<Long>>) processVariables.get( 224 return (Map<String, List<Long>>) processVariables.get(
225 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); 225 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -231,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -231,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService {
231 repositoryService.saveModel(model); 231 repositoryService.saveModel(model);
232 } 232 }
233 233
234 - private void validateBpmnXml(byte[] bpmnBytes, int type) { 234 + private void validateBpmnXml(byte[] bpmnBytes, Integer type) {
235 BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); 235 BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
236 if (bpmnModel == null) { 236 if (bpmnModel == null) {
237 throw exception(MODEL_NOT_EXISTS); 237 throw exception(MODEL_NOT_EXISTS);
@@ -248,6 +248,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -248,6 +248,7 @@ public class BpmModelServiceImpl implements BpmModelService {
248 throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); 248 throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());
249 } 249 }
250 }); 250 });
  251 + // TODO @小北:是不是可以 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);然后,最好判空。。。极端情况下,没 usertask ,哈哈哈哈。
251 // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” 252 // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”
252 Map<Integer, UserTask> userTaskMap = new HashMap<>(); 253 Map<Integer, UserTask> userTaskMap = new HashMap<>();
253 // BPMN 设计器,校验第一个用户任务节点 254 // BPMN 设计器,校验第一个用户任务节点
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -557,26 +557,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -557,26 +557,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
557 // 2.2 添加评论 557 // 2.2 添加评论
558 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), 558 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
559 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); 559 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
560 -  
561 - // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空  
562 - // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题  
563 - // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值)  
564 - // 历史中的变量值  
565 - // 3.1 设置流程变量  
566 - Map<String, Object> processVariables = new HashMap<>();  
567 - // 3.2 获取历史中流程变量  
568 - if (CollUtil.isNotEmpty(instance.getProcessVariables())) {  
569 - processVariables.putAll(instance.getProcessVariables());  
570 - }  
571 - // 3.3 合并前端传递的流程变量,以前端为准  
572 - if (CollUtil.isNotEmpty(reqVO.getVariables())) {  
573 - processVariables.putAll(reqVO.getVariables());  
574 - }  
575 - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑  
576 - Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, 560 + // 2.3 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑
  561 + Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(),
577 bpmnModel, reqVO.getNextAssignees(), instance); 562 bpmnModel, reqVO.getNextAssignees(), instance);
578 - // 3.4 调用 BPM complete 去完成任务  
579 runtimeService.setVariables(task.getProcessInstanceId(), variables); 563 runtimeService.setVariables(task.getProcessInstanceId(), variables);
  564 + // 2.4 调用 BPM complete 去完成任务
580 taskService.complete(task.getId(), variables, true); 565 taskService.complete(task.getId(), variables, true);
581 566
582 // 【加签专属】处理加签任务 567 // 【加签专属】处理加签任务
@@ -601,10 +586,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -601,10 +586,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
601 // 1. 获取下一个将要执行的节点集合 586 // 1. 获取下一个将要执行的节点集合
602 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); 587 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
603 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); 588 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
604 - // 2. 循环下一个将要执行的节点集合 589 +
  590 + // 2. 校验选择的下一个节点的审批人,是否合法
605 Map<String, List<Long>> processVariables; 591 Map<String, List<Long>> processVariables;
606 for (FlowNode nextFlowNode : nextFlowNodes) { 592 for (FlowNode nextFlowNode : nextFlowNodes) {
607 - // 获取任务节点中的审批人策略  
608 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); 593 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
609 // 2.1 情况一:如果节点中的审批人策略为 发起人自选 594 // 2.1 情况一:如果节点中的审批人策略为 发起人自选
610 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { 595 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
@@ -614,16 +599,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -614,16 +599,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
614 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 599 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
615 } 600 }
616 processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); 601 processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());
617 - if (processVariables == null){ 602 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖
  603 + // TODO @小北:【不用改】通过 if return,让逻辑更简洁一点;虽然会多判断一次 processVariables,但是 if else 层级更少。
  604 + if (processVariables != null
  605 + && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) {
  606 + continue;
  607 + }
  608 + // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES
  609 + if (processVariables == null) {
618 processVariables = new HashMap<>(); 610 processVariables = new HashMap<>();
619 - }else {  
620 - List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId());  
621 - // 特殊:如果当前节点已经存在审批人,则不允许覆盖  
622 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) {  
623 - continue;  
624 - }  
625 } 611 }
626 - // 校验通过的全部节点和审批人  
627 processVariables.put(nextFlowNode.getId(), assignees); 612 processVariables.put(nextFlowNode.getId(), assignees);
628 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); 613 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables);
629 } 614 }
@@ -635,16 +620,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -635,16 +620,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
635 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 620 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
636 } 621 }
637 processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); 622 processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
638 - if (processVariables == null){ 623 + if (processVariables == null) {
639 processVariables = new HashMap<>(); 624 processVariables = new HashMap<>();
640 - }else { 625 + } else {
641 List<Long> approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); 626 List<Long> approveUserSelectAssignee = processVariables.get(nextFlowNode.getId());
642 // 特殊:如果当前节点已经存在审批人,则不允许覆盖 627 // 特殊:如果当前节点已经存在审批人,则不允许覆盖
  628 + // TODO @小北:这种,应该可以覆盖呢。
643 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { 629 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) {
644 continue; 630 continue;
645 } 631 }
646 } 632 }
647 - // 校验通过的全部节点和审批人 633 + // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES
648 processVariables.put(nextFlowNode.getId(), assignees); 634 processVariables.put(nextFlowNode.getId(), assignees);
649 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); 635 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables);
650 } 636 }