Commit 149cab1dac170df649be1c0d08e446b58bb42a11
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
Showing
7 changed files
with
37 additions
and
45 deletions
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 | } |