From 0085b42518898b1d06aeb87a050976928fe97a6b Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 12 Feb 2025 13:53:15 +0800 Subject: [PATCH 001/116] =?UTF-8?q?feat:=20=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java | 1 + .../framework/flowable/core/util/SimpleModelUtils.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index 238a9347c3..c9da0718df 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -25,6 +25,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 APPROVE_NODE(11, "审批人", "userTask"), COPY_NODE(12, "抄送人", "serviceTask"), + TRANSACTOR_NODE(13, "办理人", "userTask"), DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 309c11cf83..d6673fc54a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -40,7 +40,7 @@ public class SimpleModelUtils { static { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), - new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); @@ -514,6 +514,13 @@ public class SimpleModelUtils { } + private static class TransactorNodeConvert extends ApproveNodeConvert { + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; + } + } + private static class CopyNodeConvert implements NodeConvert { @Override From e5aede6265bdbc77a6d9491c4e7660491c3ffcbe Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 12 Feb 2025 14:10:20 +0800 Subject: [PATCH 002/116] =?UTF-8?q?feat:=20=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E9=AB=98=E4=BA=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 3 ++- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d6673fc54a..2f18c475bf 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -792,10 +792,11 @@ public class SimpleModelUtils { BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); Assert.notNull(nodeType, "模型节点类型不支持"); - // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE + // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { // 添加元素 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ca50baef80..96aeaf24b8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -479,7 +479,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 1. 开始节点/审批节点 if (ObjectUtils.equalsAny(node.getType(), BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) { + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), + BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); activityNode.setCandidateUserIds(candidateUserIds); From f2894328909b62af4eb88f62f9bd227aea6078d2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 12 Feb 2025 18:46:40 +0800 Subject: [PATCH 003/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 2f18c475bf..e65c88a0f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -515,10 +515,12 @@ public class SimpleModelUtils { } private static class TransactorNodeConvert extends ApproveNodeConvert { + @Override public BpmSimpleModelNodeTypeEnum getType() { return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; } + } private static class CopyNodeConvert implements NodeConvert { From 7cf55c5300bdf026ad042525b14eded6fa04a3bc Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Thu, 13 Feb 2025 09:33:12 +0800 Subject: [PATCH 004/116] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8C=BA=E5=88=86=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/enums/BpmnModelConstants.java | 5 +++++ .../flowable/core/util/BpmnModelUtils.java | 20 +++++++++++++++++++ .../flowable/core/util/SimpleModelUtils.java | 2 ++ .../task/BpmProcessInstanceServiceImpl.java | 5 +++-- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java index 9dce8a2eae..7363eedba6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -129,4 +129,9 @@ public interface BpmnModelConstants { */ String REASON_REQUIRE = "reasonRequire"; + /** + * 节点类型 + */ + String NODE_TYPE = "nodeType"; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3f22c7c259..d85db6230e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -410,6 +410,26 @@ public class BpmnModelUtils { return parseExtensionElement(flowElement, TRIGGER_PARAM); } + /** + * 给节点添加节点类型 + * + * @param nodeType 节点类型 + * @param flowElement 节点 + */ + public static void addNodeType(Integer nodeType, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); + } + + /** + * 解析节点类型 + * + * @param flowElement 节点 + * @return 节点类型 + */ + public static Integer parseNodeType(FlowElement flowElement) { + return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); + } + // ========== BPM 简单查找相关的方法 ========== /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 2f18c475bf..319410f1ef 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -445,6 +445,8 @@ public class SimpleModelUtils { addSignEnable(node.getSignEnable(), userTask); // 审批意见 addReasonRequire(node.getReasonRequire(), userTask); + // 节点类型 + addNodeType(node.getType(), userTask); return userTask; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 96aeaf24b8..7b9b9e1687 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -67,6 +67,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.flowable.bpmn.constants.BpmnXMLConstants.*; @@ -325,7 +326,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + : parseNodeType(flowNode)) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -402,7 +403,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setNodeType(parseNodeType(flowNode)) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) From 94499c0e5f13b1a497f8f6ec73a7fd3c812f9d76 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Thu, 13 Feb 2025 19:49:49 +0800 Subject: [PATCH 005/116] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=BE=E6=A0=87=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 7bdc963c8a..dfbc74978b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -27,9 +27,7 @@ import java.util.List; @Data public class BpmModelMetaInfoVO { - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") - @NotEmpty(message = "流程图标不能为空") - @URL(message = "流程图标格式不正确") + @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") private String icon; @Schema(description = "流程描述", example = "我是描述") From 8826f92d9dea4c35df3acc8ee5dd92fdc8ff1bda Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Feb 2025 19:59:01 +0800 Subject: [PATCH 006/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E7=A7=BB=E9=99=A4=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=BE=E6=A0=87=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=8C=E8=BF=98=E6=98=AF=E9=9C=80=E8=A6=81=20URL=20=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index dfbc74978b..e00b6d4d3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; -import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; @@ -28,6 +27,7 @@ import java.util.List; public class BpmModelMetaInfoVO { @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") + @URL(message = "流程图标格式不正确") private String icon; @Schema(description = "流程描述", example = "我是描述") From ef296822304ec8785e46243e7d3992e6498a6323 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Feb 2025 08:22:04 +0800 Subject: [PATCH 007/116] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8C=BA=E5=88=86=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/controller/admin/task/vo/task/BpmTaskRespVO.java | 3 +++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 6 ++++-- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 83812ee1ab..d36b61a2c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,6 +85,9 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; + @Schema(description = "节点类型", example = "10") + private Integer nodeType; + @Data @Schema(description = "流程实例") public static class ProcessInstance { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 7b9b9e1687..d12fe23d04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -326,7 +326,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : parseNodeType(flowNode)) + : ObjectUtil.isNull(parseNodeType(flowNode)) ? + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -403,7 +404,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(parseNodeType(flowNode)) + .setNodeType(ObjectUtil.isNull(parseNodeType(flowNode)) ? + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 76c7771035..73ae53b25e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -162,6 +162,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { bpmnModel, todoTask.getTaskDefinitionKey()); Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); + Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); // 4. 任务表单 BpmFormDO taskForm = null; @@ -171,7 +172,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) .setSignEnable(signEnable) - .setReasonRequire(reasonRequire); + .setReasonRequire(reasonRequire) + .setNodeType(nodeType); } @Override From 00eea85a69e7223aa190d0737b355fbd0571ad9c Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Feb 2025 08:31:15 +0800 Subject: [PATCH 008/116] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index d36b61a2c7..5ba3c3c85a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,7 +85,7 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; - @Schema(description = "节点类型", example = "10") + @Schema(description = "节点类型", example = "10") // 只有Simple设计器的场景下才会使用此字段 private Integer nodeType; @Data From 045a224c2255a4add4c991b8ba2bf95533399bec Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Fri, 14 Feb 2025 15:51:57 +0800 Subject: [PATCH 009/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=81=9C?= =?UTF-8?q?=E7=94=A8=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/definition/BpmModelServiceImpl.java | 4 ++-- .../definition/BpmProcessDefinitionService.java | 3 ++- .../definition/BpmProcessDefinitionServiceImpl.java | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 9ccc2f2c92..db3beeff5b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -304,7 +304,7 @@ public class BpmModelServiceImpl implements BpmModelService { } // 2. 更新状态 - processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + processDefinitionService.updateProcessDefinitionState(definition.getId(), state,definition.isSuspended()); } @Override @@ -403,7 +403,7 @@ public class BpmModelServiceImpl implements BpmModelService { return; } processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), - SuspensionState.SUSPENDED.getStateCode()); + SuspensionState.SUSPENDED.getStateCode(),oldDefinition.isSuspended()); } private Model getModelByKey(String key) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java index f42e69e73b..9f388621f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -60,8 +60,9 @@ public interface BpmProcessDefinitionService { * * @param id 流程定义的编号 * @param state 状态 + * @param isSuspended 是否处于激活或挂起状态 */ - void updateProcessDefinitionState(String id, Integer state); + void updateProcessDefinitionState(String id, Integer state, boolean isSuspended); /** * 更新模型编号 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index c6a178c6c8..cc9640f2f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -155,17 +155,21 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ } @Override - public void updateProcessDefinitionState(String id, Integer state) { + public void updateProcessDefinitionState(String id, Integer state,boolean isSuspended) { // 激活 if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { - repositoryService.activateProcessDefinitionById(id, false, null); + if (isSuspended) { + repositoryService.activateProcessDefinitionById(id, false, null); + } return; } // 挂起 if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { // suspendProcessInstances = false,进行中的任务,不进行挂起。 // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 - repositoryService.suspendProcessDefinitionById(id, false, null); + if (!isSuspended) { + repositoryService.suspendProcessDefinitionById(id, false, null); + } return; } log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); From e9a3773b7275ff60e03860f1dadd934629555620 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 08:48:51 +0800 Subject: [PATCH 010/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/controller/admin/task/vo/task/BpmTaskRespVO.java | 4 ++-- .../flowable/core/enums/BpmnModelConstants.java | 4 ++++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 9 +++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 4 +--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 5ba3c3c85a..fce72a490a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,8 +85,8 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; - @Schema(description = "节点类型", example = "10") // 只有Simple设计器的场景下才会使用此字段 - private Integer nodeType; + @Schema(description = "节点类型", example = "10") + private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 @Data @Schema(description = "流程实例") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java index 7363eedba6..cb857b7390 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; + /** * BPMN XML 常量信息 * @@ -131,6 +133,8 @@ public interface BpmnModelConstants { /** * 节点类型 + * + * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 */ String NODE_TYPE = "nodeType"; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index d12fe23d04..dc6788a9e2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -326,8 +327,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjectUtil.isNull(parseNodeType(flowNode)) ? - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -404,8 +405,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(ObjectUtil.isNull(parseNodeType(flowNode)) ? - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 73ae53b25e..b092e52f61 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -171,9 +171,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) - .setSignEnable(signEnable) - .setReasonRequire(reasonRequire) - .setNodeType(nodeType); + .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } @Override From 56ea9d2381e6aa837ce54bc841cc018a14b562cc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 09:21:44 +0800 Subject: [PATCH 011/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91BPM=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=81=9C?= =?UTF-8?q?=E7=94=A8=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/definition/BpmModelServiceImpl.java | 4 ++-- .../definition/BpmProcessDefinitionService.java | 3 +-- .../BpmProcessDefinitionServiceImpl.java | 14 +++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index db3beeff5b..9ccc2f2c92 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -304,7 +304,7 @@ public class BpmModelServiceImpl implements BpmModelService { } // 2. 更新状态 - processDefinitionService.updateProcessDefinitionState(definition.getId(), state,definition.isSuspended()); + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); } @Override @@ -403,7 +403,7 @@ public class BpmModelServiceImpl implements BpmModelService { return; } processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), - SuspensionState.SUSPENDED.getStateCode(),oldDefinition.isSuspended()); + SuspensionState.SUSPENDED.getStateCode()); } private Model getModelByKey(String key) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java index 9f388621f3..f42e69e73b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -60,9 +60,8 @@ public interface BpmProcessDefinitionService { * * @param id 流程定义的编号 * @param state 状态 - * @param isSuspended 是否处于激活或挂起状态 */ - void updateProcessDefinitionState(String id, Integer state, boolean isSuspended); + void updateProcessDefinitionState(String id, Integer state); /** * 更新模型编号 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index cc9640f2f4..96280fa088 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -28,8 +28,7 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static java.util.Collections.emptyList; /** @@ -155,10 +154,15 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ } @Override - public void updateProcessDefinitionState(String id, Integer state,boolean isSuspended) { + public void updateProcessDefinitionState(String id, Integer state) { + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); + if (processDefinition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + // 激活 if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { - if (isSuspended) { + if (processDefinition.isSuspended()) { repositoryService.activateProcessDefinitionById(id, false, null); } return; @@ -167,7 +171,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { // suspendProcessInstances = false,进行中的任务,不进行挂起。 // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 - if (!isSuspended) { + if (!processDefinition.isSuspended()) { repositoryService.suspendProcessDefinitionById(id, false, null); } return; From a4e090383b0d4339bbd3ca6f61a4317d4c2ff307 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 09:40:02 +0800 Subject: [PATCH 012/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E5=B7=B2=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=9A=84=E5=88=97=E8=A1=A8=EF=BC=8C=E5=BC=BA=E5=88=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E8=87=AA=E5=8A=A8=E5=AE=8C=E6=88=90=E7=9A=84=E2=80=9C?= =?UTF-8?q?=E5=8F=91=E8=B5=B7=E4=BA=BA=E2=80=9D=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index b092e52f61..f57f088ef5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -61,6 +61,7 @@ import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; @@ -194,6 +195,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { return PageResult.empty(); } List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + // 特殊:强制移除自动完成的“发起人”节点 + // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 + tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); return new PageResult<>(tasks, count); } From 9a4bb60a783d5c4f4f4bcaa2e09961ed525aec83 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Sun, 16 Feb 2025 08:47:13 +0800 Subject: [PATCH 013/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E4=BF=AE=E6=94=B9=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=99=A8=E5=A2=9E=E5=8A=A0=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/definition/BpmTriggerTypeEnum.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 18 ++++-- .../flowable/core/util/SimpleModelUtils.java | 4 +- .../task/trigger/BpmFormUpdateTrigger.java | 63 +++++++++++++++++++ .../trigger/BpmUpdateNormalFormTrigger.java | 44 ------------- 5 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java delete mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 9a2f073726..42d8bafabb 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,7 +17,7 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jason:FORM_UPDATE + FORM_UPDATE(2, "更新流程表单"); /** * 触发器执行动作类型 @@ -39,5 +39,4 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { public static BpmTriggerTypeEnum typeOf(Integer type) { return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 291cf4d830..50c45a96a2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -345,12 +345,10 @@ public class BpmSimpleModelNodeVO { @Valid private HttpRequestTriggerSetting httpRequestSetting; - // TODO @jason:这个要不直接叫 formSetting,更好理解一点哈 - // TODO @jason:如果搞成 List,是不是可以做条件组了?微信讨论哈 /** * 流程表单触发器设置 */ - private NormalFormTriggerSetting normalFormSetting; + private List formSettings; @Schema(description = "http 请求触发器设置", example = "{}") @Data @@ -383,12 +381,20 @@ public class BpmSimpleModelNodeVO { @Schema(description = "流程表单触发器设置", example = "{}") @Data - public static class NormalFormTriggerSetting { + public static class FormTriggerSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; @Schema(description = "修改的表单字段", example = "userName") private Map updateFormFields; - } - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 309c11cf83..55591468e3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -724,8 +724,8 @@ public class SimpleModelUtils { if (node.getTriggerSetting().getHttpRequestSetting() != null) { addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); } - if (node.getTriggerSetting().getNormalFormSetting() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getNormalFormSetting()); + if (node.getTriggerSetting().getFormSettings() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); } } return serviceTask; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java new file mode 100644 index 0000000000..6a007c0dfe --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * BPM 更新流程表单触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormUpdateTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_UPDATE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析更新流程表单配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); + return; + } + + // 2.获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.更新流程变量 + for (FormTriggerSetting setting : settings) { + if (CollUtil.isEmpty(setting.getUpdateFormFields())) { + continue; + } + boolean isFormUpdateNeeded = true; + if (setting.getConditionType() != null) { // 配置了条件,判断条件是否满足 + String conditionExpression = SimpleModelUtils.buildConditionExpression(setting.getConditionType(), setting.getConditionExpression(), + setting.getConditionGroups()); + isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFormUpdateNeeded) { + processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); + } + } + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java deleted file mode 100644 index deab1f5e36..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -// TODO @jason:改成 BpmFormUpdateTrigger -/** - * BPM 更新流程表单触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmUpdateNormalFormTrigger implements BpmTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析更新流程表单配置 - NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class); - if (setting == null) { - log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); - return; - } - // 2.更新流程变量 - if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) { - processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); - } - } - -} From 3d20ce1a9bdc56bdec2b55a98bb1c15ff76735d7 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 09:52:09 +0800 Subject: [PATCH 014/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E6=94=AF=E8=8A=82=E7=82=B9=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3f22c7c259..3705d697c9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -780,7 +780,8 @@ public class BpmnModelUtils { Gateway gateway = (Gateway) currentElement; SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); + //流程第一次发起时,variables条件值一定为空,发生异常会导致后续分支节点无法预测, + || (null != variables && evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); From 1023afda4051db10d7a3198a067a0b55461b7b1b Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 10:28:11 +0800 Subject: [PATCH 015/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E6=94=AF=E8=8A=82=E7=82=B9=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3705d697c9..12fa1bf6d9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -32,7 +32,7 @@ import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_P /** * BPMN Model 操作工具类。目前分成三部分: - * + *

* 1. BPMN 修改 + 解析元素相关的方法 * 2. BPMN 简单查找相关的方法 * 3. BPMN 复杂遍历相关的方法 @@ -105,8 +105,8 @@ public class BpmnModelUtils { * 给节点添加候选人元素 * * @param candidateStrategy 候选人策略 - * @param candidateParam 候选人参数,允许空 - * @param flowElement 节点 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 */ public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, @@ -150,9 +150,9 @@ public class BpmnModelUtils { /** * 解析审批类型 * - * @see BpmUserTaskApproveTypeEnum * @param userTask 任务节点 * @return 审批类型 + * @see BpmUserTaskApproveTypeEnum */ public static Integer parseApproveType(FlowElement userTask) { return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); @@ -162,7 +162,7 @@ public class BpmnModelUtils { * 添加任务拒绝处理元素 * * @param rejectHandler 任务拒绝处理 - * @param userTask 任务节点 + * @param userTask 任务节点 */ public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { if (rejectHandler == null) { @@ -196,9 +196,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 * - * @see BpmUserTaskAssignStartUserHandlerTypeEnum * @param assignStartUserHandlerType 发起人处理类型 - * @param userTask 任务节点 + * @param userTask 任务节点 + * @see BpmUserTaskAssignStartUserHandlerTypeEnum */ public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { if (assignStartUserHandlerType == null) { @@ -210,9 +210,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人为空时,处理类型枚举 * - * @see BpmUserTaskAssignEmptyHandlerTypeEnum * @param emptyHandler 空处理 - * @param userTask 任务节点 + * @param userTask 任务节点 + * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { if (emptyHandler == null) { @@ -256,7 +256,7 @@ public class BpmnModelUtils { * 给节点添加表单字段权限元素 * * @param fieldsPermissions 表单字段权限 - * @param flowElement 节点 + * @param flowElement 节点 */ public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { if (CollUtil.isNotEmpty(fieldsPermissions)) { @@ -267,7 +267,7 @@ public class BpmnModelUtils { /** * 解析表单字段权限 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 表单字段权限 */ @@ -313,7 +313,7 @@ public class BpmnModelUtils { /** * 解析操作按钮设置 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 操作按钮设置 */ @@ -762,9 +762,9 @@ public class BpmnModelUtils { // 情况:StartEvent/EndEvent/UserTask/ServiceTask if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { // 添加元素 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); @@ -778,10 +778,16 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - //流程第一次发起时,variables条件值一定为空,发生异常会导致后续分支节点无法预测, - || (null != variables && evalConditionExpress(variables, flow.getConditionExpression()))); + SequenceFlow matchSequenceFlow; + //流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == variables) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -831,7 +837,7 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param express 条件表达式 * @return 是否满足条件 */ public static boolean evalConditionExpress(Map variables, String express) { From 8651f2f649df6aa6235c1702722e16e29fc6be80 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:03:14 +0800 Subject: [PATCH 016/116] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0nodeIds?= =?UTF-8?q?=E5=8F=82=E6=95=B0=EF=BC=8C=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84?= =?UTF-8?q?=E6=B5=8B=E8=BD=A8=E8=BF=B9=E4=B8=8B=E7=9A=84=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java index b13ff561fb..f49f7255a3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -21,4 +21,7 @@ public class BpmProcessInstanceCreateReqVO { @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") private Map> startUserSelectAssignees; + @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List nodeIds; + } From 296e6ab3adbc5954cd5bcaf68d04dedf00a2e2bb Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:04:52 +0800 Subject: [PATCH 017/116] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E8=8E=B7?= =?UTF-8?q?=E5=BE=97=E5=AE=A1=E6=89=B9=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=96=B9=E5=BC=8F=E5=92=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=BD=BF=E7=94=A8get=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/BpmProcessInstanceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 3a847ce4e8..e3db031e15 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -158,11 +158,11 @@ public class BpmProcessInstanceController { return success(true); } - @GetMapping("/get-approval-detail") + @PostMapping("/get-approval-detail") @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + public CommonResult getApprovalDetail(@Valid @RequestBody BpmApprovalDetailReqVO reqVO) { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } From 5e277e020f946e7c7886d70196797356f023e052 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:11:00 +0800 Subject: [PATCH 018/116] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E5=AE=A1=E6=89=B9=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/dto/BpmProcessInstanceCreateReqDTO.java | 5 +++++ .../service/task/BpmProcessInstanceServiceImpl.java | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java index b1ac35366f..344e2712db 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -41,4 +41,9 @@ public class BpmProcessInstanceCreateReqDTO { */ private Map> startUserSelectAssignees; + /** + * 活动节点列表 createProcessInstance弃用时可移除 + */ + private List nodeIds; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 96aeaf24b8..51da18e67c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -608,7 +608,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, - createReqVO.getStartUserSelectAssignees()); + createReqVO.getStartUserSelectAssignees(), createReqVO.getNodeIds()); } @Override @@ -620,13 +620,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), - createReqDTO.getStartUserSelectAssignees()); + createReqDTO.getStartUserSelectAssignees(),createReqDTO.getNodeIds()); }); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey, - Map> startUserSelectAssignees) { + Map> startUserSelectAssignees, List nodeIds) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -644,7 +644,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees); + validateStartUserSelectAssignees(definition, startUserSelectAssignees, nodeIds); // 2. 创建流程实例 if (variables == null) { @@ -690,16 +690,17 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees) { + Map> startUserSelectAssignees, List nodeIds) { // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); if (CollUtil.isEmpty(tasks)) { return; } + // 2.流程发起时要先获取当前流程的预测走向节点,发起是 // 2. 校验发起人自选审批人的审批人和抄送人是否都配置了 - tasks.forEach(task -> { + tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From c68ab33dfd8b665ca1dca9d460d36b692feadd55 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 17 Feb 2025 22:13:42 +0800 Subject: [PATCH 019/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20fix:=20=E8=A1=A5=E5=85=85=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=A7=84=E5=88=99=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 22 +++++++++---------- .../task/BpmProcessInstanceServiceImpl.java | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 12fa1bf6d9..e09a0f94fa 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -32,7 +32,7 @@ import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_P /** * BPMN Model 操作工具类。目前分成三部分: - *

+ * * 1. BPMN 修改 + 解析元素相关的方法 * 2. BPMN 简单查找相关的方法 * 3. BPMN 复杂遍历相关的方法 @@ -105,8 +105,8 @@ public class BpmnModelUtils { * 给节点添加候选人元素 * * @param candidateStrategy 候选人策略 - * @param candidateParam 候选人参数,允许空 - * @param flowElement 节点 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 */ public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, @@ -150,9 +150,9 @@ public class BpmnModelUtils { /** * 解析审批类型 * + * @see BpmUserTaskApproveTypeEnum * @param userTask 任务节点 * @return 审批类型 - * @see BpmUserTaskApproveTypeEnum */ public static Integer parseApproveType(FlowElement userTask) { return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); @@ -162,7 +162,7 @@ public class BpmnModelUtils { * 添加任务拒绝处理元素 * * @param rejectHandler 任务拒绝处理 - * @param userTask 任务节点 + * @param userTask 任务节点 */ public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { if (rejectHandler == null) { @@ -196,9 +196,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 * - * @param assignStartUserHandlerType 发起人处理类型 - * @param userTask 任务节点 * @see BpmUserTaskAssignStartUserHandlerTypeEnum + * @param assignStartUserHandlerType 发起人处理类型 + * @param userTask 任务节点 */ public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { if (assignStartUserHandlerType == null) { @@ -211,7 +211,7 @@ public class BpmnModelUtils { * 给节点添加用户任务的审批人为空时,处理类型枚举 * * @param emptyHandler 空处理 - * @param userTask 任务节点 + * @param userTask 任务节点 * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { @@ -267,7 +267,7 @@ public class BpmnModelUtils { /** * 解析表单字段权限 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 表单字段权限 */ @@ -313,7 +313,7 @@ public class BpmnModelUtils { /** * 解析操作按钮设置 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 操作按钮设置 */ @@ -837,7 +837,7 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param express 条件表达式 * @return 是否满足条件 */ public static boolean evalConditionExpress(Map variables, String express) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 51da18e67c..716b7e4cc5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -697,9 +697,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (CollUtil.isEmpty(tasks)) { return; } - // 2.流程发起时要先获取当前流程的预测走向节点,发起是 - // 2. 校验发起人自选审批人的审批人和抄送人是否都配置了 + // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From 3265df7548769975a20eb600b6f0a0a216ae3b7f Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 17 Feb 2025 22:41:15 +0800 Subject: [PATCH 020/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20fix:=20=E8=A1=A5=E5=85=85=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=A7=84=E5=88=99=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/flowable/core/util/BpmnModelUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index e09a0f94fa..fe81c7849c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -210,9 +210,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人为空时,处理类型枚举 * + * @see BpmUserTaskAssignEmptyHandlerTypeEnum * @param emptyHandler 空处理 * @param userTask 任务节点 - * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { if (emptyHandler == null) { @@ -256,7 +256,7 @@ public class BpmnModelUtils { * 给节点添加表单字段权限元素 * * @param fieldsPermissions 表单字段权限 - * @param flowElement 节点 + * @param flowElement 节点 */ public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { if (CollUtil.isNotEmpty(fieldsPermissions)) { @@ -762,9 +762,9 @@ public class BpmnModelUtils { // 情况:StartEvent/EndEvent/UserTask/ServiceTask if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { // 添加元素 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); From 1f6f00164ac55116fe256543954960260887f532 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 10:00:36 +0800 Subject: [PATCH 021/116] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/BpmProcessInstanceController.java | 4 ++-- .../admin/task/vo/instance/BpmApprovalDetailReqVO.java | 3 +++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index e3db031e15..3a847ce4e8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -158,11 +158,11 @@ public class BpmProcessInstanceController { return success(true); } - @PostMapping("/get-approval-detail") + @GetMapping("/get-approval-detail") @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult getApprovalDetail(@Valid @RequestBody BpmApprovalDetailReqVO reqVO) { + public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index 9121f10362..d9ac673514 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -18,6 +18,9 @@ public class BpmApprovalDetailReqVO { @Schema(description = "流程变量") private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 + @Schema(description = "流程变量") + private String processVariablesStr; // 使用场景:同 processDefinitionId,用于流程预测 + @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 716b7e4cc5..6ae08e1060 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -165,6 +166,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 Map processVariables = reqVO.getProcessVariables(); // 流程变量 + if (reqVO.getProcessVariablesStr() != null){ + processVariables = JSONUtil.parseObj(reqVO.getProcessVariablesStr()); + } // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); From 23ed5b780f88ecc171ca7388e75330cf0488d8e3 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 14:13:49 +0800 Subject: [PATCH 022/116] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=8F=82=E6=95=B0=EF=BC=8C=E9=A2=84=E6=B5=8B=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/task/dto/BpmProcessInstanceCreateReqDTO.java | 5 ----- .../vo/instance/BpmProcessInstanceCreateReqVO.java | 3 --- .../service/task/BpmProcessInstanceServiceImpl.java | 12 ++++++------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java index 344e2712db..b1ac35366f 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -41,9 +41,4 @@ public class BpmProcessInstanceCreateReqDTO { */ private Map> startUserSelectAssignees; - /** - * 活动节点列表 createProcessInstance弃用时可移除 - */ - private List nodeIds; - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java index f49f7255a3..b13ff561fb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -21,7 +21,4 @@ public class BpmProcessInstanceCreateReqVO { @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") private Map> startUserSelectAssignees; - @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List nodeIds; - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 6ae08e1060..b17aaa4101 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -612,7 +612,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, - createReqVO.getStartUserSelectAssignees(), createReqVO.getNodeIds()); + createReqVO.getStartUserSelectAssignees()); } @Override @@ -624,13 +624,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), - createReqDTO.getStartUserSelectAssignees(),createReqDTO.getNodeIds()); + createReqDTO.getStartUserSelectAssignees()); }); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey, - Map> startUserSelectAssignees, List nodeIds) { + Map> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -648,7 +648,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees, nodeIds); + validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { @@ -694,7 +694,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees, List nodeIds) { + Map> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); @@ -703,7 +703,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 - tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { + tasks.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From 9a83515c0547daf922c75459db0f07ce25e4bfb8 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 16:06:51 +0800 Subject: [PATCH 023/116] =?UTF-8?q?fix:=20=E6=B5=81=E7=A8=8B=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE=E5=9C=A8=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index b17aaa4101..66e615794e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; @@ -648,7 +649,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees); + validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables); // 2. 创建流程实例 if (variables == null) { @@ -693,17 +694,21 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return instance.getId(); } - private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees) { - // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); - List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); - if (CollUtil.isEmpty(tasks)) { + private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, + Map> startUserSelectAssignees, Map variables) { + // 1.获取预测的节点信息 + BpmApprovalDetailReqVO detailReqVO = new BpmApprovalDetailReqVO(); + detailReqVO.setProcessVariables(variables); + detailReqVO.setProcessDefinitionId(definition.getId()); + BpmApprovalDetailRespVO respVO = getApprovalDetail(userId, detailReqVO); + List activityNodes = respVO.getActivityNodes(); + if (CollUtil.isEmpty(activityNodes)){ return; } - + //移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> null == task.getCandidateStrategy() || !task.getCandidateStrategy().equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 - tasks.forEach(task -> { + activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From 3017933710ad1f930267ae26c1e62786678ab77c Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 19 Feb 2025 10:04:05 +0800 Subject: [PATCH 024/116] =?UTF-8?q?fix:=20Simple=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=8A=84=E9=80=81=E8=8A=82=E7=82=B9=E5=9C=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E9=A2=84=E6=B5=8B=E6=97=B6=E6=9C=AA=E6=98=BE=E7=A4=BA=E6=8A=84?= =?UTF-8?q?=E9=80=81=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index dc6788a9e2..d786ba2f8f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -499,6 +499,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3. 抄送节点 if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人 BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); return activityNode; } return null; From 009f1889a93e7c9e30798b30396e87e853cf0a13 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 12:55:17 +0800 Subject: [PATCH 025/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E8=A7=A6=E5=8F=91=E5=99=A8?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9=E8=8A=82=E7=82=B9=E7=9A=84=E8=AF=84?= =?UTF-8?q?=E5=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/trigger/BpmFormUpdateTrigger.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java index 6a007c0dfe..0382397d08 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -49,12 +49,14 @@ public class BpmFormUpdateTrigger implements BpmTrigger { if (CollUtil.isEmpty(setting.getUpdateFormFields())) { continue; } + // 配置了条件,判断条件是否满足 boolean isFormUpdateNeeded = true; - if (setting.getConditionType() != null) { // 配置了条件,判断条件是否满足 - String conditionExpression = SimpleModelUtils.buildConditionExpression(setting.getConditionType(), setting.getConditionExpression(), - setting.getConditionGroups()); + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); } + // 更新流程表单 if (isFormUpdateNeeded) { processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); } From cb3467ada2bd8d07a17699e950147b9333896c39 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 14:11:42 +0800 Subject: [PATCH 026/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 6 +++++ .../vo/instance/BpmApprovalDetailReqVO.java | 2 +- .../flowable/core/util/BpmnModelUtils.java | 13 +++------ .../task/BpmProcessInstanceServiceImpl.java | 27 +++++++++---------- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 3a847ce4e8..8af426e66d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; @@ -163,6 +166,9 @@ public class BpmProcessInstanceController { @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())){ + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(),Map.class)); + } return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index d9ac673514..d93e2fd69b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -19,7 +19,7 @@ public class BpmApprovalDetailReqVO { private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 @Schema(description = "流程变量") - private String processVariablesStr; // 使用场景:同 processDefinitionId,用于流程预测 + private String processVariablesStr; //解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index fe81c7849c..5909c66c9e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,16 +778,9 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow; - //流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - } + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 66e615794e..91d2405b19 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -29,7 +28,6 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; @@ -167,9 +165,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 Map processVariables = reqVO.getProcessVariables(); // 流程变量 - if (reqVO.getProcessVariablesStr() != null){ - processVariables = JSONUtil.parseObj(reqVO.getProcessVariablesStr()); - } // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); @@ -213,8 +208,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 // B,会不会表单权限不一致哈。 BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); - - // 3.2 预测未运行节点的审批信息 + // 3.2 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == processVariables) { + processVariables = new HashMap<>(); + } + // 3.3 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); @@ -697,17 +695,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, Map> startUserSelectAssignees, Map variables) { // 1.获取预测的节点信息 - BpmApprovalDetailReqVO detailReqVO = new BpmApprovalDetailReqVO(); - detailReqVO.setProcessVariables(variables); - detailReqVO.setProcessDefinitionId(definition.getId()); - BpmApprovalDetailRespVO respVO = getApprovalDetail(userId, detailReqVO); - List activityNodes = respVO.getActivityNodes(); + BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() + .setProcessDefinitionId(definition.getId()) + .setProcessVariables(variables)); + List activityNodes = detailRespVO.getActivityNodes(); if (CollUtil.isEmpty(activityNodes)){ return; } - //移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> null == task.getCandidateStrategy() || !task.getCandidateStrategy().equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); - // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + // 2.移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task. getCandidateStrategy())); + // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From 4b89f6893634f1015c40d9de961412824d3c6efd Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 14:13:08 +0800 Subject: [PATCH 027/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/vo/instance/BpmApprovalDetailReqVO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index d93e2fd69b..069ec2000e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -19,7 +19,7 @@ public class BpmApprovalDetailReqVO { private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 @Schema(description = "流程变量") - private String processVariablesStr; //解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 + private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID From 29a902f37ee7eede247527555c2d33b37a571763 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 15:00:28 +0800 Subject: [PATCH 028/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 25 ++++++++++++++----- .../flowable/core/util/SimpleModelUtils.java | 24 +++++++++++++----- .../task/BpmProcessInstanceServiceImpl.java | 6 +---- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 5909c66c9e..edbfbb820d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,9 +778,16 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + SequenceFlow matchSequenceFlow; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == variables) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -800,9 +807,15 @@ public class BpmnModelUtils { if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); + Collection matchSequenceFlows; + if (null == variables){ + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + }else { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + } if (CollUtil.isEmpty(matchSequenceFlows)) { matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e65c88a0f3..910808ef3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -808,9 +808,15 @@ public class SimpleModelUtils { // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); + BpmSimpleModelNodeVO matchConditionNode; + if(null == variables) { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + }else { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + } if (matchConditionNode == null) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -823,9 +829,15 @@ public class SimpleModelUtils { // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); + Collection matchConditionNodes; + if (null == variables) { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + }else { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + } if (CollUtil.isEmpty(matchConditionNodes)) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 91d2405b19..ff8bb0c4c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -208,11 +208,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 // B,会不会表单权限不一致哈。 BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); - // 3.2 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == processVariables) { - processVariables = new HashMap<>(); - } - // 3.3 预测未运行节点的审批信息 + // 3.2 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); From 642e72ae7abda7264e430cba9593c9e06aee3c28 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 15:02:04 +0800 Subject: [PATCH 029/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/framework/flowable/core/util/BpmnModelUtils.java | 1 + .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index edbfbb820d..d150aad56b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,6 +808,7 @@ public class BpmnModelUtils { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; Collection matchSequenceFlows; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if (null == variables){ matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 910808ef3b..1422d919c0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -809,6 +809,7 @@ public class SimpleModelUtils { if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 BpmSimpleModelNodeVO matchConditionNode; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if(null == variables) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -830,6 +831,7 @@ public class SimpleModelUtils { if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 Collection matchConditionNodes; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if (null == variables) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); From 248127a941cf511d14c3831d079c64abb805fe44 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 19 Feb 2025 20:01:38 +0800 Subject: [PATCH 030/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 21 +++++-------------- .../flowable/core/util/SimpleModelUtils.java | 18 ++-------------- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index d150aad56b..49ab5bb35b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,16 +778,9 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -807,16 +800,9 @@ public class BpmnModelUtils { if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables){ - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - }else { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); - } if (CollUtil.isEmpty(matchSequenceFlows)) { matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -851,6 +837,9 @@ public class BpmnModelUtils { if (express == null) { return Boolean.FALSE; } + if (variables == null) { + return Boolean.FALSE; + } try { Object result = FlowableUtils.getExpressionValue(variables, express); return Boolean.TRUE.equals(result); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 1422d919c0..98e0c49b69 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -808,16 +808,9 @@ public class SimpleModelUtils { // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - BpmSimpleModelNodeVO matchConditionNode; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if(null == variables) { - matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - }else { - matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) && evalConditionExpress(variables, conditionNode.getConditionSetting())); - } if (matchConditionNode == null) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -830,16 +823,9 @@ public class SimpleModelUtils { // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - Collection matchConditionNodes; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - }else { - matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) && evalConditionExpress(variables, conditionNode.getConditionSetting())); - } if (CollUtil.isEmpty(matchConditionNodes)) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); From eaa15b24ff1b521af2103d7041c86242e0af42e7 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 19 Feb 2025 21:05:14 +0800 Subject: [PATCH 031/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ff8bb0c4c7..e284e42706 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -699,7 +699,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } // 2.移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task. getCandidateStrategy())); + activityNodes.removeIf(task -> !Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From 24f1ce16c714dbebc8bd9958f7d05889bfd0226a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:20:12 +0800 Subject: [PATCH 032/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=8F=91=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/BpmProcessInstanceController.java | 6 +++--- .../flowable/core/util/BpmnModelUtils.java | 9 ++++++--- .../service/task/BpmProcessInstanceServiceImpl.java | 13 ++++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 8af426e66d..67e7219f1d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; @@ -165,9 +164,10 @@ public class BpmProcessInstanceController { @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())){ - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(),Map.class)); + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); } return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 65d38dcbe7..223aa1a7c8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -799,8 +799,8 @@ public class BpmnModelUtils { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -857,9 +857,12 @@ public class BpmnModelUtils { if (express == null) { return Boolean.FALSE; } + // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables if (variables == null) { - return Boolean.FALSE; + variables = new HashMap<>(); } + + // 执行计算 try { Object result = FlowableUtils.getExpressionValue(variables, express); return Boolean.TRUE.equals(result); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fbe5f97f51..632ed47073 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -696,8 +696,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, - Map> startUserSelectAssignees, Map variables) { - // 1.获取预测的节点信息 + Map> startUserSelectAssignees, + Map variables) { + // 1. 获取预测的节点信息 BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() .setProcessDefinitionId(definition.getId()) .setProcessVariables(variables)); @@ -705,9 +706,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (CollUtil.isEmpty(activityNodes)){ return; } - // 2.移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> !Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); - // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + + // 2.1 移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From ceba5b8cecad017bd75ec8ac03ac8154dde32c5a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:35:39 +0800 Subject: [PATCH 033/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=8F=91=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/flowable/core/util/BpmnModelUtils.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 223aa1a7c8..27d923bdeb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -850,11 +850,11 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param expression 条件表达式 * @return 是否满足条件 */ - public static boolean evalConditionExpress(Map variables, String express) { - if (express == null) { + public static boolean evalConditionExpress(Map variables, String expression) { + if (expression == null) { return Boolean.FALSE; } // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables @@ -864,10 +864,11 @@ public class BpmnModelUtils { // 执行计算 try { - Object result = FlowableUtils.getExpressionValue(variables, express); + Object result = FlowableUtils.getExpressionValue(variables, expression); return Boolean.TRUE.equals(result); } catch (FlowableException ex) { - log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", express, variables, ex); + // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 + log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); return Boolean.FALSE; } } From 9698fee36447d3d14a7812c06c6e25f8e4844fb8 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 09:52:43 +0800 Subject: [PATCH 034/116] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmSimpleModelNodeTypeEnum.java | 3 + .../module/bpm/enums/task/BpmReasonEnum.java | 1 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 32 +++++++++ .../core/enums/BpmnVariableConstants.java | 7 ++ .../flowable/core/util/SimpleModelUtils.java | 65 ++++++++++++++++++- .../bpm/service/task/BpmTaskServiceImpl.java | 37 +++++++---- 6 files changed, 132 insertions(+), 13 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index c9da0718df..44cba2bd15 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,6 +30,9 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), + CHILD_PROCESS(20, "子流程", "callActivity"), + ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), + // 50 ~ 条件分支 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java index d41c0c3133..bb6b244b02 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java @@ -26,6 +26,7 @@ public enum BpmReasonEnum { TIMEOUT_REJECT("审批超时,系统自动不通过"), ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 50c45a96a2..23371b19d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -10,6 +10,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.flowable.bpmn.model.IOParameter; import org.hibernate.validator.constraints.URL; import java.util.List; @@ -122,6 +123,8 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + private ChildProcessSetting childProcessSetting; + @Schema(description = "任务监听器") @Valid @Data @@ -397,4 +400,33 @@ public class BpmSimpleModelNodeVO { private Map updateFormFields; } } + + @Schema(description = "子流程节点配置") + @Data + @Valid + public static class ChildProcessSetting { + + @Schema(description = "被调用流程", example = "xxx") + @NotEmpty(message = "被调用流程不能为空") + private String calledElement; + + @Schema(description = "被调用流程名称", example = "xxx") + @NotEmpty(message = "被调用流程名称不能为空") + private String calledElementName; + + @Schema(description = "是否异步", example = "false") + @NotNull(message = "是否异步不能为空") + private Boolean async; + + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariable; + + @Schema(description = "输出参数(子->主)", example = "[]") + private List outVariable; + + @Schema(description = "是否自动跳过子流程发起节点", example = "false") + @NotNull(message = "是否自动跳过子流程发起节点不能为空") + private Boolean skipStartUserNode; + + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 8172cf59a5..119a441857 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -51,6 +51,13 @@ public class BpmnVariableConstants { */ public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + /** + * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; + /** * 流程实例的变量 - 流程开始时间 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e32d93ceb0..d41fc44242 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -22,6 +22,8 @@ import org.springframework.util.MultiValueMap; import java.util.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; import static java.util.Arrays.asList; @@ -42,7 +44,8 @@ public class SimpleModelUtils { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), - new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), + new ChildProcessConvert(), new AsyncChildProcessConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); } @@ -773,6 +776,66 @@ public class SimpleModelUtils { } + private static class ChildProcessConvert implements NodeConvert { + + @Override + public CallActivity convert(BpmSimpleModelNodeVO node) { + BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); + List inVariable = childProcessSetting.getInVariable() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); + CallActivity callActivity = new CallActivity(); + callActivity.setId(node.getId()); + callActivity.setName(node.getName()); + callActivity.setCalledElementType("key"); + // 1. 是否异步 + callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledElement()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); + // 3. 是否自动跳过子流程发起节点 + if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("true"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } else { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("false"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } + // 4. 主→子变量传递 + // 默认需要传递的一些变量 + // 4.1 流程状态 + IOParameter ioParameter = new IOParameter(); + ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS); + inVariable.add(ioParameter); + callActivity.setInParameters(inVariable); + // 5. 子→主变量传递 + if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { + callActivity.setOutParameters(childProcessSetting.getOutVariable()); + } + return callActivity; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; + } + + } + + private static class AsyncChildProcessConvert extends ChildProcessConvert { + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS; + } + + } + + private static String buildGatewayJoinId(String id) { return id + "_join"; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f57f088ef5..e104141965 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.*; import cn.hutool.extra.spring.SpringUtil; @@ -63,6 +64,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; /** @@ -1212,19 +1214,30 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } - // 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 - Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + // 发起人节点 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); + return; + } + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 + // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), + PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + if (userTaskElement.getId().equals(START_USER_NODE_ID) && + (skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) && + !Boolean.TRUE.equals(returnTaskFlag)) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); + return; + } + // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 + if (!userTaskElement.getId().equals(START_USER_NODE_ID) && + StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); - return; - } - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); // 情况一:自动跳过 From 0e48458ef7e85bb6bc8ba5e663885b32164a59f8 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 14:04:00 +0800 Subject: [PATCH 035/116] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=AB=98=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/SimpleModelUtils.java | 4 ++++ .../task/BpmProcessInstanceServiceImpl.java | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d41fc44242..18798bb3fb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -816,6 +816,8 @@ public class SimpleModelUtils { if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 添加节点类型 + addNodeType(node.getType(), callActivity); return callActivity; } @@ -865,6 +867,8 @@ public class SimpleModelUtils { || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS + || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { // 添加元素 resultNodes.add(currentNode); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 632ed47073..c0c1525517 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -320,6 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程 List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -390,7 +391,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List tasks) { // 构建运行中的任务,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null - && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER))); + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); Map> runningTaskMap = convertMultiMap(runActivities, HistoricActivityInstance::getActivityId); @@ -404,7 +405,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”和"子流程"的识别 BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) @@ -413,6 +414,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + if (task == null) { + continue; + } activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); // 加签子任务,需要过滤掉已经完成的加签子任务 List childrenTasks = filterList( @@ -503,6 +507,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService activityNode.setCandidateUserIds(candidateUserIds); return activityNode; } + + // 4. 子流程节点 + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType()) || + BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS.getType().equals(node.getType())) { + return activityNode; + } return null; } From 11858ca0dd3b7063ea7743bb551c1ca73fda5089 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 15:40:23 +0800 Subject: [PATCH 036/116] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E5=AD=90=E6=B5=81=E7=A8=8B=E5=8F=91=E8=B5=B7=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...BpmChildProcessStartUserEmptyTypeEnum.java | 36 ++++++++ .../BpmChildProcessStartUserTypeEnum.java | 35 ++++++++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 22 +++++ .../flowable/core/util/FlowableUtils.java | 3 +- .../flowable/core/util/SimpleModelUtils.java | 14 +++ .../listener/BpmCallActivityListener.java | 88 +++++++++++++++++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java new file mode 100644 index 0000000000..55e6e02e87 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 当子流程发起人为空时类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + CHILD_PROCESS_ADMIN(2, "子流程管理员"), + MAIN_PROCESS_ADMIN(3, "主流程管理员"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java new file mode 100644 index 0000000000..10d04ea4f4 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程发起人类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 23371b19d7..fc1cc2bb4a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -428,5 +428,27 @@ public class BpmSimpleModelNodeVO { @NotNull(message = "是否自动跳过子流程发起节点不能为空") private Boolean skipStartUserNode; + @Schema(description = "子流程发起人配置", example = "{}") + private StartUserSetting startUserSetting; + + @Schema(description = "子流程发起人配置") + @Data + @Valid + public static class StartUserSetting { + + @Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "子流程发起人类型") + @InEnum(BpmChildProcessStartUserTypeEnum.class) + private Integer type; + + @Schema(description = "表单", example = "xxx") + private String formField; + + @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) + private Integer emptyHandleType; + + } + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index a458567d81..998320eb58 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -240,7 +241,7 @@ public class FlowableUtils { return formFieldsMap.entrySet().stream() .limit(3) .map(entry -> new KeyValue<>(entry.getValue().getTitle(), - processVariables.getOrDefault(entry.getValue().getField(), "").toString())) + StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) .collect(Collectors.toList()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 18798bb3fb..fbc38cbeab 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; import cn.iocoder.yudao.module.bpm.enums.definition.*; @@ -16,6 +17,7 @@ import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; +import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.TaskListener; import org.springframework.util.MultiValueMap; @@ -816,6 +818,18 @@ public class SimpleModelUtils { if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 6. 子流程发起人配置 + List executionListeners = new ArrayList<>(); + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(ExecutionListener.EVENTNAME_START); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation("${bpmCallActivityListener}"); + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); + flowableListener.getFieldExtensions().add(fieldExtension); + executionListeners.add(flowableListener); + callActivity.setExecutionListeners(executionListeners); // 添加节点类型 addNodeType(node.getType(), callActivity); return callActivity; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java new file mode 100644 index 0000000000..fe34c6964f --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.bpm.service.task.listener; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; +import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * BPM 子流程监听器 + * + * @author Lesan + */ +@Component +@Slf4j +public class BpmCallActivityListener implements ExecutionListener { + + public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; + + @Setter + private FixedValue listenerConfig; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Override + public void notify(DelegateExecution execution) { + String expressionText = listenerConfig.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + // 1. 当发起人来源为表单时 + if (startUserSetting != null && + startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + String formField = parent.getVariable(startUserSetting.getFormField(), String.class); + // 1.1 当表单值为空时 + if (StrUtil.isEmpty(formField)) { + // 1.1.1 来自主流程发起人 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())){ + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + return; + } + // 1.1.2 来自子流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())){ + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + return; + } + // 1.1.3 来自主流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())){ + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + return; + } + } + // 1.2 使用表单值,并兜底字符串转Long失败时使用主流程发起人 + try { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formField)); + } catch (Exception e) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + } + } + // 2. 当发起人来源为主流程发起人时,并兜底startUserSetting为空时 + if (startUserSetting == null || + startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + } + } + +} \ No newline at end of file From cbdc081cfe348bae4728eab704b96eb30d21617d Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Fri, 21 Feb 2025 22:41:49 +0800 Subject: [PATCH 037/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E5=88=A0=E9=99=A4=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/definition/BpmTriggerTypeEnum.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 6 +- .../task/BpmProcessInstanceService.java | 26 ++++--- .../task/BpmProcessInstanceServiceImpl.java | 16 +++-- .../task/trigger/BpmFormDeleteTrigger.java | 72 +++++++++++++++++++ 5 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 42d8bafabb..8944528c06 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,7 +17,8 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - FORM_UPDATE(2, "更新流程表单"); + FORM_UPDATE(2, "更新流程表单数据"), + FORM_DELETE(3, "删除流程表单数据"); /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 50c45a96a2..0f4ac41a04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -14,6 +14,7 @@ import org.hibernate.validator.constraints.URL; import java.util.List; import java.util.Map; +import java.util.Set; @Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") @Data @@ -393,8 +394,11 @@ public class BpmSimpleModelNodeVO { @Schema(description = "条件组", example = "{}") private ConditionGroups conditionGroups; - @Schema(description = "修改的表单字段", example = "userName") + @Schema(description = "修改的表单字段", example = "{}") private Map updateFormFields; + + @Schema(description = "删除表单字段", example = "[]") + private Set deleteFields; } } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 55b86a5514..83d453ed4e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -7,6 +7,7 @@ import jakarta.validation.Valid; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -148,15 +149,6 @@ public interface BpmProcessInstanceService { */ void updateProcessInstanceReject(ProcessInstance processInstance, String reason); - // ========== Event 事件相关方法 ========== - - /** - * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 - * - * @param instance 流程任务 - */ - void processProcessInstanceCompleted(ProcessInstance instance); - /** * 更新 ProcessInstance 的变量 * @@ -165,4 +157,20 @@ public interface BpmProcessInstanceService { */ void updateProcessInstanceVariables(String id, Map variables); + /** + * 删除 ProcessInstance 的变量 + * + * @param processInstanceId 流程编号 + * @param variableNames 流程变量名 + */ + void removeProcessInstanceVariables(String processInstanceId, Collection variableNames); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 + * + * @param instance 流程任务 + */ + void processProcessInstanceCompleted(ProcessInstance instance); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index dc6788a9e2..cc314d347a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -776,6 +776,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmReasonEnum.REJECT_TASK.format(reason)); } + @Override + public void updateProcessInstanceVariables(String id, Map variables) { + runtimeService.setVariables(id, variables); + } + + @Override + public void removeProcessInstanceVariables(String processInstanceId, Collection variableNames) { + runtimeService.removeVariables(processInstanceId, variableNames); + } + // ========== Event 事件相关方法 ========== @Override @@ -809,10 +819,4 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); }); } - - @Override - public void updateProcessInstanceVariables(String id, Map variables) { - runtimeService.setVariables(id, variables); - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java new file mode 100644 index 0000000000..488c48b082 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * BPM 删除流程表单数据触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormDeleteTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_DELETE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析删除流程表单数据配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 删除流程表单数据触发器配置为空]", processInstanceId); + return; + } + + // 2.获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.获取需要删除的表单字段 + Set deleteFields = new HashSet<>(); + settings.forEach(setting -> { + if (CollUtil.isEmpty(setting.getDeleteFields())) { + return; + } + // 配置了条件,判断条件是否满足 + boolean isFieldDeletedNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFieldDeletedNeeded) { + deleteFields.addAll(setting.getDeleteFields()); + } + }); + + // 4. 删除流程变量 + if (CollUtil.isNotEmpty(deleteFields)) { + processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); + } + } +} From 7a6d1bdd791cc84f4746d6a096b6d0c7dc28bb2b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 16:20:04 +0800 Subject: [PATCH 038/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=88=A0=E9=99=A4=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E6=95=B0=E6=8D=AE=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceService.java | 4 ++-- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++-- .../bpm/service/task/trigger/BpmFormDeleteTrigger.java | 6 +++--- .../bpm/service/task/trigger/BpmFormUpdateTrigger.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 83d453ed4e..c4684d3402 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -160,10 +160,10 @@ public interface BpmProcessInstanceService { /** * 删除 ProcessInstance 的变量 * - * @param processInstanceId 流程编号 + * @param id 流程编号 * @param variableNames 流程变量名 */ - void removeProcessInstanceVariables(String processInstanceId, Collection variableNames); + void removeProcessInstanceVariables(String id, Collection variableNames); // ========== Event 事件相关方法 ========== diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 271561eeaf..28993d40d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -790,8 +790,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override - public void removeProcessInstanceVariables(String processInstanceId, Collection variableNames) { - runtimeService.removeVariables(processInstanceId, variableNames); + public void removeProcessInstanceVariables(String id, Collection variableNames) { + runtimeService.removeVariables(id, variableNames); } // ========== Event 事件相关方法 ========== diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java index 488c48b082..ab29c502d5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java @@ -43,10 +43,10 @@ public class BpmFormDeleteTrigger implements BpmTrigger { return; } - // 2.获取流程变量 + // 2. 获取流程变量 Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - // 3.获取需要删除的表单字段 + // 3.1 获取需要删除的表单字段 Set deleteFields = new HashSet<>(); settings.forEach(setting -> { if (CollUtil.isEmpty(setting.getDeleteFields())) { @@ -64,7 +64,7 @@ public class BpmFormDeleteTrigger implements BpmTrigger { } }); - // 4. 删除流程变量 + // 3.2 删除流程变量 if (CollUtil.isNotEmpty(deleteFields)) { processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java index 0382397d08..9ec2608b93 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -41,10 +41,10 @@ public class BpmFormUpdateTrigger implements BpmTrigger { return; } - // 2.获取流程变量 + // 2. 获取流程变量 Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - // 3.更新流程变量 + // 3. 更新流程变量 for (FormTriggerSetting setting : settings) { if (CollUtil.isEmpty(setting.getUpdateFormFields())) { continue; From 254b55778f839de4639c4831ca0110f89bbbaf36 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 17:17:27 +0800 Subject: [PATCH 039/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmSimpleModelNodeTypeEnum.java | 2 +- .../module/bpm/enums/task/BpmReasonEnum.java | 2 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 19 +++++-- .../flowable/core/util/FlowableUtils.java | 6 ++- .../flowable/core/util/SimpleModelUtils.java | 36 ++++++++----- .../task/BpmProcessInstanceServiceImpl.java | 5 +- .../bpm/service/task/BpmTaskServiceImpl.java | 13 ++--- .../listener/BpmCallActivityListener.java | 53 ++++++++++--------- 8 files changed, 81 insertions(+), 55 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index 44cba2bd15..b21bc65313 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,7 +30,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), - CHILD_PROCESS(20, "子流程", "callActivity"), + CHILD_PROCESS(20, "子流程", "callActivity"), // TODO @lesan:CHILD_PROCESS、ASYNC_CHILD_PROCESS 可以合并为一个么? ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), // 50 ~ 条件分支 diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java index bb6b244b02..b0ade75299 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java @@ -26,7 +26,7 @@ public enum BpmReasonEnum { TIMEOUT_REJECT("审批超时,系统自动不通过"), ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), - ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), // 目前仅“子流程”使用 ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index b238779a48..9ba95d87e3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -124,6 +124,9 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + /** + * 子流程设置 + */ private ChildProcessSetting childProcessSetting; @Schema(description = "任务监听器") @@ -410,21 +413,24 @@ public class BpmSimpleModelNodeVO { @Valid public static class ChildProcessSetting { - @Schema(description = "被调用流程", example = "xxx") + // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题? + @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程不能为空") private String calledElement; - @Schema(description = "被调用流程名称", example = "xxx") + @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程名称不能为空") private String calledElementName; - @Schema(description = "是否异步", example = "false") + @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @NotNull(message = "是否异步不能为空") private Boolean async; - @Schema(description = "输入参数(主->子)", example = "[]") + // TODO @lesan:inVariables + @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]") private List inVariable; + // TODO @lesan:outVariables @Schema(description = "输出参数(子->主)", example = "[]") private List outVariable; @@ -432,7 +438,8 @@ public class BpmSimpleModelNodeVO { @NotNull(message = "是否自动跳过子流程发起节点不能为空") private Boolean skipStartUserNode; - @Schema(description = "子流程发起人配置", example = "{}") + @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + // TODO @lesan:这个应该也必须填写? private StartUserSetting startUserSetting; @Schema(description = "子流程发起人配置") @@ -448,7 +455,9 @@ public class BpmSimpleModelNodeVO { @Schema(description = "表单", example = "xxx") private String formField; + // TODO @lesan:emptyHandleType => emptyType,和 type 对上? @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "当子流程发起人为空时类型不能为空") @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) private Integer emptyHandleType; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 998320eb58..7ab71a0470 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -25,7 +25,10 @@ import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.TaskInfo; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -241,6 +244,7 @@ public class FlowableUtils { return formFieldsMap.entrySet().stream() .limit(3) .map(entry -> new KeyValue<>(entry.getValue().getTitle(), + // TODO @lesan: MapUtil.getStr 可以更简单? StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) .collect(Collectors.toList()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index fbc38cbeab..a92ab7044e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -11,8 +11,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.B import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; +import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener; +import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.Process; @@ -24,10 +27,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; -import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; import static java.util.Arrays.asList; /** @@ -462,7 +462,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskCreateListener()); flowableListeners.add(flowableListener); } @@ -471,7 +471,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskAssignListener()); flowableListeners.add(flowableListener); } @@ -480,7 +480,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskCompleteListener()); flowableListeners.add(flowableListener); } @@ -788,48 +788,56 @@ public class SimpleModelUtils { CallActivity callActivity = new CallActivity(); callActivity.setId(node.getId()); callActivity.setName(node.getName()); - callActivity.setCalledElementType("key"); + callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈? // 1. 是否异步 callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); + // 2. 调用的子流程 callActivity.setCalledElement(childProcessSetting.getCalledElement()); callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); + // 3. 是否自动跳过子流程发起节点 + // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()??? if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { IOParameter ioParameter = new IOParameter(); ioParameter.setSourceExpression("true"); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); inVariable.add(ioParameter); } else { IOParameter ioParameter = new IOParameter(); ioParameter.setSourceExpression("false"); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); inVariable.add(ioParameter); } + // 4. 主→子变量传递 - // 默认需要传递的一些变量 - // 4.1 流程状态 + // 4.1 【默认需要传递的一些变量】流程状态 + // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈 IOParameter ioParameter = new IOParameter(); - ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); inVariable.add(ioParameter); callActivity.setInParameters(inVariable); + // 5. 子→主变量传递 + // TODO @lesan:通过 isNotEmpty 这种哈 if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 6. 子流程发起人配置 List executionListeners = new ArrayList<>(); FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(ExecutionListener.EVENTNAME_START); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation("${bpmCallActivityListener}"); + flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION); FieldExtension fieldExtension = new FieldExtension(); fieldExtension.setFieldName("listenerConfig"); fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); flowableListener.getFieldExtensions().add(fieldExtension); executionListeners.add(flowableListener); callActivity.setExecutionListeners(executionListeners); + // 添加节点类型 addNodeType(node.getType(), callActivity); return callActivity; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 758d63a79e..558180dd3f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -320,7 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会有啥影响?微信聊? List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -389,7 +389,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map processVariables, List activities, List tasks) { - // 构建运行中的任务,基于 activityId 分组 + // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); Map> runningTaskMap = convertMultiMap(runActivities, @@ -414,6 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + // TODO @lesan:这里为啥 continue 哈? if (task == null) { continue; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index e104141965..902ce8529d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1214,7 +1214,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } - // 发起人节点 + // 获取发起人节点 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); if (bpmnModel == null) { log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); @@ -1227,16 +1227,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); - if (userTaskElement.getId().equals(START_USER_NODE_ID) && - (skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) && - !Boolean.TRUE.equals(returnTaskFlag)) { + if (userTaskElement.getId().equals(START_USER_NODE_ID) + && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + && !Boolean.TRUE.equals(returnTaskFlag)) { // TODO @lesan:ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE) 改成这个有问题么?尽量不用 ! 取反 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); return; } // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (!userTaskElement.getId().equals(START_USER_NODE_ID) && - StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { + if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) + && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index fe34c6964f..2be4fcc8de 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; -import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -22,7 +21,7 @@ import org.springframework.stereotype.Component; import java.util.List; /** - * BPM 子流程监听器 + * BPM 子流程监听器:设置流程的发起人 * * @author Lesan */ @@ -42,47 +41,51 @@ public class BpmCallActivityListener implements ExecutionListener { public void notify(DelegateExecution execution) { String expressionText = listenerConfig.getExpressionText(); Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); - BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); - // 1. 当发起人来源为表单时 - if (startUserSetting != null && - startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( + expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + + // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 + if (startUserSetting == null + || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - String formField = parent.getVariable(startUserSetting.getFormField(), String.class); - // 1.1 当表单值为空时 - if (StrUtil.isEmpty(formField)) { - // 1.1.1 来自主流程发起人 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())){ + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + return; + } + + // 2. 当发起人来源为表单时 + if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + String formFieldValue = parent.getVariable(startUserSetting.getFormField(), String.class); + // 2.1 当表单值为空时 + if (StrUtil.isEmpty(formFieldValue)) { + // 2.1.1 来自主流程发起人 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); return; } - // 1.1.2 来自子流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())){ + // 2.1.2 来自子流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; } - // 1.1.3 来自主流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())){ + // 2.1.3 来自主流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; } } - // 1.2 使用表单值,并兜底字符串转Long失败时使用主流程发起人 + // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 try { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(formField)); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); } catch (Exception e) { + // todo @lesan:打个日志,方便排查 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); } } - // 2. 当发起人来源为主流程发起人时,并兜底startUserSetting为空时 - if (startUserSetting == null || - startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { - ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); - } } } \ No newline at end of file From 3f8221ba845420160107cfedac30745751e3fb69 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Sat, 22 Feb 2025 22:51:56 +0800 Subject: [PATCH 040/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91=20=E6=96=B0=E5=A2=9E=E5=BC=82=E6=AD=A5=20htt?= =?UTF-8?q?p=20=E8=AF=B7=E6=B1=82=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 8 +++ .../enums/definition/BpmTriggerTypeEnum.java | 3 +- .../api/task/BpmProcessInstanceApiImpl.java | 9 +++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 12 ++++ .../core/listener/BpmTaskEventListener.java | 3 +- .../flowable/core/util/SimpleModelUtils.java | 60 +++++++++++++---- .../bpm/service/task/BpmTaskService.java | 4 +- .../bpm/service/task/BpmTaskServiceImpl.java | 2 +- .../BpmAbstractHttpRequestTrigger.java | 66 +++++++++++++++++++ .../trigger/BpmAsyncHttpRequestTrigger.java | 50 ++++++++++++++ ...er.java => BpmSyncHttpRequestTrigger.java} | 48 ++++---------- 11 files changed, 211 insertions(+), 54 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{BpmHttpRequestTrigger.java => BpmSyncHttpRequestTrigger.java} (63%) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java index e4eddf6ebd..89311ed75d 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java @@ -20,4 +20,12 @@ public interface BpmProcessInstanceApi { */ String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO); + + /** + * 异步 HTTP 请求触发器回调, 为了唤醒流程继续执行 + * + * @param processInstanceId 流程实例编号 + * @param callbackId 回调编号, 对应 ReceiveTask Id + */ + void asyncHttpTriggerCallback(String processInstanceId, String callbackId); } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 8944528c06..61dc2a5f71 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -18,7 +18,8 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), FORM_UPDATE(2, "更新流程表单数据"), - FORM_DELETE(3, "删除流程表单数据"); + FORM_DELETE(3, "删除流程表单数据"), + ASYNC_HTTP_REQUEST(4, "发起异步 HTTP 请求"); /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index f8aea4d74e..a639de7e0f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.api.task; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -21,8 +22,16 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource private BpmProcessInstanceService processInstanceService; + @Resource + private BpmTaskService bpmTaskService; + @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return processInstanceService.createProcessInstance(userId, reqDTO); } + + @Override + public void asyncHttpTriggerCallback(String processInstanceId, String callbackId) { + bpmTaskService.triggerReceiveTask(processInstanceId, callbackId); + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 0f4ac41a04..4591bf1417 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; @@ -123,6 +124,13 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + /** + * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 + */ + @JsonIgnore + private String attachNodeId; // 目前用于异步触发器节点。需要 UserTask 和 ReceiveTask(附加节点) 来完成 + + @Schema(description = "任务监听器") @Valid @Data @@ -378,6 +386,10 @@ public class BpmSimpleModelNodeVO { @Schema(description = "请求返回处理设置", example = "[]") private List> response; + /** + * 异步 Http 请求,需要指定回调 Id,用于回调执行. 由后端指定 + */ + private String callbackId; } @Schema(description = "流程表单触发器设置", example = "{}") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index ecef8fdb48..bb256001b0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -109,8 +109,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { // 2.2 延迟器超时处理 } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { String taskKey = boundaryEvent.getAttachedToRefId(); - taskService.processDelayTimerTimeout(event.getProcessInstanceId(), taskKey); + taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); } } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e32d93ceb0..04a14e3180 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -21,6 +21,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.ASYNC_HTTP_REQUEST; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; @@ -164,8 +165,15 @@ public class SimpleModelUtils { // 情况一:有“子”节点,则建立连线 // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); - process.addFlowElement(sequenceFlow); + + if (StrUtil.isEmpty(node.getAttachNodeId())) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); + process.addFlowElement(sequenceFlow); + } else { + // 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 + List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); + sequenceFlows.forEach(process::addFlowElement); + } // 因为有子节点,递归调用后续子节点 if (isChildNodeValid) { @@ -173,6 +181,19 @@ public class SimpleModelUtils { } } + /** + * 构建有附加节点的连线 + * + * @param nodeId 当前节点 Id + * @param attachNodeId 附属节点 Id + * @param targetNodeId 目标节点 Id + */ + private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); + SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); + return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); + } + /** * 遍历条件节点,构建 SequenceFlow 元素 * @@ -723,23 +744,38 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { @Override - public ServiceTask convert(BpmSimpleModelNodeVO node) { + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); serviceTask.setName(node.getName()); serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); - if (node.getTriggerSetting() != null) { - addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); - if (node.getTriggerSetting().getHttpRequestSetting() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); - } - if (node.getTriggerSetting().getFormSettings() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); - } + Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); + + addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); + flowElements.add(serviceTask); + // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (ASYNC_HTTP_REQUEST.getType().equals(node.getTriggerSetting().getType())) { + String attachNodeId = "Activity_" + IdUtil.fastUUID(); + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId(attachNodeId); + receiveTask.setName("异步 HTTP 请求"); + node.setAttachNodeId(attachNodeId); + // 设置 receiveId + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 http 请求设置不能为空"); + node.getTriggerSetting().getHttpRequestSetting().setCallbackId(attachNodeId); + flowElements.add(receiveTask); } - return serviceTask; + + if (node.getTriggerSetting().getHttpRequestSetting() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); + } + if (node.getTriggerSetting().getFormSettings() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); + } + return flowElements; } @Override diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index bff859b063..6dce076f87 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -277,11 +277,11 @@ public interface BpmTaskService { void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); /** - * 处理 延迟器 超时事件 + * 触发 ReceiveTask, 让流程继续执行 * * @param processInstanceId 流程示例编号 * @param taskDefineKey 任务 Key */ - void processDelayTimerTimeout(String processInstanceId, String taskDefineKey); + void triggerReceiveTask(String processInstanceId, String taskDefineKey); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f57f088ef5..c098697f7a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1309,7 +1309,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public void processDelayTimerTimeout(String processInstanceId, String taskDefineKey) { + public void triggerReceiveTask(String processInstanceId, String taskDefineKey) { Execution execution = runtimeService.createExecutionQuery() .processInstanceId(processInstanceId) .activityId(taskDefineKey) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java new file mode 100644 index 0000000000..a10aa8dc38 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * BPM 发送 HTTP 请求触发器抽象类 + * + * @author jason + */ +@Slf4j +public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { + + @Resource + private RestTemplate restTemplate; + + protected ResponseEntity sendHttpRequest(String url, + MultiValueMap headers, + MultiValueMap body) { + // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢? + // 3. 发起请求 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity = null; + try { + responseEntity = restTemplate.exchange(url, HttpMethod.POST, + requestEntity, String.class); + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + } + return responseEntity; + } + + protected MultiValueMap buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + SimpleModelUtils.addHttpRequestParam(headers, headerSettings, processVariables); + return headers; + } + + protected MultiValueMap buildHttpBody(ProcessInstance processInstance, + List bodySettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap body = new LinkedMultiValueMap<>(); + SimpleModelUtils.addHttpRequestParam(body, bodySettings, processVariables); + body.add("processInstanceId", processInstance.getId()); + return body; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java new file mode 100644 index 0000000000..189cc401af --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; + +/** + * BPM 发送异步 HTTP 请求触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.ASYNC_HTTP_REQUEST; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); + if (setting == null) { + log.error("[execute][流程({}) HTTP 异步触发器请求配置为空]", processInstanceId); + return; + } + + // 2.1 设置请求头 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); + // 2.2 设置请求体 + MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); + body.add("callbackId", setting.getCallbackId()); // 异步请求 callbackId 需要传给被调用方. 用于回调执行 + + // 3. 发起请求 + sendHttpRequest(setting.getUrl(), headers, body); + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java similarity index 63% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java index 52936f93f9..e01ebc3c6c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java @@ -7,42 +7,31 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import com.fasterxml.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; - /** - * BPM 发送 HTTP 请求触发器 + * BPM 发送同步 HTTP 请求触发器 * * @author jason */ @Component @Slf4j -public class BpmHttpRequestTrigger implements BpmTrigger { +public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { @Resource private BpmProcessInstanceService processInstanceService; - @Resource - private RestTemplate restTemplate; - @Override public BpmTriggerTypeEnum getType() { return BpmTriggerTypeEnum.HTTP_REQUEST; @@ -56,40 +45,27 @@ public class BpmHttpRequestTrigger implements BpmTrigger { log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); return; } + // 2.1 设置请求头 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); - SimpleModelUtils.addHttpRequestParam(headers, setting.getHeader(), processVariables); + MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 - MultiValueMap body = new LinkedMultiValueMap<>(); - SimpleModelUtils.addHttpRequestParam(body, setting.getBody(), processVariables); - body.add("processInstanceId", processInstanceId); + MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢? // 3. 发起请求 - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity responseEntity; - try { - responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST, - requestEntity, String.class); - log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); - } catch (RestClientException e) { - log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); - return; - } + ResponseEntity responseEntity = sendHttpRequest(setting.getUrl(), headers, body); // 4.1 判断是否需要解析返回值 - if (StrUtil.isEmpty(responseEntity.getBody()) + if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) || !responseEntity.getStatusCode().is2xxSuccessful() || CollUtil.isEmpty(setting.getResponse())) { return; } // 4.2 解析返回值, 返回值必须符合 CommonResult 规范。 CommonResult> respResult = JsonUtils.parseObjectQuietly( - responseEntity.getBody(), new TypeReference<>() {}); - if (respResult == null || !respResult.isSuccess()){ + responseEntity.getBody(), new TypeReference<>() { + }); + if (respResult == null || !respResult.isSuccess()) { return; } // 4.3 获取需要更新的流程变量 @@ -103,11 +79,11 @@ public class BpmHttpRequestTrigger implements BpmTrigger { /** * 从请求返回值获取需要更新的流程变量 * - * @param result 请求返回结果 + * @param result 请求返回结果 * @param responseSettings 返回设置 * @return 需要更新的流程变量 */ - private Map getNeedUpdatedVariablesFromResponse(Map result, + private Map getNeedUpdatedVariablesFromResponse(Map result, List> responseSettings) { Map updateVariables = new HashMap<>(); if (CollUtil.isEmpty(result)) { From f45758b8fdac813c0e7b26ab798ab610be2d9374 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 23 Feb 2025 20:12:05 +0800 Subject: [PATCH 041/116] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=87=AA=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index e284e42706..1976e13a8b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -173,7 +173,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); - processVariables = historicProcessInstance.getProcessVariables(); + // 如果流程变量为空,则使用历史流程变量 + if (null == processVariables) { + processVariables = historicProcessInstance.getProcessVariables(); + } } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( From dee2b7cb96d7e7b3385c49301835ea503f74095f Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Mon, 24 Feb 2025 09:38:37 +0800 Subject: [PATCH 042/116] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmSimpleModelNodeTypeEnum.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 20 +++--- .../flowable/core/util/FlowableUtils.java | 5 +- .../flowable/core/util/SimpleModelUtils.java | 65 +++++++------------ .../task/BpmProcessInstanceServiceImpl.java | 7 +- .../bpm/service/task/BpmTaskServiceImpl.java | 2 +- .../listener/BpmCallActivityListener.java | 9 +-- 7 files changed, 43 insertions(+), 68 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index b21bc65313..e5ffa12025 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,8 +30,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), - CHILD_PROCESS(20, "子流程", "callActivity"), // TODO @lesan:CHILD_PROCESS、ASYNC_CHILD_PROCESS 可以合并为一个么? - ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), + CHILD_PROCESS(20, "子流程", "callActivity"), // 50 ~ 条件分支 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 2f36e3eecb..bc2c72898f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -425,33 +425,30 @@ public class BpmSimpleModelNodeVO { @Valid public static class ChildProcessSetting { - // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题? @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程不能为空") - private String calledElement; + private String calledProcessDefinitionKey; @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程名称不能为空") - private String calledElementName; + private String calledProcessDefinitionName; @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @NotNull(message = "是否异步不能为空") private Boolean async; - // TODO @lesan:inVariables - @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]") - private List inVariable; + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariables; - // TODO @lesan:outVariables @Schema(description = "输出参数(子->主)", example = "[]") - private List outVariable; + private List outVariables; - @Schema(description = "是否自动跳过子流程发起节点", example = "false") + @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @NotNull(message = "是否自动跳过子流程发起节点不能为空") private Boolean skipStartUserNode; @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - // TODO @lesan:这个应该也必须填写? + @NotNull(message = "子流程发起人配置不能为空") private StartUserSetting startUserSetting; @Schema(description = "子流程发起人配置") @@ -467,11 +464,10 @@ public class BpmSimpleModelNodeVO { @Schema(description = "表单", example = "xxx") private String formField; - // TODO @lesan:emptyHandleType => emptyType,和 type 对上? @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "当子流程发起人为空时类型不能为空") @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) - private Integer emptyHandleType; + private Integer emptyType; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 7ab71a0470..6022b8fe93 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -244,8 +244,7 @@ public class FlowableUtils { return formFieldsMap.entrySet().stream() .limit(3) .map(entry -> new KeyValue<>(entry.getValue().getTitle(), - // TODO @lesan: MapUtil.getStr 可以更简单? - StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) + MapUtil.getStr(processVariables, entry.getValue().getField(), ""))) .collect(Collectors.toList()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 19c804a754..412f630f95 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -48,7 +48,7 @@ public class SimpleModelUtils { new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), - new ChildProcessConvert(), new AsyncChildProcessConvert()); + new ChildProcessConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); } @@ -819,46 +819,38 @@ public class SimpleModelUtils { @Override public CallActivity convert(BpmSimpleModelNodeVO node) { BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); - List inVariable = childProcessSetting.getInVariable() == null ? - new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); + List inVariables = childProcessSetting.getInVariables() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables()); CallActivity callActivity = new CallActivity(); callActivity.setId(node.getId()); callActivity.setName(node.getName()); - callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈? + callActivity.setCalledElementType("key"); // 1. 是否异步 - callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); - - // 2. 调用的子流程 - callActivity.setCalledElement(childProcessSetting.getCalledElement()); - callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); - - // 3. 是否自动跳过子流程发起节点 - // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()??? - if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { - IOParameter ioParameter = new IOParameter(); - ioParameter.setSourceExpression("true"); - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); - inVariable.add(ioParameter); - } else { - IOParameter ioParameter = new IOParameter(); - ioParameter.setSourceExpression("false"); - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); - inVariable.add(ioParameter); + if (node.getChildProcessSetting().getAsync()) { + // TODO @lesan: 这里目前测试没有跳过执行call activity 后面的节点 + callActivity.setAsynchronous(true); } - // 4. 主→子变量传递 - // 4.1 【默认需要传递的一些变量】流程状态 - // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈 + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName()); + + // 3. 是否自动跳过子流程发起节点 IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString()); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariables.add(ioParameter); + + // 4. 【默认需要传递的一些变量】流程状态 + ioParameter = new IOParameter(); ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - inVariable.add(ioParameter); - callActivity.setInParameters(inVariable); + inVariables.add(ioParameter); - // 5. 子→主变量传递 - // TODO @lesan:通过 isNotEmpty 这种哈 - if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { - callActivity.setOutParameters(childProcessSetting.getOutVariable()); + // 5. 主→子变量传递、子->主变量传递 + callActivity.setInParameters(inVariables); + if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) { + callActivity.setOutParameters(childProcessSetting.getOutVariables()); } // 6. 子流程发起人配置 @@ -886,16 +878,6 @@ public class SimpleModelUtils { } - private static class AsyncChildProcessConvert extends ChildProcessConvert { - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS; - } - - } - - private static String buildGatewayJoinId(String id) { return id + "_join"; } @@ -926,7 +908,6 @@ public class SimpleModelUtils { || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS - || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { // 添加元素 resultNodes.add(currentNode); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 558180dd3f..b5c9709bd1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -320,7 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会有啥影响?微信聊? + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会导致timeline不会展示已结束的子流程 List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // TODO @lesan:这里为啥 continue 哈? + // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错 if (task == null) { continue; } @@ -510,8 +510,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 4. 子流程节点 - if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType()) || - BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS.getType().equals(node.getType())) { + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) { return activityNode; } return null; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index e91c7e8f35..8f59e6e99d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1230,7 +1230,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (userTaskElement.getId().equals(START_USER_NODE_ID) && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 - && !Boolean.TRUE.equals(returnTaskFlag)) { // TODO @lesan:ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE) 改成这个有问题么?尽量不用 ! 取反 + && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); return; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index 2be4fcc8de..1c2d980380 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -59,19 +59,19 @@ public class BpmCallActivityListener implements ExecutionListener { // 2.1 当表单值为空时 if (StrUtil.isEmpty(formFieldValue)) { // 2.1.1 来自主流程发起人 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); return; } // 2.1.2 来自子流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; } // 2.1.3 来自主流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); @@ -82,7 +82,8 @@ public class BpmCallActivityListener implements ExecutionListener { try { FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); } catch (Exception e) { - // todo @lesan:打个日志,方便排查 + log.error("[error][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", + DELEGATE_EXPRESSION, formFieldValue); FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); } } From a42ad08bc949337dadb6c3f60508bb4ace214be1 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Mon, 24 Feb 2025 13:20:52 +0800 Subject: [PATCH 043/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E5=BC=82=E6=AD=A5=20http=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E8=A7=A6=E5=8F=91=E5=99=A8=E5=A4=96=E9=83=A8=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=20controller=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../instance/BpmHttpTriggerCallbackReqVO.java | 18 ++++++++++++++++++ .../trigger/BpmAbstractHttpRequestTrigger.java | 5 ++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fbc7ba223..3a50bba523 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -39,6 +39,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); + ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java new file mode 100644 index 0000000000..9d60840ba2 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - Bpm 异步 Http 触发器请求回调 Request VO") +@Data +public class BpmHttpTriggerCallbackReqVO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "dca1cdcc-b8fe-11ef-99b5-00ff4722db8b") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @Schema(description = "回调编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "dca1cdcc-b8fe-11ef-99b5-01ff4722db8b") + @NotEmpty(message = "回调编号不能为空") + private String callbackId; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java index a10aa8dc38..cbe1d5aea1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java @@ -16,7 +16,9 @@ import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; /** * BPM 发送 HTTP 请求触发器抽象类 @@ -35,13 +37,14 @@ public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢? // 3. 发起请求 HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity responseEntity = null; + ResponseEntity responseEntity; try { responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); } catch (RestClientException e) { log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); } return responseEntity; } From fda6aff8af3a9faa85736d99bca4a7c42a924b2e Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Mon, 24 Feb 2025 13:25:22 +0800 Subject: [PATCH 044/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E5=BC=82=E6=AD=A5=20http=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E8=A7=A6=E5=8F=91=E5=99=A8=E5=A4=96=E9=83=A8=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=20controller=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/BpmProcessInstanceController.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 67e7219f1d..5222877753 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; @@ -179,4 +180,12 @@ public class BpmProcessInstanceController { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + @PostMapping("/http-trigger/callback") + @Operation(summary = "异步 HTTP 请求触发器回调") + @PermitAll // 允许外部调用,不需要登录。 TODO @芋艿 需要加一下验证签名吗? + public CommonResult httpTriggerCallback(@Valid @RequestBody BpmHttpTriggerCallbackReqVO reqVO) { + taskService.triggerReceiveTask(reqVO.getId(), reqVO.getCallbackId()); + return success(true); + } + } From 15d0a11badab81b0338fac48d7b176e97bc93161 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Mon, 24 Feb 2025 15:30:15 +0800 Subject: [PATCH 045/116] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/BpmBoundaryEventTypeEnum.java | 3 ++- .../vo/model/simple/BpmSimpleModelNodeVO.java | 21 +++++++++++++++++ .../core/listener/BpmTaskEventListener.java | 3 +++ .../flowable/core/util/SimpleModelUtils.java | 23 +++++++++++++++++-- .../bpm/service/task/BpmTaskService.java | 8 +++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 12 ++++++++++ 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java index a63804de32..69c5512599 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java @@ -14,7 +14,8 @@ import lombok.Getter; public enum BpmBoundaryEventTypeEnum { USER_TASK_TIMEOUT(1, "用户任务超时"), - DELAY_TIMER_TIMEOUT(2, "延迟器超时"); + DELAY_TIMER_TIMEOUT(2, "延迟器超时"), + CHILD_PROCESS_TIMEOUT(3, "子流程超时"); private final Integer type; private final String name; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index bc2c72898f..a85a75c160 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -451,6 +451,9 @@ public class BpmSimpleModelNodeVO { @NotNull(message = "子流程发起人配置不能为空") private StartUserSetting startUserSetting; + @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private TimeoutSetting timeoutSetting; + @Schema(description = "子流程发起人配置") @Data @Valid @@ -471,5 +474,23 @@ public class BpmSimpleModelNodeVO { } + @Schema(description = "超时设置") + @Data + @Valid + public static class TimeoutSetting { + + @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启超时设置不能为空") + private Boolean enable; + + @Schema(description = "时间类型", example = "1") + @InEnum(BpmDelayTimerTypeEnum.class) + private Integer type; + + @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00") + private String timeExpression; + + } + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index bb256001b0..5f88987f28 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -110,6 +110,9 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { String taskKey = boundaryEvent.getAttachedToRefId(); taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) { + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey); } } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 412f630f95..cae5cb9421 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -817,7 +817,8 @@ public class SimpleModelUtils { private static class ChildProcessConvert implements NodeConvert { @Override - public CallActivity convert(BpmSimpleModelNodeVO node) { + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); List inVariables = childProcessSetting.getInVariables() == null ? new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables()); @@ -866,9 +867,27 @@ public class SimpleModelUtils { executionListeners.add(flowableListener); callActivity.setExecutionListeners(executionListeners); + // 7. 超时设置 + if (childProcessSetting.getTimeoutSetting() != null) { + BoundaryEvent boundaryEvent = new BoundaryEvent(); + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); + boundaryEvent.setCancelActivity(false); + boundaryEvent.setAttachedToRef(callActivity); + TimerEventDefinition eventDefinition = new TimerEventDefinition(); + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression()); + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression()); + } + boundaryEvent.addEventDefinition(eventDefinition); + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType()); + flowElements.add(boundaryEvent); + } + // 添加节点类型 addNodeType(node.getType(), callActivity); - return callActivity; + flowElements.add(callActivity); + return flowElements; } @Override diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 6dce076f87..4c83e30f8a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -284,4 +284,12 @@ public interface BpmTaskService { */ void triggerReceiveTask(String processInstanceId, String taskDefineKey); + /** + * 处理 子流程 审批超时事件 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void processChildProcessTimeout(String processInstanceId, String taskDefineKey); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 8f59e6e99d..6bd43749a6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -41,6 +41,7 @@ import org.flowable.engine.ManagementService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.runtime.ActivityInstance; import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.DelegationState; @@ -1339,6 +1340,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { () -> runtimeService.trigger(execution.getId())); } + @Override + @Transactional(rollbackFor = Exception.class) + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { + List activityInstances = runtimeService.createActivityInstanceQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey).list(); + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), () -> { + moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()); + })); + } + /** * 获得自身的代理对象,解决 AOP 生效问题 * From 5d163550424d40d0025293a9d8bc78f11b675a7c Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 24 Feb 2025 16:31:03 +0800 Subject: [PATCH 046/116] =?UTF-8?q?fix=EF=BC=9A=201=E3=80=81=E9=A2=84?= =?UTF-8?q?=E6=B5=8B=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=EF=BC=8C=E5=A6=82=E6=9E=9C=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=EF=BC=8C=E5=88=99=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=A9=BA?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=202=E3=80=81=E5=A6=82=E6=9E=9CprocessVariabl?= =?UTF-8?q?es=E4=B8=8D=E4=B8=BA=E7=A9=BA=EF=BC=8C=E5=88=99=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=89=8D=E7=AB=AF=E4=BC=A0=E9=80=92=E7=9A=84=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmTaskCandidateStartUserSelectStrategy.java | 10 ++++++++-- .../task/BpmProcessInstanceServiceImpl.java | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java index 9fd14d6ded..d547643609 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -53,8 +53,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", execution.getProcessInstanceId()); - // 获得审批人 + // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); + if (CollUtil.isEmpty(assignees)){ + return null; + } return new LinkedHashSet<>(assignees); } @@ -68,8 +71,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand if (startUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } - // 获得审批人 + // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 List assignees = startUserSelectAssignees.get(activityId); + if (CollUtil.isEmpty(assignees)){ + return null; + } return new LinkedHashSet<>(assignees); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 1976e13a8b..27884ae51e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -173,10 +173,19 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); - // 如果流程变量为空,则使用历史流程变量 - if (null == processVariables) { - processVariables = historicProcessInstance.getProcessVariables(); + // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 + Map historicVariables = historicProcessInstance.getProcessVariables(); + if (null != processVariables) { + // 遍历新变量值,仅更新历史变量中存在的键 + for (Map.Entry entry : processVariables.entrySet()) { + String key = entry.getKey(); + if (historicVariables.containsKey(key)) { + // 如果历史变量中存在该键,则用新值覆盖 + historicVariables.put(key, entry.getValue()); + } + } } + processVariables = historicVariables; } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( From 45a72f4cd857397c05da7a3136bb291e564a46b8 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 24 Feb 2025 17:15:17 +0800 Subject: [PATCH 047/116] =?UTF-8?q?fix=EF=BC=9A=E5=AE=A1=E6=89=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=B7=BB=E5=8A=A0=E5=AE=A1=E6=89=B9=E4=BA=BA=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/vo/task/BpmTaskApproveReqVO.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index 40df86efc8..07078b8db3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; +import java.util.List; import java.util.Map; @Schema(description = "管理后台 - 通过流程任务的 Request VO") @@ -23,4 +24,7 @@ public class BpmTaskApproveReqVO { @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) private Map variables; + @Schema(description = "节点审批人", example = "[1,2]") + private List assignees; + } From e11529375ef1e52c548597d12271c7ddb9fb9437 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 24 Feb 2025 22:56:18 +0800 Subject: [PATCH 048/116] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E6=97=B6?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9A=84=E7=B1=BB=E5=9E=8B=E6=98=AF=E5=90=A6=E4=B8=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E5=B7=B2?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E4=BA=86=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C?= =?UTF-8?q?=E5=90=A6=E5=88=99=E7=94=B1=E5=89=8D=E7=AB=AF=E4=BC=A0=E9=80=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/vo/task/BpmTaskApproveReqVO.java | 4 ++-- .../dept/BpmTaskCandidateStartUserSelectStrategy.java | 4 ++-- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index 07078b8db3..b84cb44ca6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -24,7 +24,7 @@ public class BpmTaskApproveReqVO { @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) private Map variables; - @Schema(description = "节点审批人", example = "[1,2]") - private List assignees; + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") + private Map> startUserSelectAssignees; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java index d547643609..07b051cadd 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -56,7 +56,7 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); if (CollUtil.isEmpty(assignees)){ - return null; + return Sets.newLinkedHashSet(); } return new LinkedHashSet<>(assignees); } @@ -74,7 +74,7 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 List assignees = startUserSelectAssignees.get(activityId); if (CollUtil.isEmpty(assignees)){ - return null; + return Sets.newLinkedHashSet(); } return new LinkedHashSet<>(assignees); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 76c7771035..f4d08b59d9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -520,6 +520,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 修改表单的值需要存储到 ProcessInstance 变量 + if (CollUtil.isNotEmpty(reqVO.getStartUserSelectAssignees())) { + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + reqVO.getStartUserSelectAssignees()); + } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { From d0fbb7677cb0d49e06efc10cfadb374ccbd92fc1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 25 Feb 2025 07:56:05 +0800 Subject: [PATCH 049/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E8=A7=A6=E5=8F=91=E5=99=A8=20HTT?= =?UTF-8?q?P=20=E5=BC=82=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 6 +++--- .../enums/definition/BpmTriggerTypeEnum.java | 2 +- .../api/task/BpmProcessInstanceApiImpl.java | 1 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 16 +++++++------- .../task/BpmProcessInstanceController.java | 1 + .../instance/BpmHttpTriggerCallbackReqVO.java | 2 ++ .../core/listener/BpmTaskEventListener.java | 1 + .../flowable/core/util/SimpleModelUtils.java | 21 +++++++++++-------- .../bpm/service/task/BpmTaskService.java | 3 ++- .../bpm/service/task/BpmTaskServiceImpl.java | 2 +- .../{ => form}/BpmFormDeleteTrigger.java | 3 ++- .../{ => form}/BpmFormUpdateTrigger.java | 3 ++- .../BpmAbstractHttpRequestTrigger.java | 7 ++++--- .../BpmAsyncHttpRequestTrigger.java | 5 +++-- .../{ => http}/BpmSyncHttpRequestTrigger.java | 2 +- 15 files changed, 44 insertions(+), 31 deletions(-) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{ => form}/BpmFormDeleteTrigger.java (95%) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{ => form}/BpmFormUpdateTrigger.java (95%) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{ => http}/BpmAbstractHttpRequestTrigger.java (95%) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{ => http}/BpmAsyncHttpRequestTrigger.java (91%) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/{ => http}/BpmSyncHttpRequestTrigger.java (98%) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java index 89311ed75d..e3113b4c81 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.bpm.api.task; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; - import jakarta.validation.Valid; /** @@ -20,12 +19,13 @@ public interface BpmProcessInstanceApi { */ String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO); - + // TODO @jason:新增 BpmProcessTaskApi 接口,这个要不改成 triggerTask,保持通用性(和 flowable 保持一致) /** * 异步 HTTP 请求触发器回调, 为了唤醒流程继续执行 * * @param processInstanceId 流程实例编号 - * @param callbackId 回调编号, 对应 ReceiveTask Id + * @param callbackId 回调编号, 对应 ReceiveTask Id TODO @jason:改成 taskDefineKey */ void asyncHttpTriggerCallback(String processInstanceId, String callbackId); + } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 61dc2a5f71..e738d9436c 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -19,7 +19,7 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), FORM_UPDATE(2, "更新流程表单数据"), FORM_DELETE(3, "删除流程表单数据"), - ASYNC_HTTP_REQUEST(4, "发起异步 HTTP 请求"); + ASYNC_HTTP_REQUEST(4, "发起异步 HTTP 请求"); // TODO @jason:HTTP_REQUEST_ASYNC /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index a639de7e0f..dbb6a264eb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -34,4 +34,5 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { public void asyncHttpTriggerCallback(String processInstanceId, String callbackId) { bpmTaskService.triggerReceiveTask(processInstanceId, callbackId); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 2f36e3eecb..bdebfe64b5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -117,7 +117,8 @@ public class BpmSimpleModelNodeVO { @Schema(description = "路由分支组", example = "[]") private List routerGroups; - @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成,所以 hidden = true + @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true + @JsonIgnore private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE /** @@ -125,12 +126,9 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; - /** - * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 - */ + @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true @JsonIgnore - private String attachNodeId; // 目前用于异步触发器节点。需要 UserTask 和 ReceiveTask(附加节点) 来完成 - + private String attachNodeId; // 目前用于触发器节点(异步)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 /** * 子流程设置 @@ -382,7 +380,6 @@ public class BpmSimpleModelNodeVO { @Valid private List body; - // TODO @json:可能未来看情况,搞个 HttpResponseParam;得看看有没别的业务需要,抽象统一 /** * 请求返回处理设置,用于修改流程表单值 *

@@ -392,10 +389,13 @@ public class BpmSimpleModelNodeVO { @Schema(description = "请求返回处理设置", example = "[]") private List> response; + // TODO @jason:改成 callbackTaskDefineKey /** - * 异步 Http 请求,需要指定回调 Id,用于回调执行. 由后端指定 + * 异步 Http 请求,需要指定回调 ID,用于回调执行 */ + @Schema(description = "回调 ID", example = "xxx", hidden = true) private String callbackId; + } @Schema(description = "流程表单触发器设置", example = "{}") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 5222877753..90ecd14977 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -180,6 +180,7 @@ public class BpmProcessInstanceController { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + // TODO @jason:要不去掉这个接口,单体通过 asyncHttpTriggerCallback? @PostMapping("/http-trigger/callback") @Operation(summary = "异步 HTTP 请求触发器回调") @PermitAll // 允许外部调用,不需要登录。 TODO @芋艿 需要加一下验证签名吗? diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java index 9d60840ba2..2004c31c8a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; +// TODO @jason:要不去掉这个接口,单体通过 asyncHttpTriggerCallback? @Schema(description = "管理后台 - Bpm 异步 Http 触发器请求回调 Request VO") @Data public class BpmHttpTriggerCallbackReqVO { @@ -15,4 +16,5 @@ public class BpmHttpTriggerCallbackReqVO { @Schema(description = "回调编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "dca1cdcc-b8fe-11ef-99b5-01ff4722db8b") @NotEmpty(message = "回调编号不能为空") private String callbackId; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index bb256001b0..a107b42a0c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -112,4 +112,5 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); } } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 19c804a754..f1e1eeb38a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -171,11 +171,12 @@ public class SimpleModelUtils { // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; + // 如果没有附加节点:则直接建立连线 if (StrUtil.isEmpty(node.getAttachNodeId())) { SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); process.addFlowElement(sequenceFlow); } else { - // 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 + // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(异步) List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); sequenceFlows.forEach(process::addFlowElement); } @@ -189,9 +190,9 @@ public class SimpleModelUtils { /** * 构建有附加节点的连线 * - * @param nodeId 当前节点 Id - * @param attachNodeId 附属节点 Id - * @param targetNodeId 目标节点 Id + * @param nodeId 当前节点 ID + * @param attachNodeId 附属节点 ID + * @param targetNodeId 目标节点 ID */ private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); @@ -748,8 +749,10 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { + // TODO @芋艿:【异步】在看看 @Override public List convertList(BpmSimpleModelNodeVO node) { + Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); @@ -757,23 +760,23 @@ public class SimpleModelUtils { serviceTask.setName(node.getName()); serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); - Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); - addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); flowElements.add(serviceTask); + // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + // TODO @jason:这里能挪到最后处理么?这样,代码的整体性更好; if (ASYNC_HTTP_REQUEST.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); String attachNodeId = "Activity_" + IdUtil.fastUUID(); ReceiveTask receiveTask = new ReceiveTask(); receiveTask.setId(attachNodeId); receiveTask.setName("异步 HTTP 请求"); node.setAttachNodeId(attachNodeId); - // 设置 receiveId - Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 http 请求设置不能为空"); - node.getTriggerSetting().getHttpRequestSetting().setCallbackId(attachNodeId); + node.getTriggerSetting().getHttpRequestSetting().setCallbackId(attachNodeId); // 设置 receiveId flowElements.add(receiveTask); } + // TODO @jason:是不是挪到 flowElements.add(serviceTask); 之前哈,因为它在处理 serviceTask if (node.getTriggerSetting().getHttpRequestSetting() != null) { addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 6dce076f87..6ddc93bdb6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -276,8 +276,9 @@ public interface BpmTaskService { */ void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); + // TODO @jason:改成 triggerTask。然后,“触发 ReceiveTask,让流程继续执行”,改成一些调用场景 /** - * 触发 ReceiveTask, 让流程继续执行 + * 触发 ReceiveTask,让流程继续执行 * * @param processInstanceId 流程示例编号 * @param taskDefineKey 任务 Key diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index e91c7e8f35..1a1f93c264 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1329,7 +1329,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { .activityId(taskDefineKey) .singleResult(); if (execution == null) { - log.error("[processDelayTimerTimeout][processInstanceId({}) activityId({}) 没有找到执行活动]", + log.error("[triggerReceiveTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); return; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java similarity index 95% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java index ab29c502d5..c441c5f9b6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; +package cn.iocoder.yudao.module.bpm.service.task.trigger.form; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; import com.fasterxml.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java similarity index 95% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java index 9ec2608b93..4940d697d4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; +package cn.iocoder.yudao.module.bpm.service.task.trigger.form; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; import com.fasterxml.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java similarity index 95% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java index cbe1d5aea1..0e5e7276f7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAbstractHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java @@ -1,7 +1,8 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; +package cn.iocoder.yudao.module.bpm.service.task.trigger.http; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; @@ -39,8 +40,7 @@ public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { HttpEntity> requestEntity = new HttpEntity<>(body, headers); ResponseEntity responseEntity; try { - responseEntity = restTemplate.exchange(url, HttpMethod.POST, - requestEntity, String.class); + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); } catch (RestClientException e) { log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); @@ -66,4 +66,5 @@ public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { body.add("processInstanceId", processInstance.getId()); return body; } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java similarity index 91% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java index 189cc401af..83c75babc5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; +package cn.iocoder.yudao.module.bpm.service.task.trigger.http; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; @@ -42,7 +42,8 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - body.add("callbackId", setting.getCallbackId()); // 异步请求 callbackId 需要传给被调用方. 用于回调执行 + // TODO @芋艿:【异步】在看看 + body.add("callbackId", setting.getCallbackId()); // 异步请求 callbackId 需要传给被调用方,用于回调执行 // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java similarity index 98% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index e01ebc3c6c..4a506f3b13 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; +package cn.iocoder.yudao.module.bpm.service.task.trigger.http; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; From 48c976cf0a72ff6b1ecc685c6bd13ce2fceb6045 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 25 Feb 2025 12:41:30 +0800 Subject: [PATCH 050/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=AD=90=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E8=B6=85=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/listener/BpmTaskEventListener.java | 1 + .../flowable/core/util/SimpleModelUtils.java | 3 +++ .../task/BpmProcessInstanceServiceImpl.java | 2 +- .../bpm/service/task/BpmTaskService.java | 16 +++++++------- .../bpm/service/task/BpmTaskServiceImpl.java | 21 +++++++++---------- .../listener/BpmCallActivityListener.java | 2 +- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index 5ef0b74c3a..b39a0bd623 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -110,6 +110,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { String taskKey = boundaryEvent.getAttachedToRefId(); taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); + // 2.3 子流程超时处理 } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) { String taskKey = boundaryEvent.getAttachedToRefId(); taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 111ea748b1..898fe55831 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -416,6 +416,7 @@ public class SimpleModelUtils { private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { // 1.1 定时器边界事件 + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId("Event-" + IdUtil.fastUUID()); boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 @@ -723,6 +724,7 @@ public class SimpleModelUtils { // 2. 添加接收任务的 Timer Boundary Event if (node.getDelaySetting() != null) { // 2.1 定时器边界事件 + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId("Event-" + IdUtil.fastUUID()); boundaryEvent.setCancelActivity(false); @@ -872,6 +874,7 @@ public class SimpleModelUtils { // 7. 超时设置 if (childProcessSetting.getTimeoutSetting() != null) { + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId("Event-" + IdUtil.fastUUID()); boundaryEvent.setCancelActivity(false); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index b5c9709bd1..ed128f186e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错 + // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错;TODO @lesan:写个注释??? if (task == null) { continue; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index d2f9ddec1d..0e493f937e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -276,6 +276,14 @@ public interface BpmTaskService { */ void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); + /** + * 处理 ChildProcess 子流程的审批超时事件 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void processChildProcessTimeout(String processInstanceId, String taskDefineKey); + // TODO @jason:改成 triggerTask。然后,“触发 ReceiveTask,让流程继续执行”,改成一些调用场景 /** * 触发 ReceiveTask,让流程继续执行 @@ -285,12 +293,4 @@ public interface BpmTaskService { */ void triggerReceiveTask(String processInstanceId, String taskDefineKey); - /** - * 处理 子流程 审批超时事件 - * - * @param processInstanceId 流程示例编号 - * @param taskDefineKey 任务 Key - */ - void processChildProcessTimeout(String processInstanceId, String taskDefineKey); - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 7195fcddad..dc16b0d5f1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1323,6 +1323,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { })); } + @Override + @Transactional(rollbackFor = Exception.class) + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { + List activityInstances = runtimeService.createActivityInstanceQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey).list(); + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), + () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()))); + } + @Override public void triggerReceiveTask(String processInstanceId, String taskDefineKey) { Execution execution = runtimeService.createExecutionQuery() @@ -1340,17 +1350,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { () -> runtimeService.trigger(execution.getId())); } - @Override - @Transactional(rollbackFor = Exception.class) - public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { - List activityInstances = runtimeService.createActivityInstanceQuery() - .processInstanceId(processInstanceId) - .activityId(taskDefineKey).list(); - activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), () -> { - moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()); - })); - } - /** * 获得自身的代理对象,解决 AOP 生效问题 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index 1c2d980380..b454d33335 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -82,7 +82,7 @@ public class BpmCallActivityListener implements ExecutionListener { try { FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); } catch (Exception e) { - log.error("[error][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", + log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", DELEGATE_EXPRESSION, formFieldValue); FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); } From 197c4ad9bf159f6f3f08bf07cbdd14001f1e8b16 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 25 Feb 2025 13:17:27 +0800 Subject: [PATCH 051/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91INFRA=EF=BC=9A=E9=94=99=E8=AF=AF=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E3=80=81=E8=AE=BF=E9=97=AE=E6=97=A5=E5=BF=97=EF=BC=8C?= =?UTF-8?q?params=20=E8=BF=87=E9=95=BF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/service/logger/ApiAccessLogServiceImpl.java | 6 +++--- .../module/infra/service/logger/ApiErrorLogServiceImpl.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java index 9483e8f738..4433fc2a75 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.infra.service.logger; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; @@ -35,8 +35,8 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService { @Override public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) { ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class); - apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); - apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH)); + apiAccessLog.setRequestParams(StrUtils.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); + apiAccessLog.setResultMsg(StrUtils.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH)); if (TenantContextHolder.getTenantId() != null) { apiAccessLogMapper.insert(apiAccessLog); } else { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java index 747b220b5c..1fcbc8dd78 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.infra.service.logger; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; @@ -39,7 +39,7 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService { public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) { ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class) .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); - apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); + apiErrorLog.setRequestParams(StrUtils.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); if (TenantContextHolder.getTenantId() != null) { apiErrorLogMapper.insert(apiErrorLog); } else { From f2909d6bb6bb1928bcc3d20f6e75a14844b61ef2 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 25 Feb 2025 16:27:05 +0800 Subject: [PATCH 052/116] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=87=AA=E9=80=89=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=E5=8F=82=E6=95=B0=EF=BC=8C=E7=94=B1=E5=89=8D=E7=AB=AF=E4=BC=A0?= =?UTF-8?q?=E9=80=92=E4=B8=8B=E4=B8=AA=E8=8A=82=E7=82=B9=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f4d08b59d9..f2bd3ff993 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -519,10 +519,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); - // 修改表单的值需要存储到 ProcessInstance 变量 + // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getStartUserSelectAssignees())) { - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, - reqVO.getStartUserSelectAssignees()); + // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); + startUserSelectAssignees.putAll(reqVO.getStartUserSelectAssignees()); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); From b2ca263067661919b0eeec181058b228bfd45593 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 25 Feb 2025 16:29:35 +0800 Subject: [PATCH 053/116] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateStartUserSelectStrategy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java index 07b051cadd..f4efa549e5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -53,7 +53,7 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", execution.getProcessInstanceId()); - // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 + // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); if (CollUtil.isEmpty(assignees)){ return Sets.newLinkedHashSet(); @@ -71,7 +71,7 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand if (startUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } - // 获得审批人,如果不存在,则直接返回空,避免类型转换异常 + // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 List assignees = startUserSelectAssignees.get(activityId); if (CollUtil.isEmpty(assignees)){ return Sets.newLinkedHashSet(); From 5e31062d6b64ff7c007f332040c4a805a5441410 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Tue, 25 Feb 2025 22:23:12 +0800 Subject: [PATCH 054/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9=E3=80=91=20=E5=BC=82=E6=AD=A5=20htt?= =?UTF-8?q?p=20=E8=A7=A6=E5=8F=91=E5=99=A8=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 9 +----- .../bpm/api/task/BpmProcessTaskApi.java | 21 +++++++++++++ .../enums/definition/BpmTriggerTypeEnum.java | 2 +- .../api/task/BpmProcessInstanceApiImpl.java | 15 ++-------- .../bpm/api/task/BpmProcessTaskApiImpl.java | 24 +++++++++++++++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 8 ++--- .../task/BpmProcessInstanceController.java | 11 ------- .../instance/BpmHttpTriggerCallbackReqVO.java | 20 ------------- .../core/listener/BpmTaskEventListener.java | 2 +- .../flowable/core/util/SimpleModelUtils.java | 30 ++++++++----------- .../bpm/service/task/BpmTaskService.java | 10 ++++--- .../bpm/service/task/BpmTaskServiceImpl.java | 2 +- .../http/BpmAsyncHttpRequestTrigger.java | 4 +-- 13 files changed, 75 insertions(+), 83 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java delete mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java index e3113b4c81..b8ccad64b1 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java @@ -19,13 +19,6 @@ public interface BpmProcessInstanceApi { */ String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO); - // TODO @jason:新增 BpmProcessTaskApi 接口,这个要不改成 triggerTask,保持通用性(和 flowable 保持一致) - /** - * 异步 HTTP 请求触发器回调, 为了唤醒流程继续执行 - * - * @param processInstanceId 流程实例编号 - * @param callbackId 回调编号, 对应 ReceiveTask Id TODO @jason:改成 taskDefineKey - */ - void asyncHttpTriggerCallback(String processInstanceId, String callbackId); + } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java new file mode 100644 index 0000000000..a17ab46583 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.bpm.api.task; + +import jakarta.validation.constraints.NotEmpty; + +/** + * 流程任务 Api 接口 + * + * @author jason + */ +public interface BpmProcessTaskApi { + + + /** + * 触发流程任务的执行 + * + * @param processInstanceId 流程实例编号 + * @param taskDefineKey 任务 Key + */ + void triggerTask(@NotEmpty(message = "流程实例的编号不能为空") String processInstanceId, + @NotEmpty(message = "任务 Key 不能为空") String taskDefineKey); +} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index e738d9436c..c17780e1bc 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -19,7 +19,7 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), FORM_UPDATE(2, "更新流程表单数据"), FORM_DELETE(3, "删除流程表单数据"), - ASYNC_HTTP_REQUEST(4, "发起异步 HTTP 请求"); // TODO @jason:HTTP_REQUEST_ASYNC + HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index dbb6a264eb..aa9b15cb6d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.bpm.api.task; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; /** * Flowable 流程实例 Api 实现类 @@ -22,17 +20,8 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource private BpmProcessInstanceService processInstanceService; - @Resource - private BpmTaskService bpmTaskService; - @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return processInstanceService.createProcessInstance(userId, reqDTO); } - - @Override - public void asyncHttpTriggerCallback(String processInstanceId, String callbackId) { - bpmTaskService.triggerReceiveTask(processInstanceId, callbackId); - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java new file mode 100644 index 0000000000..7e314ebfb9 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.bpm.api.task; + +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 流程任务 Api 实现类 + * + * @author jason + */ +@Service +@Validated +public class BpmProcessTaskApiImpl implements BpmProcessTaskApi { + + @Resource + private BpmTaskService bpmTaskService; + + @Override + public void triggerTask(String processInstanceId, String taskDefineKey) { + bpmTaskService.triggerTask(processInstanceId, taskDefineKey); + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index bdebfe64b5..b5e978f2b6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -389,13 +389,11 @@ public class BpmSimpleModelNodeVO { @Schema(description = "请求返回处理设置", example = "[]") private List> response; - // TODO @jason:改成 callbackTaskDefineKey /** - * 异步 Http 请求,需要指定回调 ID,用于回调执行 + * 异步 Http 请求,需要指定回调任务 Key,用于回调执行 */ - @Schema(description = "回调 ID", example = "xxx", hidden = true) - private String callbackId; - + @Schema(description = "回调任务 Key", example = "xxx", hidden = true) + private String callbackTaskDefineKey; } @Schema(description = "流程表单触发器设置", example = "{}") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 90ecd14977..def95cca63 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -22,7 +22,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; @@ -179,14 +178,4 @@ public class BpmProcessInstanceController { public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } - - // TODO @jason:要不去掉这个接口,单体通过 asyncHttpTriggerCallback? - @PostMapping("/http-trigger/callback") - @Operation(summary = "异步 HTTP 请求触发器回调") - @PermitAll // 允许外部调用,不需要登录。 TODO @芋艿 需要加一下验证签名吗? - public CommonResult httpTriggerCallback(@Valid @RequestBody BpmHttpTriggerCallbackReqVO reqVO) { - taskService.triggerReceiveTask(reqVO.getId(), reqVO.getCallbackId()); - return success(true); - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java deleted file mode 100644 index 2004c31c8a..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmHttpTriggerCallbackReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -// TODO @jason:要不去掉这个接口,单体通过 asyncHttpTriggerCallback? -@Schema(description = "管理后台 - Bpm 异步 Http 触发器请求回调 Request VO") -@Data -public class BpmHttpTriggerCallbackReqVO { - - @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "dca1cdcc-b8fe-11ef-99b5-00ff4722db8b") - @NotEmpty(message = "流程实例的编号不能为空") - private String id; - - @Schema(description = "回调编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "dca1cdcc-b8fe-11ef-99b5-01ff4722db8b") - @NotEmpty(message = "回调编号不能为空") - private String callbackId; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index a107b42a0c..8cddaf262a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -109,7 +109,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { // 2.2 延迟器超时处理 } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { String taskKey = boundaryEvent.getAttachedToRefId(); - taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); + taskService.triggerTask(event.getProcessInstanceId(), taskKey); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index f1e1eeb38a..0194bea678 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -26,7 +26,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.ASYNC_HTTP_REQUEST; +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static java.util.Arrays.asList; @@ -754,6 +754,17 @@ public class SimpleModelUtils { public List convertList(BpmSimpleModelNodeVO node) { Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); + // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (HTTP_REQUEST_ASYNC.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); + String attachNodeId = "Activity_" + IdUtil.fastUUID(); + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId(attachNodeId); + receiveTask.setName("异步 HTTP 请求"); + node.setAttachNodeId(attachNodeId); + node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(attachNodeId); // 设置 callbackTaskDefineKey + flowElements.add(receiveTask); + } // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); @@ -761,28 +772,13 @@ public class SimpleModelUtils { serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); - flowElements.add(serviceTask); - - // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - // TODO @jason:这里能挪到最后处理么?这样,代码的整体性更好; - if (ASYNC_HTTP_REQUEST.getType().equals(node.getTriggerSetting().getType())) { - Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); - String attachNodeId = "Activity_" + IdUtil.fastUUID(); - ReceiveTask receiveTask = new ReceiveTask(); - receiveTask.setId(attachNodeId); - receiveTask.setName("异步 HTTP 请求"); - node.setAttachNodeId(attachNodeId); - node.getTriggerSetting().getHttpRequestSetting().setCallbackId(attachNodeId); // 设置 receiveId - flowElements.add(receiveTask); - } - - // TODO @jason:是不是挪到 flowElements.add(serviceTask); 之前哈,因为它在处理 serviceTask if (node.getTriggerSetting().getHttpRequestSetting() != null) { addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); } if (node.getTriggerSetting().getFormSettings() != null) { addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); } + flowElements.add(serviceTask); return flowElements; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 6ddc93bdb6..69e95fddd2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -276,13 +276,15 @@ public interface BpmTaskService { */ void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); - // TODO @jason:改成 triggerTask。然后,“触发 ReceiveTask,让流程继续执行”,改成一些调用场景 + /** - * 触发 ReceiveTask,让流程继续执行 + * 触发流程任务 (ReceiveTask) 的执行 + *

+ * 1. Simple 模型异步 HTTP 请求触发器节点的回调,触发流程继续执行 + * 2. Simple 模型延迟器节点,到时触发流程继续执行 * * @param processInstanceId 流程示例编号 * @param taskDefineKey 任务 Key */ - void triggerReceiveTask(String processInstanceId, String taskDefineKey); - + void triggerTask(String processInstanceId, String taskDefineKey); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 1a1f93c264..db739987b8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1323,7 +1323,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public void triggerReceiveTask(String processInstanceId, String taskDefineKey) { + public void triggerTask(String processInstanceId, String taskDefineKey) { Execution execution = runtimeService.createExecutionQuery() .processInstanceId(processInstanceId) .activityId(taskDefineKey) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java index 83c75babc5..0949c4c8fe 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java @@ -24,7 +24,7 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { @Override public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.ASYNC_HTTP_REQUEST; + return BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; } @Override @@ -43,7 +43,7 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); // TODO @芋艿:【异步】在看看 - body.add("callbackId", setting.getCallbackId()); // 异步请求 callbackId 需要传给被调用方,用于回调执行 + body.add("callbackId", setting.getCallbackTaskDefineKey()); // 异步请求 callbackId 需要传给被调用方,用于回调执行 // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); From 0d886dc42b14e266b4ec397709302e21055e3e29 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 26 Feb 2025 08:29:09 +0800 Subject: [PATCH 055/116] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ed128f186e..c3566c8e95 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错;TODO @lesan:写个注释??? + // ChildProcess 子流程节点仅存在于 activity 中,并且没有自身的 task ,需要跳过执行 if (task == null) { continue; } From 9c9f2812e9dc95095be0dc5bece7ad9417b1310c Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 26 Feb 2025 08:59:54 +0800 Subject: [PATCH 056/116] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0buildTimeoutBo?= =?UTF-8?q?undaryEvent=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/SimpleModelUtils.java | 92 ++++++++++--------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index def9e8aac5..b48e3eb87d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.B import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; @@ -84,7 +83,7 @@ public class SimpleModelUtils { traverseNodeToBuildFlowNode(startNode, process); // 3. 构建并添加节点之间的连线 Sequence Flow - EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); + EndEvent endEvent = getEndEvent(bpmnModel); traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); // 4. 自动布局 @@ -364,7 +363,7 @@ public class SimpleModelUtils { userTask.setName(node.getName()); // 人工审批 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); // 候选人策略为发起人自己 addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask); // 添加表单字段权限属性元素 @@ -415,25 +414,17 @@ public class SimpleModelUtils { */ private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { - // 1.1 定时器边界事件 - // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? - BoundaryEvent boundaryEvent = new BoundaryEvent(); - boundaryEvent.setId("Event-" + IdUtil.fastUUID()); - boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 - boundaryEvent.setAttachedToRef(userTask); - // 1.2 定义超时时间、最大提醒次数 - TimerEventDefinition eventDefinition = new TimerEventDefinition(); - eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); + // 1. 创建 Timeout Boundary Event + String timeCycle = null; if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) && timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { - eventDefinition.setTimeCycle(String.format("R%d/%s", - timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); + timeCycle = String.format("R%d/%s", + timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()); } - boundaryEvent.addEventDefinition(eventDefinition); + BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(), + timeoutHandler.getTimeDuration(), timeCycle, null); - // 2.1 添加定时器边界事件类型 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType()); - // 2.2 添加超时执行动作元素 + // 2 添加超时执行动作元素 addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType()); return boundaryEvent; } @@ -516,7 +507,7 @@ public class SimpleModelUtils { BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum); // 添加审批方式的扩展属性 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, approveMethod); + addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod); if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) { // 随机审批,不需要设置多实例属性 return; @@ -723,21 +714,14 @@ public class SimpleModelUtils { // 2. 添加接收任务的 Timer Boundary Event if (node.getDelaySetting() != null) { - // 2.1 定时器边界事件 - // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? - BoundaryEvent boundaryEvent = new BoundaryEvent(); - boundaryEvent.setId("Event-" + IdUtil.fastUUID()); - boundaryEvent.setCancelActivity(false); - boundaryEvent.setAttachedToRef(receiveTask); - // 2.2 定义超时时间 - TimerEventDefinition eventDefinition = new TimerEventDefinition(); + BoundaryEvent boundaryEvent = null; if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { - eventDefinition.setTimeDuration(node.getDelaySetting().getDelayTime()); + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + node.getDelaySetting().getDelayTime(), null, null); } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { - eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime()); + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + null, null, node.getDelaySetting().getDelayTime()); } - boundaryEvent.addEventDefinition(eventDefinition); - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType()); flowElements.add(boundaryEvent); } return flowElements; @@ -870,19 +854,14 @@ public class SimpleModelUtils { // 7. 超时设置 if (childProcessSetting.getTimeoutSetting() != null) { - // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么? - BoundaryEvent boundaryEvent = new BoundaryEvent(); - boundaryEvent.setId("Event-" + IdUtil.fastUUID()); - boundaryEvent.setCancelActivity(false); - boundaryEvent.setAttachedToRef(callActivity); - TimerEventDefinition eventDefinition = new TimerEventDefinition(); - if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { - eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression()); - } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { - eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression()); + BoundaryEvent boundaryEvent = null; + if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + node.getDelaySetting().getDelayTime(), null, null); + } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(), + null, null, node.getDelaySetting().getDelayTime()); } - boundaryEvent.addEventDefinition(eventDefinition); - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType()); flowElements.add(boundaryEvent); } @@ -903,6 +882,33 @@ public class SimpleModelUtils { return id + "_join"; } + private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, + String timeDuration, + String timeCycle, + String timeDate) { + // 1.1 定时器边界事件 + BoundaryEvent boundaryEvent = new BoundaryEvent(); + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); + boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 + boundaryEvent.setAttachedToRef(attachedToRef); + // 1.2 定义超时时间表达式 + TimerEventDefinition eventDefinition = new TimerEventDefinition(); + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeDuration(timeDuration); + } + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeCycle(timeCycle); + } + if (ObjUtil.isNotNull(timeDate)) { + eventDefinition.setTimeDate(timeDate); + } + boundaryEvent.addEventDefinition(eventDefinition); + + // 2.1 添加定时器边界事件类型 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); + return boundaryEvent; + } + // ========== SIMPLE 流程预测相关的方法 ========== public static List simulateProcess(BpmSimpleModelNodeVO rootNode, Map variables) { From 8a9d64bbaf7948e099ae52c7d7d8ae18397beb31 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 26 Feb 2025 15:18:34 +0800 Subject: [PATCH 057/116] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ildProcessMultiInstanceSourceTypeEnum.java | 36 +++++++++++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 31 ++++++++++ .../BpmParallelMultiInstanceBehavior.java | 61 +++++++++++++------ .../BpmSequentialMultiInstanceBehavior.java | 59 ++++++++++++------ .../core/enums/BpmnModelConstants.java | 5 ++ .../flowable/core/util/BpmnModelUtils.java | 11 ++++ .../flowable/core/util/SimpleModelUtils.java | 27 ++++++-- .../listener/BpmCallActivityListener.java | 20 +++--- 8 files changed, 198 insertions(+), 52 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java new file mode 100644 index 0000000000..f3cac8ba92 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程多实例来源类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { + + FIXED_QUANTITY(1, "固定数量"), + DIGITAL_FORM(2, "数字表单"), + MULTI_FORM(3, "多项表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 76a44fe3e1..c0d2a06672 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -452,6 +452,9 @@ public class BpmSimpleModelNodeVO { @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") private TimeoutSetting timeoutSetting; + @Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private MultiInstanceSetting multiInstanceSetting; + @Schema(description = "子流程发起人配置") @Data @Valid @@ -490,5 +493,33 @@ public class BpmSimpleModelNodeVO { } + @Schema(description = "多实例设置") + @Data + @Valid + public static class MultiInstanceSetting { + + @Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启多实例不能为空") + private Boolean enable; + + @Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否串行不能为空") + private Boolean sequential; + + @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "完成比例不能为空") + private Integer completeRatio; + + @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源类型不能为空") + @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class) + private Integer sourceType; + + @Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源不能为空") + private String source; + + } + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index d239dbe3ff..84c89fc97b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -1,15 +1,21 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import lombok.Setter; import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import java.util.List; import java.util.Set; /** @@ -42,27 +48,42 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { - // 第一步,设置 collectionVariable 和 CollectionVariable - // 从 execution.getVariable() 读取所有任务处理人的 key - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); - // 从 execution.getVariable() 读取当前所有任务处理的人的 key - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); - // 第二步,获取任务的所有处理人 - @SuppressWarnings("unchecked") - Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); - if (assigneeUserIds == null) { - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(assigneeUserIds)) { - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 - assigneeUserIds = SetUtils.asSet((Long) null); + // 第二步,获取任务的所有处理人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); } - execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + return assigneeUserIds.size(); } - return assigneeUserIds.size(); + + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index b3a3a24f80..a3ba19c70e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -2,14 +2,20 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import lombok.Setter; import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import java.util.List; import java.util.Set; /** @@ -35,28 +41,43 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { - // 第一步,设置 collectionVariable 和 CollectionVariable - // 从 execution.getVariable() 读取所有任务处理人的 key - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); - // 从 execution.getVariable() 读取当前所有任务处理的人的 key - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); - // 第二步,获取任务的所有处理人 - // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人 - @SuppressWarnings("unchecked") - Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); - if (assigneeUserIds == null) { - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(assigneeUserIds)) { - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 - assigneeUserIds = SetUtils.asSet((Long) null); + // 第二步,获取任务的所有处理人 + // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); } - execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + return assigneeUserIds.size(); } - return assigneeUserIds.size(); + + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java index cb857b7390..e416428c4e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -68,6 +68,11 @@ public interface BpmnModelConstants { */ String USER_TASK_APPROVE_METHOD = "approveMethod"; + /** + * BPMN Child Process 的扩展属性,用于标记多实例来源类型 + */ + String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = "childProcessMultiInstanceSourceType"; + /** * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 27d923bdeb..83b9b44662 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -158,6 +158,17 @@ public class BpmnModelUtils { return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); } + /** + * 解析子流程多实例来源类型 + * + * @see BpmChildProcessMultiInstanceSourceTypeEnum + * @param element 任务节点 + * @return 多实例来源类型 + */ + public static Integer parseMultiInstanceSourceType(FlowElement element) { + return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE)); + } + /** * 添加任务拒绝处理元素 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index b48e3eb87d..9a32f4cf1f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -853,18 +853,35 @@ public class SimpleModelUtils { callActivity.setExecutionListeners(executionListeners); // 7. 超时设置 - if (childProcessSetting.getTimeoutSetting() != null) { + if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) { BoundaryEvent boundaryEvent = null; - if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), - node.getDelaySetting().getDelayTime(), null, null); - } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null); + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(), - null, null, node.getDelaySetting().getDelayTime()); + null, null, childProcessSetting.getTimeoutSetting().getTimeExpression()); } flowElements.add(boundaryEvent); } + // 8. 多实例 + if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) { + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); + multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential()); + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { + multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); + } + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType()) || + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); + } + multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D))); + callActivity.setLoopCharacteristics(multiInstanceCharacteristics); + addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); + } + // 添加节点类型 addNodeType(node.getType(), callActivity); flowElements.add(callActivity); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index b454d33335..40313a9663 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; @@ -9,13 +10,14 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmpt import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.impl.el.FixedValue; -import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import java.util.List; @@ -37,30 +39,32 @@ public class BpmCallActivityListener implements ExecutionListener { @Resource private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Override public void notify(DelegateExecution execution) { String expressionText = listenerConfig.getExpressionText(); Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId()); // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 if (startUserSetting == null || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { - ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); return; } // 2. 当发起人来源为表单时 if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { - ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - String formFieldValue = parent.getVariable(startUserSetting.getFormField(), String.class); + String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField()); // 2.1 当表单值为空时 if (StrUtil.isEmpty(formFieldValue)) { // 2.1.1 来自主流程发起人 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); return; } // 2.1.2 来自子流程管理员 @@ -72,7 +76,7 @@ public class BpmCallActivityListener implements ExecutionListener { } // 2.1.3 来自主流程管理员 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; @@ -84,7 +88,7 @@ public class BpmCallActivityListener implements ExecutionListener { } catch (Exception e) { log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", DELEGATE_EXPRESSION, formFieldValue); - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); } } } From 37b2fd4789e50c646a3839fd31231d39d131eec4 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 26 Feb 2025 15:59:42 +0800 Subject: [PATCH 058/116] =?UTF-8?q?feat=EF=BC=9A=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/vo/task/BpmTaskApproveReqVO.java | 4 ++-- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index b84cb44ca6..a0751c12e6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -24,7 +24,7 @@ public class BpmTaskApproveReqVO { @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) private Map variables; - @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") - private Map> startUserSelectAssignees; + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") + private Map> nextAssignees; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f2bd3ff993..1866ed006f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -520,11 +520,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 下个节点审批人如果不存在,则由前端传递 - if (CollUtil.isNotEmpty(reqVO.getStartUserSelectAssignees())) { + if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); - startUserSelectAssignees.putAll(reqVO.getStartUserSelectAssignees()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); + hisProcessVariables.putAll(reqVO.getNextAssignees()); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); From 13c2d36eeea6455d65c4b1262fccfdf816e0558c Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 26 Feb 2025 17:45:35 +0800 Subject: [PATCH 059/116] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=A1=E6=89=B9=E6=97=B6=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=8C=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=A1=E6=89=B9=E7=9A=84=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../task/BpmProcessInstanceServiceImpl.java | 9 +---- .../bpm/service/task/BpmTaskServiceImpl.java | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fbc7ba223..e089f33751 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -57,6 +57,7 @@ public interface ErrorCodeConstants { ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); + ErrorCode TASK_START_USER_SELECT_NODE_NOT_EXISTS = new ErrorCode(1_009_004_007, "({})不是下一个执行的流程节点!"); // ========== 动态表单模块 1-009-010-000 ========== ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 27884ae51e..3bd5c1098f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -176,14 +176,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 Map historicVariables = historicProcessInstance.getProcessVariables(); if (null != processVariables) { - // 遍历新变量值,仅更新历史变量中存在的键 - for (Map.Entry entry : processVariables.entrySet()) { - String key = entry.getKey(); - if (historicVariables.containsKey(key)) { - // 如果历史变量中存在该键,则用新值覆盖 - historicVariables.put(key, entry.getValue()); - } - } + historicVariables.putAll(processVariables); } processVariables = historicVariables; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 1866ed006f..44c04fb030 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -10,15 +10,19 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -56,6 +60,7 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -519,6 +524,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); + // 校验传递的参数中是否存在不是下一个执行的节点 + checkNextActivityNodes(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -536,6 +543,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { handleParentTaskIfSign(task.getParentTaskId()); } + /** + * 校验传递的参数中是否存在不是下一个执行的节点 + * + * @param loginUserId 流程发起人 + * @param processInstanceId 流程实例id + * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} + */ + private void checkNextActivityNodes(Long loginUserId, Map variables,String processInstanceId, + Map> nextActivityNodes){ + // 1、查询流程【预测】的全部信息 + BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, + new BpmApprovalDetailReqVO().setProcessVariables(variables).setProcessInstanceId(processInstanceId)); + // 2、获取预测节点的信息 + List activityNodes = approvalDetail.getActivityNodes(); + if (CollUtil.isNotEmpty(activityNodes)) { + // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 + List notStartActivityNodes = activityNodes.stream().filter(node -> + BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) + && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); + // 3、校验传递的参数中是否存在不是下一节点的信息 + for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { + if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { + log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); + throw exception(TASK_START_USER_SELECT_NODE_NOT_EXISTS, nextActivityNode.getKey()); + } + } + } + } + /** * 审批通过存在“后加签”的任务。 *

From 3c0b9262d7498891d8934d1c347219661936f597 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Feb 2025 21:34:52 +0800 Subject: [PATCH 060/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E8=A7=A6=E5=8F=91=E5=99=A8=20HTT?= =?UTF-8?q?P=20=E5=BC=82=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/api/task/BpmProcessTaskApi.java | 2 +- .../module/bpm/enums/definition/BpmTriggerTypeEnum.java | 2 +- .../module/bpm/api/task/BpmProcessInstanceApiImpl.java | 1 + .../yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java | 1 + .../definition/vo/model/simple/BpmSimpleModelNodeVO.java | 1 + .../framework/flowable/core/util/SimpleModelUtils.java | 9 +++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 3 +-- .../task/trigger/http/BpmAsyncHttpRequestTrigger.java | 1 + 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java index a17ab46583..8f6f4258c8 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java @@ -9,7 +9,6 @@ import jakarta.validation.constraints.NotEmpty; */ public interface BpmProcessTaskApi { - /** * 触发流程任务的执行 * @@ -18,4 +17,5 @@ public interface BpmProcessTaskApi { */ void triggerTask(@NotEmpty(message = "流程实例的编号不能为空") String processInstanceId, @NotEmpty(message = "任务 Key 不能为空") String taskDefineKey); + } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index c17780e1bc..78e7079c86 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -19,7 +19,7 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), FORM_UPDATE(2, "更新流程表单数据"), FORM_DELETE(3, "删除流程表单数据"), - HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); + HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); // TODO @jason:发起 HTTP 回调 /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index aa9b15cb6d..5ebb12f0f5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -24,4 +24,5 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return processInstanceService.createProcessInstance(userId, reqDTO); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java index 7e314ebfb9..d99aa03c6a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java @@ -21,4 +21,5 @@ public class BpmProcessTaskApiImpl implements BpmProcessTaskApi { public void triggerTask(String processInstanceId, String taskDefineKey) { bpmTaskService.triggerTask(processInstanceId, taskDefineKey); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index c0d2a06672..5dab8309d4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -394,6 +394,7 @@ public class BpmSimpleModelNodeVO { */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; + } @Schema(description = "流程表单触发器设置", example = "{}") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 9a32f4cf1f..d505cedf98 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -743,14 +743,15 @@ public class SimpleModelUtils { // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 if (HTTP_REQUEST_ASYNC.getType().equals(node.getTriggerSetting().getType())) { Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); - String attachNodeId = "Activity_" + IdUtil.fastUUID(); ReceiveTask receiveTask = new ReceiveTask(); - receiveTask.setId(attachNodeId); + receiveTask.setId("Activity_" + IdUtil.fastUUID()); receiveTask.setName("异步 HTTP 请求"); - node.setAttachNodeId(attachNodeId); - node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(attachNodeId); // 设置 callbackTaskDefineKey + node.setAttachNodeId(receiveTask.getId()); flowElements.add(receiveTask); + // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 + node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); } + // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 8352ae226c..483741868e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1340,8 +1340,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { .activityId(taskDefineKey) .singleResult(); if (execution == null) { - log.error("[triggerReceiveTask][processInstanceId({}) activityId({}) 没有找到执行活动]", - processInstanceId, taskDefineKey); + log.error("[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); return; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java index 0949c4c8fe..404f7d24d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java @@ -10,6 +10,7 @@ import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; +// TODO @jason:BpmHttpCallbackTrigger /** * BPM 发送异步 HTTP 请求触发器 * From e892dcabac41a8f5dca5d3c6069c306590d458a3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Feb 2025 21:45:47 +0800 Subject: [PATCH 061/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=AD=90=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmChildProcessMultiInstanceSourceTypeEnum.java | 1 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 4 ++-- .../behavior/BpmParallelMultiInstanceBehavior.java | 2 ++ .../behavior/BpmSequentialMultiInstanceBehavior.java | 2 ++ .../flowable/core/util/SimpleModelUtils.java | 11 ++++++----- .../service/task/BpmProcessInstanceServiceImpl.java | 3 ++- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java index f3cac8ba92..8ab275f572 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -33,4 +33,5 @@ public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable< public Integer[] array() { return ARRAYS; } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 5dab8309d4..d0a49c3669 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -394,7 +394,7 @@ public class BpmSimpleModelNodeVO { */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; - + } @Schema(description = "流程表单触发器设置", example = "{}") @@ -509,7 +509,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @NotNull(message = "完成比例不能为空") - private Integer completeRatio; + private Integer completeRatio; // TODO @lesan:approveRatio 要不这个,和上面保持一致? @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "多实例来源类型不能为空") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index 84c89fc97b..e63c68e4e8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -48,6 +48,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 if (execution.getCurrentFlowElement() instanceof UserTask) { // 第一步,设置 collectionVariable 和 CollectionVariable // 从 execution.getVariable() 读取所有任务处理人的 key @@ -72,6 +73,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav return assigneeUserIds.size(); } + // 情况二:CallActivity 节点 if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index a3ba19c70e..35bba43106 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -41,6 +41,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 if (execution.getCurrentFlowElement() instanceof UserTask) { // 第一步,设置 collectionVariable 和 CollectionVariable // 从 execution.getVariable() 读取所有任务处理人的 key @@ -66,6 +67,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB return assigneeUserIds.size(); } + // 情况二:CallActivity 节点 if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d505cedf98..94a4796f42 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -721,6 +721,8 @@ public class SimpleModelUtils { } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), null, null, node.getDelaySetting().getDelayTime()); + } else { + throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); } flowElements.add(boundaryEvent); } @@ -751,7 +753,7 @@ public class SimpleModelUtils { // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); } - + // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); @@ -877,6 +879,7 @@ public class SimpleModelUtils { childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } +// TODO @lesan:String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); @@ -901,9 +904,7 @@ public class SimpleModelUtils { } private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, - String timeDuration, - String timeCycle, - String timeDate) { + String timeDuration, String timeCycle, String timeDate) { // 1.1 定时器边界事件 BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId("Event-" + IdUtil.fastUUID()); @@ -922,7 +923,7 @@ public class SimpleModelUtils { } boundaryEvent.addEventDefinition(eventDefinition); - // 2.1 添加定时器边界事件类型 + // 2. 添加定时器边界事件类型 addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); return boundaryEvent; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index c3566c8e95..0bb8a29d80 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -414,7 +414,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // ChildProcess 子流程节点仅存在于 activity 中,并且没有自身的 task ,需要跳过执行 + // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 + // TODO @芋艿:后续看看怎么优化! if (task == null) { continue; } From 357f4966d30418128ad6aa501bc96bdf45555e42 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 26 Feb 2025 22:38:31 +0800 Subject: [PATCH 062/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=20=E4=BF=AE=E6=94=B9=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E7=A7=B0validateNextAssignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 44c04fb030..df682c09f9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -525,7 +525,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 - checkNextActivityNodes(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); + validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -550,7 +550,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param processInstanceId 流程实例id * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} */ - private void checkNextActivityNodes(Long loginUserId, Map variables,String processInstanceId, + private void validateNextAssignees(Long loginUserId, Map variables,String processInstanceId, Map> nextActivityNodes){ // 1、查询流程【预测】的全部信息 BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, @@ -562,7 +562,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); - // 3、校验传递的参数中是否存在不是下一节点的信息 + // 3、校验传递的参数中是否存在不是下一个节点的信息 for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); From b03025746661e637454ed2a70c94e79e4641b9ae Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 26 Feb 2025 23:24:21 +0800 Subject: [PATCH 063/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=20=E4=BF=AE=E6=94=B9=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E7=A7=B0validateNextAssignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index df682c09f9..236efb4f48 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -561,7 +561,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) - && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); + && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) + && CollUtil.isEmpty(node.getCandidateUsers())).toList(); // 3、校验传递的参数中是否存在不是下一个节点的信息 for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { From 0565db2f2d5b09edc4dcf935db7ec384a5d363c4 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Thu, 27 Feb 2025 06:40:02 +0800 Subject: [PATCH 064/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9=E3=80=91=20=E5=BC=82=E6=AD=A5=20htt?= =?UTF-8?q?p=20=E8=A7=A6=E5=8F=91=E5=99=A8=E4=BF=AE=E6=94=B9=E4=B8=BA=20Ht?= =?UTF-8?q?tp=20=E5=9B=9E=E8=B0=83=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/enums/definition/BpmTriggerTypeEnum.java | 7 ++++--- .../vo/model/simple/BpmSimpleModelNodeVO.java | 4 ++-- .../flowable/core/util/SimpleModelUtils.java | 14 +++++++------- .../module/bpm/service/task/BpmTaskService.java | 3 ++- ...estTrigger.java => BpmHttpCallbackTrigger.java} | 13 ++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/{BpmAsyncHttpRequestTrigger.java => BpmHttpCallbackTrigger.java} (79%) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 78e7079c86..c1aaae3437 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,9 +17,10 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - FORM_UPDATE(2, "更新流程表单数据"), - FORM_DELETE(3, "删除流程表单数据"), - HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); // TODO @jason:发起 HTTP 回调 + HTTP_CALLBACK(2, "发起 HTTP 回调"), + FORM_UPDATE(10, "更新流程表单数据"), + FORM_DELETE(11, "删除流程表单数据"), + ; /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 5dab8309d4..5c3950d19a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -128,7 +128,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true @JsonIgnore - private String attachNodeId; // 目前用于触发器节点(异步)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 + private String attachNodeId; // 目前用于触发器节点(HTTP 回调)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 /** * 子流程设置 @@ -390,7 +390,7 @@ public class BpmSimpleModelNodeVO { private List> response; /** - * 异步 Http 请求,需要指定回调任务 Key,用于回调执行 + * Http 回调请求,需要指定回调任务 Key,用于回调执行 */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d505cedf98..0c22dba334 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -25,7 +25,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_CALLBACK; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static java.util.Arrays.asList; @@ -175,7 +175,7 @@ public class SimpleModelUtils { SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); process.addFlowElement(sequenceFlow); } else { - // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(异步) + // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(HTTP 回调) List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); sequenceFlows.forEach(process::addFlowElement); } @@ -735,17 +735,17 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { - // TODO @芋艿:【异步】在看看 + // TODO @芋艿:【回调】在看看 @Override public List convertList(BpmSimpleModelNodeVO node) { Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); - // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - if (HTTP_REQUEST_ASYNC.getType().equals(node.getTriggerSetting().getType())) { - Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); + // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); ReceiveTask receiveTask = new ReceiveTask(); receiveTask.setId("Activity_" + IdUtil.fastUUID()); - receiveTask.setName("异步 HTTP 请求"); + receiveTask.setName("HTTP 回调"); node.setAttachNodeId(receiveTask.getId()); flowElements.add(receiveTask); // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index c38771df43..a40fadba75 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -287,11 +287,12 @@ public interface BpmTaskService { /** * 触发流程任务 (ReceiveTask) 的执行 *

- * 1. Simple 模型异步 HTTP 请求触发器节点的回调,触发流程继续执行 + * 1. Simple 模型 HTTP 回调请求触发器节点的回调,触发流程继续执行 * 2. Simple 模型延迟器节点,到时触发流程继续执行 * * @param processInstanceId 流程示例编号 * @param taskDefineKey 任务 Key */ void triggerTask(String processInstanceId, String taskDefineKey); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java similarity index 79% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index 404f7d24d7..f081ff82d3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -10,22 +10,21 @@ import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; -// TODO @jason:BpmHttpCallbackTrigger /** - * BPM 发送异步 HTTP 请求触发器 + * BPM HTTP 回调触发器 * * @author jason */ @Component @Slf4j -public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { +public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { @Resource private BpmProcessInstanceService processInstanceService; @Override public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; + return BpmTriggerTypeEnum.HTTP_CALLBACK; } @Override @@ -34,7 +33,7 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); if (setting == null) { - log.error("[execute][流程({}) HTTP 异步触发器请求配置为空]", processInstanceId); + log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } @@ -43,8 +42,8 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - // TODO @芋艿:【异步】在看看 - body.add("callbackId", setting.getCallbackTaskDefineKey()); // 异步请求 callbackId 需要传给被调用方,用于回调执行 + // TODO @芋艿:【回调】在看看 + body.add("callbackId", setting.getCallbackTaskDefineKey()); // 回调请求 callbackId 需要传给被调用方,用于回调执行 // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); From 074146c99133bcbdb5c72a7c8f39767db0d7a2fa Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 27 Feb 2025 09:56:34 +0800 Subject: [PATCH 065/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E8=A7=A6=E5=8F=91=E5=99=A8=20HTT?= =?UTF-8?q?P=20=E5=BC=82=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/definition/BpmTriggerTypeEnum.java | 5 +++-- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 4 +--- .../service/task/trigger/http/BpmHttpCallbackTrigger.java | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index c1aaae3437..13f997c7b4 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -16,8 +16,9 @@ import java.util.Arrays; @AllArgsConstructor public enum BpmTriggerTypeEnum implements ArrayValuable { - HTTP_REQUEST(1, "发起 HTTP 请求"), - HTTP_CALLBACK(2, "发起 HTTP 回调"), + HTTP_REQUEST(1, "发起 HTTP 请求"), // BPM => 业务,流程继续执行,无需等待业务 + HTTP_CALLBACK(2, "接收 HTTP 回调"), // BPM => 业务 => BPM,流程卡主,等待业务回调 + FORM_UPDATE(10, "更新流程表单数据"), FORM_DELETE(11, "删除流程表单数据"), ; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 6cff39f4f6..e0911240fc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -25,7 +25,6 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_CALLBACK; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static java.util.Arrays.asList; @@ -737,13 +736,12 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { - // TODO @芋艿:【回调】在看看 @Override public List convertList(BpmSimpleModelNodeVO node) { Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - if (HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); ReceiveTask receiveTask = new ReceiveTask(); receiveTask.setId("Activity_" + IdUtil.fastUUID()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index f081ff82d3..867af70b9d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -42,8 +42,8 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - // TODO @芋艿:【回调】在看看 - body.add("callbackId", setting.getCallbackTaskDefineKey()); // 回调请求 callbackId 需要传给被调用方,用于回调执行 + // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + body.add("taskDefineKey", setting.getCallbackTaskDefineKey()); // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); From db6d7a74300da30b721b1e2cdaa7de78f2e810af Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 27 Feb 2025 15:17:30 +0800 Subject: [PATCH 066/116] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0TODO?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/framework/flowable/core/util/BpmnModelUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 49ab5bb35b..b73e0cc98d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,6 +778,8 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; + // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题,需要优化, + // TODO 具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); From 6c6992c86a9f64b78e23c622a6f6204cd424e6ef Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 27 Feb 2025 17:28:22 +0800 Subject: [PATCH 067/116] =?UTF-8?q?feat=EF=BC=9ATODO=20List?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 236efb4f48..d64a822545 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -525,6 +525,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 + // 当前执行的流程节点,需根据该节点寻找下一个节点 + String taskDefinitionKey = task.getTaskDefinitionKey(); + List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); + System.out.println(nextFlowNodes); validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { @@ -543,6 +547,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { handleParentTaskIfSign(task.getParentTaskId()); } + /** + * + * @param taskDefinitionKey 当前节点id + * @param bpmnModel bpmnModel + */ + private List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables){ + FlowNode flowElement = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + // 存储下一个执行的节点 + List nextFlowNodes = new ArrayList<>(); + resolveNextNodes(flowElement, bpmnModel, variables, nextFlowNodes); + return nextFlowNodes; + } + + private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + List outgoingFlows = currentNode.getOutgoingFlows(); + for (SequenceFlow sequenceFlow : outgoingFlows) { + // 如果是排他网关,需要根据条件表达式判断 + if (currentNode instanceof ExclusiveGateway) { + String conditionExpression = sequenceFlow.getConditionExpression(); + if (conditionExpression != null && !BpmnModelUtils.evalConditionExpress(variables,conditionExpression)) { + continue; + } + } + FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode targetNode) { + if (targetNode instanceof Gateway) { + // 如果目标节点还是网关,递归处理 + resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); + } else { + nextFlowNodes.add(targetNode); + } + } + } + } + /** * 校验传递的参数中是否存在不是下一个执行的节点 * @@ -559,6 +598,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { List activityNodes = approvalDetail.getActivityNodes(); if (CollUtil.isNotEmpty(activityNodes)) { // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 + // TODO 获取下一个执行节点 List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) From 093e563b80ef8c34ccc6fb88e1d3e54502bd8a70 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Thu, 27 Feb 2025 23:33:16 +0800 Subject: [PATCH 068/116] feat: add TODO --- .../bpm/service/task/BpmTaskServiceImpl.java | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index d64a822545..04842ea51d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -529,7 +529,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { String taskDefinitionKey = task.getTaskDefinitionKey(); List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); System.out.println(nextFlowNodes); - validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); +// validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -548,40 +548,66 @@ public class BpmTaskServiceImpl implements BpmTaskService { } /** - * - * @param taskDefinitionKey 当前节点id - * @param bpmnModel bpmnModel + * 根据当前节点 ID 获取下一个执行的 FlowNode 列表 + * @param taskDefinitionKey 当前节点 ID + * @param bpmnModel BPMN 模型 + * @param variables 流程变量,用于条件判断 + * @return 下一个执行的 FlowNode 列表 */ - private List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables){ - FlowNode flowElement = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); - // 存储下一个执行的节点 + public List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables) { + if (taskDefinitionKey == null || bpmnModel == null) { + throw new IllegalArgumentException("taskDefinitionKey and bpmnModel cannot be null"); + } + FlowNode currentNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + if (currentNode == null) { + throw new IllegalArgumentException("FlowElement with given taskDefinitionKey not found in BpmnModel"); + } List nextFlowNodes = new ArrayList<>(); - resolveNextNodes(flowElement, bpmnModel, variables, nextFlowNodes); + resolveNextNodes(currentNode, bpmnModel, variables, nextFlowNodes); return nextFlowNodes; } + /** + * 递归解析下一个执行节点 + * @param currentNode 当前节点 + * @param bpmnModel BPMN 模型 + * @param variables 流程变量,用于条件判断 + * @param nextFlowNodes 存储下一个执行节点的列表 + */ private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { List outgoingFlows = currentNode.getOutgoingFlows(); for (SequenceFlow sequenceFlow : outgoingFlows) { - // 如果是排他网关,需要根据条件表达式判断 - if (currentNode instanceof ExclusiveGateway) { - String conditionExpression = sequenceFlow.getConditionExpression(); - if (conditionExpression != null && !BpmnModelUtils.evalConditionExpress(variables,conditionExpression)) { - continue; - } + if (!shouldFollowSequenceFlow(currentNode, sequenceFlow, variables)) { + continue; } FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); if (targetElement instanceof FlowNode targetNode) { if (targetNode instanceof Gateway) { - // 如果目标节点还是网关,递归处理 + // 如果目标节点是网关,递归处理 resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); - } else { + }else { nextFlowNodes.add(targetNode); } } } } + /** + * 判断是否应该遵循当前序列流 + * @param currentNode 当前节点 + * @param sequenceFlow 序列流 + * @param variables 流程变量,用于条件判断 + * @return 是否应该遵循该序列流 + */ + private boolean shouldFollowSequenceFlow(FlowNode currentNode, SequenceFlow sequenceFlow, Map variables) { + if (currentNode instanceof ExclusiveGateway) { + String conditionExpression = sequenceFlow.getConditionExpression(); + return conditionExpression == null || BpmnModelUtils.evalConditionExpress(variables, conditionExpression); + } + return true; + } + + /** * 校验传递的参数中是否存在不是下一个执行的节点 * From 8df3a2d9502a9097d0629fa64b1bc26192357066 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 28 Feb 2025 09:29:56 +0800 Subject: [PATCH 069/116] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmChildProcessMultiInstanceSourceTypeEnum.java | 4 ++-- .../definition/vo/model/simple/BpmSimpleModelNodeVO.java | 2 +- .../core/behavior/BpmParallelMultiInstanceBehavior.java | 4 ++-- .../core/behavior/BpmSequentialMultiInstanceBehavior.java | 4 ++-- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 7 +++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java index 8ab275f572..fab0ddfd71 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -17,8 +17,8 @@ import java.util.Arrays; public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { FIXED_QUANTITY(1, "固定数量"), - DIGITAL_FORM(2, "数字表单"), - MULTI_FORM(3, "多项表单"); + NUMBER_FORM(2, "数字表单"), + MULTIPLE_FORM(3, "多选表单"); private final Integer type; private final String name; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index b18ad23561..f002e6894a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -509,7 +509,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @NotNull(message = "完成比例不能为空") - private Integer completeRatio; // TODO @lesan:approveRatio 要不这个,和上面保持一致? + private Integer approveRatio; @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "多实例来源类型不能为空") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index e63c68e4e8..57f4d393f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -77,10 +77,10 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index 35bba43106..cb748182ed 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -71,10 +71,10 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e0911240fc..da17b6d901 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -873,13 +873,12 @@ public class SimpleModelUtils { if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); } - if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType()) || - childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) || + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } -// TODO @lesan:String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", - String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D))); + String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D)))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); } From 5b88d88177abeb846dc5b7210a15626be038c2ba Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 28 Feb 2025 09:35:43 +0800 Subject: [PATCH 070/116] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index da17b6d901..0060d0e9c5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -877,8 +877,8 @@ public class SimpleModelUtils { childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } - multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", - String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D)))); + multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); } From deef88f56fc4dd1235f24a10c11e1991be73a034 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Fri, 28 Feb 2025 17:10:59 +0800 Subject: [PATCH 071/116] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 119 ++++++++++++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 133 ++++++------------ 2 files changed, 161 insertions(+), 91 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index b73e0cc98d..7873681318 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -828,6 +828,125 @@ public class BpmnModelUtils { } } + /** + * 根据当前节点,获取下一个节点 + * + * @param currentElement 当前节点 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + */ + public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, + Map variables){ + // 下一个执行的流程节点集合 + List nextFlowNodes = new ArrayList<>(); + // 当前执行节点的基本属性 + FlowNode currentNode = (FlowNode) currentElement; + // 获取当前节点的关联节点 + List outgoingFlows = currentNode.getOutgoingFlows(); + if (CollUtil.isEmpty(outgoingFlows)){ + log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); + return nextFlowNodes; + } + // 遍历每个出口流 + for (SequenceFlow outgoingFlow : outgoingFlows) { + // 获取目标节点的基本属性 + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); + if (targetElement == null){ + continue; + } + if (targetElement instanceof Gateway gateway) { + // 处理不同类型的网关 + if (gateway instanceof ExclusiveGateway) { + handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof InclusiveGateway) { + handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof ParallelGateway) { + handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); + } + } else { + // 如果不是网关,直接添加到下一个节点列表 + nextFlowNodes.add((FlowNode) targetElement); + } + } + return nextFlowNodes; + } + + /** + * 处理排他网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // TODO @小北: 这里findOne和simulateNextFlowElements中有重复代码,需要优化,@芋道:是否重构?? + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + if (matchSequenceFlow == null) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlow = gateway.getOutgoingFlows().get(0); + } + } + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理包容网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + if (CollUtil.isEmpty(matchSequenceFlows)) { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlows = gateway.getOutgoingFlows(); + } + } + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + /** + * 处理并行网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // 并行网关,遍历所有出口路径,获取目标节点 + gateway.getOutgoingFlows().forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + /** * 计算条件表达式是否为 true 满足条件 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 04842ea51d..1a00f38617 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -522,14 +523,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - if (CollUtil.isNotEmpty(reqVO.getVariables())) { +// if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 - // 当前执行的流程节点,需根据该节点寻找下一个节点 - String taskDefinitionKey = task.getTaskDefinitionKey(); - List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); - System.out.println(nextFlowNodes); -// validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); + validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -539,104 +536,58 @@ public class BpmTaskServiceImpl implements BpmTaskService { } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); - } else { - taskService.complete(task.getId()); - } +// } else { +// taskService.complete(task.getId()); +// } // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); } - /** - * 根据当前节点 ID 获取下一个执行的 FlowNode 列表 - * @param taskDefinitionKey 当前节点 ID - * @param bpmnModel BPMN 模型 - * @param variables 流程变量,用于条件判断 - * @return 下一个执行的 FlowNode 列表 - */ - public List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables) { - if (taskDefinitionKey == null || bpmnModel == null) { - throw new IllegalArgumentException("taskDefinitionKey and bpmnModel cannot be null"); - } - FlowNode currentNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); - if (currentNode == null) { - throw new IllegalArgumentException("FlowElement with given taskDefinitionKey not found in BpmnModel"); - } - List nextFlowNodes = new ArrayList<>(); - resolveNextNodes(currentNode, bpmnModel, variables, nextFlowNodes); - return nextFlowNodes; - } - - /** - * 递归解析下一个执行节点 - * @param currentNode 当前节点 - * @param bpmnModel BPMN 模型 - * @param variables 流程变量,用于条件判断 - * @param nextFlowNodes 存储下一个执行节点的列表 - */ - private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - List outgoingFlows = currentNode.getOutgoingFlows(); - for (SequenceFlow sequenceFlow : outgoingFlows) { - if (!shouldFollowSequenceFlow(currentNode, sequenceFlow, variables)) { - continue; - } - FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode targetNode) { - if (targetNode instanceof Gateway) { - // 如果目标节点是网关,递归处理 - resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); - }else { - nextFlowNodes.add(targetNode); - } - } - } - } - - /** - * 判断是否应该遵循当前序列流 - * @param currentNode 当前节点 - * @param sequenceFlow 序列流 - * @param variables 流程变量,用于条件判断 - * @return 是否应该遵循该序列流 - */ - private boolean shouldFollowSequenceFlow(FlowNode currentNode, SequenceFlow sequenceFlow, Map variables) { - if (currentNode instanceof ExclusiveGateway) { - String conditionExpression = sequenceFlow.getConditionExpression(); - return conditionExpression == null || BpmnModelUtils.evalConditionExpress(variables, conditionExpression); - } - return true; - } - /** * 校验传递的参数中是否存在不是下一个执行的节点 * - * @param loginUserId 流程发起人 - * @param processInstanceId 流程实例id - * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} + * @param taskDefinitionKey 当前任务节点id + * @param variables 流程变量 + * @param bpmnModel 流程模型 + * @param nextActivityNodes 下一个节点审批人集合(参数) */ - private void validateNextAssignees(Long loginUserId, Map variables,String processInstanceId, - Map> nextActivityNodes){ - // 1、查询流程【预测】的全部信息 - BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, - new BpmApprovalDetailReqVO().setProcessVariables(variables).setProcessInstanceId(processInstanceId)); - // 2、获取预测节点的信息 - List activityNodes = approvalDetail.getActivityNodes(); - if (CollUtil.isNotEmpty(activityNodes)) { - // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 - // TODO 获取下一个执行节点 - List notStartActivityNodes = activityNodes.stream().filter(node -> - BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) - && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) - && CollUtil.isEmpty(node.getCandidateUsers())).toList(); - // 3、校验传递的参数中是否存在不是下一个节点的信息 - for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { - if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { - log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); - throw exception(TASK_START_USER_SELECT_NODE_NOT_EXISTS, nextActivityNode.getKey()); + private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + Map> nextActivityNodes,ProcessInstance processInstance){ + + // 1、获取当前任务节点的信息 + FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); + // 2、获取下一个应该执行的节点集合 + List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + // 3、比较前端传递的节点和预测的下一个节点是否匹配,匹配则将该节点设置上审批人 + for (FlowNode nextFlowNode : nextFlowNodes) { + // 获取下一个执行节点的属性 是否为 发起人自选 + Map> extensionElements = nextFlowNode.getExtensionElements(); + List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); + if (CollUtil.isEmpty(elements)){ + continue; + } + // 获取节点中的审批人策略 + Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); + // 获取流程实例中的发起人自选审批人 + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { + // 先判断节点是否存在 + if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + } + // 如果节点存在,则判断节点中的审批人策略是否为 发起人自选 + List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); + // 3.1、如果前端传递的节点为空,则抛出异常 + if (CollUtil.isEmpty(nextAssignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } + } + } /** From ff555b51366c1e0df4a571f59a3cef4f812546fb Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Fri, 28 Feb 2025 22:37:57 +0800 Subject: [PATCH 072/116] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 2 +- .../bpm/service/task/BpmTaskServiceImpl.java | 32 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 7873681318..fff0ac5317 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -880,7 +880,7 @@ public class BpmnModelUtils { * @param nextFlowNodes 下一个执行的流程节点集合 */ private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里findOne和simulateNextFlowElements中有重复代码,需要优化,@芋道:是否重构?? + // TODO @小北: 这里和simulateNextFlowElements中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋道 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 1a00f38617..bcc70da867 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -523,9 +523,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 -// if (CollUtil.isNotEmpty(reqVO.getVariables())) { + if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); - // 校验传递的参数中是否存在不是下一个执行的节点 + // 校验传递的参数中是否为下一个将要执行的任务节点 validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { @@ -536,9 +536,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); -// } else { -// taskService.complete(task.getId()); -// } + } else { + taskService.complete(task.getId()); + } // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); @@ -546,7 +546,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** - * 校验传递的参数中是否存在不是下一个执行的节点 + * 校验传递的参数中是否为下一个将要执行的任务节点 * * @param taskDefinitionKey 当前任务节点id * @param variables 流程变量 @@ -555,39 +555,37 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextActivityNodes,ProcessInstance processInstance){ - // 1、获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2、获取下一个应该执行的节点集合 + // 2、获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3、比较前端传递的节点和预测的下一个节点是否匹配,匹配则将该节点设置上审批人 + // 3、循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 获取下一个执行节点的属性 是否为 发起人自选 + // 3.1、获取下一个将要执行节点的属性(是否为自选审批人等) Map> extensionElements = nextFlowNode.getExtensionElements(); List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); if (CollUtil.isEmpty(elements)){ continue; } - // 获取节点中的审批人策略 + // 3.2、获取节点中的审批人策略 Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 获取流程实例中的发起人自选审批人 + // 3.3、获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); + // 3.4、如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 先判断节点是否存在 + // 先判断前端传递的参数节点节点是否为将要执行的节点 if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果节点存在,则判断节点中的审批人策略是否为 发起人自选 + // 如果节点存在,则获取节点中的审批人 List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); - // 3.1、如果前端传递的节点为空,则抛出异常 + // 如果前端传递的节点为空,则抛出异常 if (CollUtil.isEmpty(nextAssignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } - } - } /** From fedb9242b59b9846951d49e475b718c5a87a406e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 16:34:30 +0800 Subject: [PATCH 073/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 - .../task/vo/task/BpmTaskApproveReqVO.java | 2 +- ...mTaskCandidateStartUserSelectStrategy.java | 44 +++------------- .../flowable/core/util/BpmnModelUtils.java | 40 ++++++++------- .../task/BpmProcessInstanceServiceImpl.java | 4 +- .../bpm/service/task/BpmTaskServiceImpl.java | 51 +++++++++++-------- 6 files changed, 62 insertions(+), 80 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index a1e2d45aa9..3a50bba523 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -58,7 +58,6 @@ public interface ErrorCodeConstants { ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); - ErrorCode TASK_START_USER_SELECT_NODE_NOT_EXISTS = new ErrorCode(1_009_004_007, "({})不是下一个执行的流程节点!"); // ========== 动态表单模块 1-009-010-000 ========== ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index a0751c12e6..0969fda135 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -25,6 +25,6 @@ public class BpmTaskApproveReqVO { private Map variables; @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") - private Map> nextAssignees; + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java index f4efa549e5..9304d288ac 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -2,24 +2,21 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.d import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import com.google.common.collect.Sets; import jakarta.annotation.Resource; import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.ServiceTask; -import org.flowable.bpmn.model.Task; -import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; /** * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 @@ -53,12 +50,9 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", execution.getProcessInstanceId()); - // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 + // 获得审批人 List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); - if (CollUtil.isEmpty(assignees)){ - return Sets.newLinkedHashSet(); - } - return new LinkedHashSet<>(assignees); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); } @Override @@ -71,33 +65,9 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand if (startUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } - // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 + // 获得审批人 List assignees = startUserSelectAssignees.get(activityId); - if (CollUtil.isEmpty(assignees)){ - return Sets.newLinkedHashSet(); - } - return new LinkedHashSet<>(assignees); - } - - /** - * 获得发起人自选审批人或抄送人的 Task 列表 - * - * @param bpmnModel BPMN 模型 - * @return Task 列表 - */ - public static List getStartUserSelectTaskList(BpmnModel bpmnModel) { - if (bpmnModel == null) { - return Collections.emptyList(); - } - List tasks = new ArrayList<>(); - tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class)); - tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, ServiceTask.class)); - if (CollUtil.isEmpty(tasks)) { - return Collections.emptyList(); - } - tasks.removeIf(task -> ObjectUtil.notEqual(BpmnModelUtils.parseCandidateStrategy(task), - BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); - return tasks; + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 31c27c2b2a..1eea893f76 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -809,8 +809,7 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题,需要优化, - // TODO 具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 + // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题。需要优化,具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -866,27 +865,27 @@ public class BpmnModelUtils { * @param bpmnModel BPMN模型 * @param variables 流程变量 */ + @SuppressWarnings("PatternVariableCanBeUsed") public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, Map variables){ - // 下一个执行的流程节点集合 - List nextFlowNodes = new ArrayList<>(); - // 当前执行节点的基本属性 - FlowNode currentNode = (FlowNode) currentElement; - // 获取当前节点的关联节点 - List outgoingFlows = currentNode.getOutgoingFlows(); - if (CollUtil.isEmpty(outgoingFlows)){ + List nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合 + FlowNode currentNode = (FlowNode) currentElement; // 当前执行节点的基本属性 + List outgoingFlows = currentNode.getOutgoingFlows(); // 当前节点的关联节点 + if (CollUtil.isEmpty(outgoingFlows)) { log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); return nextFlowNodes; } + // 遍历每个出口流 for (SequenceFlow outgoingFlow : outgoingFlows) { // 获取目标节点的基本属性 FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); - if (targetElement == null){ + if (targetElement == null) { continue; } - if (targetElement instanceof Gateway gateway) { - // 处理不同类型的网关 + // 情况一:处理不同类型的网关 + if (targetElement instanceof Gateway) { + Gateway gateway = (Gateway) targetElement; if (gateway instanceof ExclusiveGateway) { handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); } else if (gateway instanceof InclusiveGateway) { @@ -895,7 +894,7 @@ public class BpmnModelUtils { handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); } } else { - // 如果不是网关,直接添加到下一个节点列表 + // 情况二:如果不是网关,直接添加到下一个节点列表 nextFlowNodes.add((FlowNode) targetElement); } } @@ -903,15 +902,17 @@ public class BpmnModelUtils { } /** - * 处理排他网关 + * 处理排它网关 * * @param gateway 排他网关 * @param bpmnModel BPMN模型 * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里和simulateNextFlowElements中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋道 + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // TODO @小北: 这里和 simulateNextFlowElements 中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋艿 + // TODO @小北:ok,把 simulateNextFlowElements 里面处理网关的,复用这个方法,可以么? SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -940,7 +941,8 @@ public class BpmnModelUtils { * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); @@ -960,6 +962,7 @@ public class BpmnModelUtils { } }); } + /** * 处理并行网关 * @@ -968,7 +971,8 @@ public class BpmnModelUtils { * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { // 并行网关,遍历所有出口路径,获取目标节点 gateway.getOutgoingFlows().forEach(flow -> { FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 2b8e67c147..9da2610cb6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -175,9 +175,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); - // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 + // 合并 DB 和前端传递的流量变量,以前端的为主 Map historicVariables = historicProcessInstance.getProcessVariables(); - if (null != processVariables) { + if (CollUtil.isNotEmpty(processVariables)) { historicVariables.putAll(processVariables); } processVariables = historicVariables; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index ab9238dc10..0b5a70ab08 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -11,13 +11,10 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailReqVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; @@ -63,7 +60,6 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -535,9 +531,15 @@ public class BpmTaskServiceImpl implements BpmTaskService { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 下个节点审批人如果不存在,则由前端传递 + // 如果有下一个审批人,则设置到流程变量中 + // TODO @小北:validateNextAssignees 升级成 validateAndSetNextAssignees,然后里面吧下面这一小段逻辑,抽进去如何? if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + // TODO @小北:这里有个需要讨论的点,微信哈; + // TODO 因为 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES 定位是发起人,那么审批人选择的,放在 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES。目前想到两个方案: + // TODO 方案一:增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,然后设置到这里面。然后,BpmTaskCandidateStartUserSelectStrategy 也从这里读 + // TODO 方案二:也是增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,根据节点审批人类型,放到 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES、PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES + // TODO 方案三:融合成 PROCESS_INSTANCE_VARIABLE_USER_SELECT_ASSIGNEES,不再区分是发起人选择、还是审批人选择。 Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); hisProcessVariables.putAll(reqVO.getNextAssignees()); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); @@ -554,45 +556,52 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** - * 校验传递的参数中是否为下一个将要执行的任务节点 + * 校验选择的下一个节点的审批人,是否合法 * - * @param taskDefinitionKey 当前任务节点id + * 1. 是否有漏选:没有选择审批人 + * 2. 是否有多选:非下一个节点 + * + * @param taskDefinitionKey 当前任务节点标识 * @param variables 流程变量 * @param bpmnModel 流程模型 - * @param nextActivityNodes 下一个节点审批人集合(参数) + * @param nextAssignees 下一个节点审批人集合(参数) + * @param processInstance 流程实例 */ private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, - Map> nextActivityNodes,ProcessInstance processInstance){ - // 1、获取当前任务节点的信息 + Map> nextAssignees, ProcessInstance processInstance) { + // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2、获取下一个将要执行的节点集合 + // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3、循环下一个将要执行的节点集合 + + // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1、获取下一个将要执行节点的属性(是否为自选审批人等) + // 3.1 获取下一个将要执行节点的属性(是否为自选审批人等) + // TODO @小北:public static Integer parseCandidateStrategy(FlowElement userTask) 使用这个工具方法哈。 Map> extensionElements = nextFlowNode.getExtensionElements(); List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); - if (CollUtil.isEmpty(elements)){ + if (CollUtil.isEmpty(elements)) { continue; } - // 3.2、获取节点中的审批人策略 + // 3.2 获取节点中的审批人策略 Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 3.3、获取流程实例中的发起人自选审批人 + // 3.3 获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.4、如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 + // 3.4 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { // 先判断前端传递的参数节点节点是否为将要执行的节点 - if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ + // TODO @小北:!nextAssignees.containsKey(nextFlowNode.getId())、和 CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) 是不是等价的? + if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果节点存在,则获取节点中的审批人 - List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); // 如果前端传递的节点为空,则抛出异常 - if (CollUtil.isEmpty(nextAssignees)) { + // TODO @小北:换一个错误码哈。 + if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } + // TODO @小北:加一个“审批人选择”的校验; } } From 5e15a100cb660aa4c10bc364518a6380173b0dde Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 19:47:16 +0800 Subject: [PATCH 074/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../bpm/service/task/BpmTaskServiceImpl.java | 29 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 3a50bba523..28335f729b 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,6 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 0b5a70ab08..694e126026 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -530,9 +530,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 如果有下一个审批人,则设置到流程变量中 - // TODO @小北:validateNextAssignees 升级成 validateAndSetNextAssignees,然后里面吧下面这一小段逻辑,抽进去如何? if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 // TODO @小北:这里有个需要讨论的点,微信哈; @@ -567,38 +566,28 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1 获取下一个将要执行节点的属性(是否为自选审批人等) - // TODO @小北:public static Integer parseCandidateStrategy(FlowElement userTask) 使用这个工具方法哈。 - Map> extensionElements = nextFlowNode.getExtensionElements(); - List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); - if (CollUtil.isEmpty(elements)) { - continue; - } - // 3.2 获取节点中的审批人策略 - Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 3.3 获取流程实例中的发起人自选审批人 + // 3.1 获取下一个将要执行节点中的审批人策略 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 3.2 获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.4 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 + // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 先判断前端传递的参数节点节点是否为将要执行的节点 - // TODO @小北:!nextAssignees.containsKey(nextFlowNode.getId())、和 CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) 是不是等价的? + // 判断节点是否为执行节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果前端传递的节点为空,则抛出异常 - // TODO @小北:换一个错误码哈。 + // 判断节点的审批人是否配置 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } // TODO @小北:加一个“审批人选择”的校验; From 8a3264cfd34b37fe49081c906d50615a1742e5e1 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:22:50 +0800 Subject: [PATCH 075/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1eea893f76..743c45f5af 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,53 +808,26 @@ public class BpmnModelUtils { // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题。需要优化,具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - if (matchSequenceFlow == null) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlow = gateway.getOutgoingFlows().get(0); - } - } + SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); } - return; } - // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - if (currentElement instanceof InclusiveGateway) { + else if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); - if (CollUtil.isEmpty(matchSequenceFlows)) { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlows = gateway.getOutgoingFlows(); - } - } + Collection matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 matchSequenceFlows.forEach( flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); } - // 情况:ParallelGateway 并行,都满足,都走 - if (currentElement instanceof ParallelGateway) { + else if (currentElement instanceof ParallelGateway) { Gateway gateway = (Gateway) currentElement; // 遍历子节点 gateway.getOutgoingFlows().forEach( nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); - return; } } @@ -911,8 +884,25 @@ public class BpmnModelUtils { */ private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里和 simulateNextFlowElements 中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋艿 - // TODO @小北:ok,把 simulateNextFlowElements 里面处理网关的,复用这个方法,可以么? + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理排它网关(Exclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map variables){ SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -924,13 +914,7 @@ public class BpmnModelUtils { matchSequenceFlow = gateway.getOutgoingFlows().get(0); } } - // 遍历满足条件的 SequenceFlow 路径 - if (matchSequenceFlow != null) { - FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - } + return matchSequenceFlow; } /** @@ -943,6 +927,26 @@ public class BpmnModelUtils { */ private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径集合 + Collection matchSequenceFlows = findMatchSequenceFlows(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 处理排它网关(Inclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static Collection findMatchSequenceFlows(Gateway gateway, Map variables) { + // 查找满足条件的 SequenceFlow 路径 Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); @@ -954,15 +958,10 @@ public class BpmnModelUtils { matchSequenceFlows = gateway.getOutgoingFlows(); } } - // 遍历满足条件的 SequenceFlow 路径,获取目标节点 - matchSequenceFlows.forEach(flow -> { - FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - }); + return matchSequenceFlows; } + /** * 处理并行网关 * From 3cf0708f64c0110d95eec78a0c56e031c466fb32 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:36:01 +0800 Subject: [PATCH 076/116] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 694e126026..be6f33ae08 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -581,11 +581,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 判断节点是否为执行节点 + // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 判断节点的审批人是否配置 + // 判断节点的审批人是否配置,节点存在,但未配置审批人 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } From 9927dd443944d787423726cdfa3f4e00ca7e0bf9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 20:39:23 +0800 Subject: [PATCH 077/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9A=E4=B9=89=E7=9A=84=20simple=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/BpmModelController.java | 2 +- .../definition/BpmProcessDefinitionController.java | 13 +++++++++++++ .../admin/task/BpmProcessInstanceController.java | 12 +++++++++--- .../task/vo/instance/BpmProcessInstanceRespVO.java | 8 ++++++++ .../admin/task/vo/task/BpmTaskPageReqVO.java | 3 +++ .../bpm/convert/task/BpmProcessInstanceConvert.java | 9 +++++++++ .../bpm/service/task/BpmProcessInstanceService.java | 1 - .../module/bpm/service/task/BpmTaskServiceImpl.java | 3 +++ 8 files changed, 46 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java index 6483ee0e39..5578114f9d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java @@ -57,7 +57,7 @@ public class BpmModelController { @GetMapping("/list") @Operation(summary = "获得模型分页") @Parameter(name = "name", description = "模型名称", example = "芋艿") - public CommonResult> getModelPage(@RequestParam(value = "name", required = false) String name) { + public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { List list = modelService.getModelList(name); if (CollUtil.isEmpty(list)) { return success(Collections.emptyList()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java index 28cbd0ab98..90c38886a7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java @@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.springframework.security.access.prepost.PreAuthorize; @@ -31,6 +32,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -99,6 +101,17 @@ public class BpmProcessDefinitionController { list, null, processDefinitionMap, null, null)); } + @GetMapping("/simple-list") + @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") + public CommonResult> getSimpleProcessDefinitionList() { + // 只查询未挂起的流程 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState( + SuspensionState.ACTIVE.getStateCode()); + // 拼接 VO 返回,只返回 id、name、key + return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() + .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); + } + @GetMapping ("/get") @Operation(summary = "获得流程定义") @Parameter(name = "id", description = "流程编号", required = true, example = "1024") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index def95cca63..7ce43a32c2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -32,10 +32,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; +import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” @@ -78,8 +78,14 @@ public class BpmProcessInstanceController { convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Set userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())); + userIds.addAll(convertSetByFlatMap(taskMap.values(), + tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap( + convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, - processDefinitionMap, categoryMap, taskMap, null, null, processDefinitionInfoMap)); + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); } @GetMapping("/manager-page") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index 76dca606a2..f73c490a54 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -73,6 +74,13 @@ public class BpmProcessInstanceRespVO { @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + private UserSimpleBaseVO assigneeUser; + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index 11c59ce3ed..f129e5a315 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -18,6 +18,9 @@ public class BpmTaskPageReqVO extends PageParam { @Schema(description = "流程分类", example = "1") private String category; + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + @Schema(description = "创建时间") @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java index 6e798e77f2..ccd7f06e74 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -76,6 +76,15 @@ public interface BpmProcessInstanceConvert { respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName())); } + if (CollUtil.isNotEmpty(respVO.getTasks())) { + respVO.getTasks().forEach(task -> { + AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee()); + if (assigneeUser!= null) { + task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, assigneeUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + }); + } } // 摘要 respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index c4684d3402..6b9c6fa3e7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -85,7 +85,6 @@ public interface BpmProcessInstanceService { PageResult getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO); - // TODO @芋艿:重点在 review 下 /** * 获取审批详情。 *

diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 0b5a70ab08..5d9f1552b4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -122,6 +122,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (StrUtil.isNotEmpty(pageVO.getCategory())) { taskQuery.taskCategory(pageVO.getCategory()); } + if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) { + taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey()); + } if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); From 4d35a272c340b132c7dfc283d3fa01bf16f90db9 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 22:30:40 +0800 Subject: [PATCH 078/116] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B8=B8=E9=87=8FPROCESS=5FINSTANCE=5FVARIABLE=5FAPPROVE=5FUSE?= =?UTF-8?q?R=5FSELECT=5FASSIGNEES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmnVariableConstants.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 119a441857..c10d91dcf2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -29,6 +29,12 @@ public class BpmnVariableConstants { * @see ProcessInstance#getProcessVariables() */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 审批人选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; /** * 流程实例的变量 - 发起用户 ID * From d8a7d668a4658b9ece574e4eb497795889bd6a04 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Mar 2025 09:56:54 +0800 Subject: [PATCH 079/116] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E6=94=AF=E6=8C=81=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E2=80=9C=E5=8E=86=E5=8F=B2=E2=80=9D=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/vo/model/BpmModelRespVO.java | 2 +- .../process/BpmProcessDefinitionRespVO.java | 29 ++++--------------- .../BpmProcessDefinitionInfoDO.java | 11 +++++-- .../BpmProcessDefinitionServiceImpl.java | 3 +- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index 278291483d..275368c7c6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -25,7 +25,7 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") private String icon; - @Schema(description = "流程分类编码", example = "1") + @Schema(description = "流程分类编号", example = "1") private String category; @Schema(description = "流程分类名字", example = "请假") private String categoryName; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 1e9dfc8207..8da290440e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -1,14 +1,14 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -import java.util.List; @Schema(description = "管理后台 - 流程定义 Response VO") @Data -public class BpmProcessDefinitionRespVO { +public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; @@ -22,36 +22,19 @@ public class BpmProcessDefinitionRespVO { @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") private String key; - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") - private String icon; - - @Schema(description = "流程描述", example = "我是描述") - private String description; - @Schema(description = "流程分类", example = "1") private String category; @Schema(description = "流程分类名字", example = "请假") private String categoryName; + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 - @Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1") - private Integer formType; - @Schema(description = "表单编号-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", example = "1024") - private Long formId; @Schema(description = "表单名字", example = "请假表单") private String formName; - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; - @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", - example = "/bpm/oa/leave/create") - private String formCustomCreatePath; - @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", - example = "/bpm/oa/leave/view") - private String formCustomViewPath; @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 @@ -67,7 +50,7 @@ public class BpmProcessDefinitionRespVO { @Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long sort; - + @Schema(description = "BPMN UserTask 用户任务") @Data public static class UserTask { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index ccab7b0435..34dfbece3a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.definition; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; @@ -60,6 +59,14 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { */ private Integer modelType; + /** + * 流程分类的编码 + * + * 关联 {@link BpmCategoryDO#getCode()} + * + * 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置 + */ + private String category; /** * 图标 */ @@ -149,7 +156,7 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { * * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 */ - @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 private List managerUserIds; /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index 96280fa088..14ee084063 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -143,9 +143,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ // 插入拓展表 BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) - .setModelId(model.getId()).setProcessDefinitionId(definition.getId()) + .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); - if (form != null) { definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); } From 9dfed5365b2b3216406e53603ffd9cab397003c2 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 21:06:28 +0800 Subject: [PATCH 080/116] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=EF=BC=9A=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E6=A0=87?= =?UTF-8?q?=E8=AF=86=EF=BC=8C=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E7=AD=96=E7=95=A5=EF=BC=8C?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E8=8E=B7=E5=BE=97=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9E=E4=BE=8B=E7=9A=84=E5=AE=A1=E6=89=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=80=89=E6=8B=A9=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=94=99=E8=AF=AF=E7=A0=811=5F009?= =?UTF-8?q?=5F004=5F007=EF=BC=8C=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=9C=AA=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 2 +- ...askCandidateApproveUserSelectStrategy.java | 73 +++++++++++++++++++ .../enums/BpmTaskCandidateStrategyEnum.java | 1 + .../flowable/core/util/FlowableUtils.java | 25 +++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 49 +++++++------ 5 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 28335f729b..7fb85b7835 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java new file mode 100644 index 0000000000..c910f08b18 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.Sets; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * 审批人在审批时选择下一个节点的审批人 + * @author smallNorthLee + */ +@Component +public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 990bc6303a..0cdb56fb90 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,6 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), + APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 6022b8fe93..fe83495e35 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -200,6 +200,31 @@ public class FlowableUtils { BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); } + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processInstance 流程实例 + * @return 审批用户选择的下一个节点的审批人Map + */ + public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processVariables 流程变量 + * @return 审批用户选择的下一个节点的审批人Map Map + */ + @SuppressWarnings("unchecked") + public static Map> getApproveUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return null; + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + } + /** * 获得流程实例的摘要 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index be6f33ae08..4b82ff991b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -528,22 +528,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 如果有下一个审批人,则设置到流程变量中 - if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - // TODO @小北:这里有个需要讨论的点,微信哈; - // TODO 因为 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES 定位是发起人,那么审批人选择的,放在 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES。目前想到两个方案: - // TODO 方案一:增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,然后设置到这里面。然后,BpmTaskCandidateStartUserSelectStrategy 也从这里读 - // TODO 方案二:也是增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,根据节点审批人类型,放到 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES、PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES - // TODO 方案三:融合成 PROCESS_INSTANCE_VARIABLE_USER_SELECT_ASSIGNEES,不再区分是发起人选择、还是审批人选择。 - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); - hisProcessVariables.putAll(reqVO.getNextAssignees()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - runtimeService.setVariables(task.getProcessInstanceId(), variables); + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + bpmnModel, reqVO.getNextAssignees(), instance); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -566,21 +553,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + // 获取流程实例中的审批人自选审批人 + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isEmpty(hisProcessVariables)){ + hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + } + // 校验通过的全部节点和审批人 + Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的发起人自选审批人 - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { + // 3.2 获取流程实例中的审批人自选审批人 + List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) + || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) + && CollUtil.isEmpty(approveUserSelectAssignee)) { // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); @@ -589,9 +584,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + // 4.3.3 校验通过的全部节点和审批人 + allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } // TODO @小北:加一个“审批人选择”的校验; } + // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 + Map newVariables = FlowableUtils.filterTaskFormVariable(variables); + // 4. 如果下个节点审批参数不为空,则设置流程变量 + if (CollUtil.isNotEmpty(allNextAssignees)) { + // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + hisProcessVariables.putAll(allNextAssignees); + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + } + return newVariables; } /** From 1b08e6828fd08714ed9c3dfe46b54b177b55d647 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 22:31:06 +0800 Subject: [PATCH 081/116] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 11 +++++ .../task/BpmProcessInstanceService.java | 10 +++++ .../task/BpmProcessInstanceServiceImpl.java | 45 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index def95cca63..21d695ac49 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -178,4 +178,15 @@ public class BpmProcessInstanceController { public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + + @GetMapping("/get-next-flow-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index c4684d3402..260a9fbd42 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -97,6 +97,15 @@ public interface BpmProcessInstanceService { */ BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** + * 获取下一个执行节点信息。 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 下一个执行节点信息 + */ + List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** * 获取流程实例的 BPMN 模型视图 * @@ -173,4 +182,5 @@ public interface BpmProcessInstanceService { * @param instance 流程任务 */ void processProcessInstanceCompleted(ProcessInstance instance); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9da2610cb6..604a228a1c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -11,6 +11,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -55,6 +56,7 @@ import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -225,6 +227,49 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); } + @Override + public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1 校验任务存在 + Task task = taskService.getTask(reqVO.getTaskId()); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + // 2 校验任务是否由当前用户审批 + if (StrUtil.isNotBlank(task.getAssignee()) + && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + // 3 校验流程实例存在 + ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + // 4 获取 BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + // 5. 获取当前任务节点的信息 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); + return convertList(nextFlowNodes, nodes -> { + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); + ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) + .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(FlowableUtils.getTaskStatus(task)) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); + + // 如果是取消状态,则跳过 + if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { + return null; + } + return activityNode; + }); + } + @Override @SuppressWarnings("unchecked") public PageResult getProcessInstancePage(Long userId, From 33ab9616291bd49cbbf82d5fde5c0ff184d05baf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 3 Mar 2025 17:36:13 +0800 Subject: [PATCH 082/116] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 604a228a1c..fd93110723 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -15,6 +15,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; @@ -249,24 +250,43 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (bpmnModel == null) { return null; } - // 5. 获取当前任务节点的信息 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + Map processVariables = new HashMap<>(); + // 获取历史中流程变量 + Map historicVariables = historicProcessInstance.getProcessVariables(); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); + } + // 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + processVariables.putAll(reqVO.getProcessVariables()); + } + // 获取流程定义信息 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(task.getProcessDefinitionId()); + // 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); - return convertList(nextFlowNodes, nodes -> { - FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); - ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) - .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) - ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(FlowableUtils.getTaskStatus(task)) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); - - // 如果是取消状态,则跳过 - if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { - return null; + // 获取下一个将要执行的节点集合 + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); + return convertList(nextFlowNodes, node -> { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + //存在一个节点多人审批的情况 + List candidateUsers = new ArrayList<>(); + for (Long candidateUserId : candidateUserIds) { + Map userMap = adminUserApi.getUserMap(candidateUserIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); } - return activityNode; + return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setId(node.getId()) + .setName(node.getName()) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUsers(candidateUsers); }); } @@ -725,8 +745,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // true,不影响没配置 // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { + // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + // 设置流程变量,审批人自选审批人 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, + startUserSelectAssignees); } // 3. 创建流程 @@ -768,9 +792,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } - // 2.1 移除掉不是发起人自选审批人节点 + // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From d41cce94cd1d6c7cc515b3c1023676d3584a3244 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 21:24:13 +0800 Subject: [PATCH 083/116] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 4 ++++ .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 743c45f5af..c3d033c05c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -856,6 +856,10 @@ public class BpmnModelUtils { if (targetElement == null) { continue; } + // 如果是结束节点,直接返回 + if (targetElement instanceof EndEvent) { + break; + } // 情况一:处理不同类型的网关 if (targetElement instanceof Gateway) { Gateway gateway = (Gateway) targetElement; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 4b82ff991b..ee37dc4d70 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -595,8 +595,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(allNextAssignees)) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 hisProcessVariables.putAll(allNextAssignees); + // 设置流程变量,审批人自选 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From 6e2d00d561d5b7b866f90012da43d83eb8c7c911 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 22:38:05 +0800 Subject: [PATCH 084/116] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/task/BpmProcessInstanceServiceImpl.java | 10 ++++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fd93110723..8dde5b208d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -169,7 +169,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Long startUserId = loginUserId; // 流程发起人 HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 - Map processVariables = reqVO.getProcessVariables(); // 流程变量 + Map processVariables = new HashMap<>(); // 流程变量 // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); @@ -180,10 +180,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); // 合并 DB 和前端传递的流量变量,以前端的为主 Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(processVariables)) { - historicVariables.putAll(processVariables); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); } - processVariables = historicVariables; + } + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index ee37dc4d70..2a89be970e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -531,6 +531,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 校验传递的参数中是否为下一个将要执行的任务节点 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -555,6 +556,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { + // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 + if (CollUtil.isEmpty(nextAssignees)){ + return variables; + } // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 @@ -587,7 +592,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 4.3.3 校验通过的全部节点和审批人 allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } - // TODO @小北:加一个“审批人选择”的校验; } // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 Map newVariables = FlowableUtils.filterTaskFormVariable(variables); @@ -599,7 +603,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From dadd43677eb90d5df6929030b960b59174c946f6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:07:58 +0800 Subject: [PATCH 085/116] =?UTF-8?q?review:=2051=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index c910f08b18..27c08c0ada 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -50,6 +50,9 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", execution.getProcessInstanceId()); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } // 获得审批人 List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); @@ -62,6 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa return Sets.newLinkedHashSet(); } Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", + activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } From 4e57bd157fa61cbcb8af1f2f9ca350ea84de6dd6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:09:11 +0800 Subject: [PATCH 086/116] =?UTF-8?q?review:=2027=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmTaskCandidateStrategyEnum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 0cdb56fb90..9b8474ebf0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 + APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), From 9e151b396676f82f5cd8e8ba07998a6d60f44b7c Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:28:25 +0800 Subject: [PATCH 087/116] =?UTF-8?q?review:=20278=20281=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 115 +++++++++--------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 8dde5b208d..9111176fd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) @@ -232,57 +232,62 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1 校验任务存在 + // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { throw exception(TASK_NOT_EXISTS); } - // 2 校验任务是否由当前用户审批 + // 1.2 校验任务是否由当前用户审批 if (StrUtil.isNotBlank(task.getAssignee()) && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); } - // 3 校验流程实例存在 + // 1.3 校验流程实例存在 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 4 获取 BpmnModel + // 1.4 校验BpmnModel BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); if (bpmnModel == null) { return null; } + //1.5 流程实例是否存在 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); if (historicProcessInstance == null) { throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); } + + // 2 设置流程变量 Map processVariables = new HashMap<>(); - // 获取历史中流程变量 + // 2.1 获取历史中流程变量 Map historicVariables = historicProcessInstance.getProcessVariables(); if (CollUtil.isNotEmpty(historicVariables)) { processVariables.putAll(historicVariables); } - // 合并前端传递的流程变量,以前端为准 - if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + // 2.2 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } - // 获取流程定义信息 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(task.getProcessDefinitionId()); - // 获取当前任务节点的信息 + // 3 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - // 获取下一个将要执行的节点集合 + // 3.1 获取下一个将要执行的节点集合 List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); return convertList(nextFlowNodes, node -> { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - //存在一个节点多人审批的情况 - List candidateUsers = new ArrayList<>(); - for (Long candidateUserId : candidateUserIds) { - Map userMap = adminUserApi.getUserMap(candidateUserIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); + // 3.2 获取节点的审批人信息 + Map userMap = adminUserApi.getUserMap(candidateUserIds); + if (CollUtil.isEmpty(userMap)) { + return null; } + // 3.3 获取节点的审批人部门信息 + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + // 3.4 存在一个节点多人审批的情况,组装审批人信息 + List candidateUsers = new ArrayList<>(); + userMap.forEach((key, value) -> { + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); + }); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) @@ -356,15 +361,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -386,9 +391,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -400,7 +405,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -456,11 +461,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -521,9 +526,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -545,8 +550,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -590,8 +595,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } @@ -622,7 +627,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, - Long startUserId, String processDefinitionId, Map processVariables) { + Long startUserId, String processDefinitionId, Map processVariables) { Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, startUserId, processDefinitionId, processVariables); return new ArrayList<>(userIds); @@ -658,11 +663,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.notEqual(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.equals(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 @@ -714,8 +719,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private String createProcessInstance0(Long userId, ProcessDefinition definition, - Map variables, String businessKey, - Map> startUserSelectAssignees) { + Map variables, String businessKey, + Map> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -744,8 +749,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 - // true,不影响没配置 - // skipExpression 的节点 + // true,不影响没配置 + // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, @@ -784,20 +789,20 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, Map> startUserSelectAssignees, - Map variables) { + Map variables) { // 1. 获取预测的节点信息 BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() .setProcessDefinitionId(definition.getId()) .setProcessVariables(variables)); List activityNodes = detailRespVO.getActivityNodes(); - if (CollUtil.isEmpty(activityNodes)){ + if (CollUtil.isEmpty(activityNodes)) { return; } // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From c16f7b8154d0691e2bb5846564a07e17b76e5534 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:54:44 +0800 Subject: [PATCH 088/116] =?UTF-8?q?review:=2024=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index 27c08c0ada..fd5e121196 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -21,6 +21,7 @@ import java.util.Map; /** * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 * 审批人在审批时选择下一个节点的审批人 + * * @author smallNorthLee */ @Component From ebd722cb4162da1c2160661ead9589cda508d55b Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:59:22 +0800 Subject: [PATCH 089/116] =?UTF-8?q?review:=20=E4=BF=AE=E6=94=B9=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E5=92=8C=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 21 ++++++++++--------- .../task/BpmProcessInstanceService.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 11 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 21d695ac49..a7dddf2645 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -172,6 +172,17 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); + } + @GetMapping("/get-bpmn-model-view") @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") @Parameter(name = "id", description = "流程实例的编号", required = true) @@ -179,14 +190,4 @@ public class BpmProcessInstanceController { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } - @GetMapping("/get-next-flow-nodes") - @Operation(summary = "获取下一个执行的流程节点") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - @SuppressWarnings("unchecked") - public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); - } - return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 260a9fbd42..69f64c4dbc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -104,7 +104,7 @@ public interface BpmProcessInstanceService { * @param reqVO 请求信息 * @return 下一个执行节点信息 */ - List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); /** * 获取流程实例的 BPMN 模型视图 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9111176fd4..fc61cc038a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -231,7 +231,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override - public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { @@ -755,9 +755,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); - // 设置流程变量,审批人自选审批人 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, - startUserSelectAssignees); +// // 设置流程变量,审批人自选审批人 +// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, +// startUserSelectAssignees); } // 3. 创建流程 @@ -801,8 +801,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From ffa7c246cfafacc53c326b0d86ded26712402943 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Tue, 4 Mar 2025 23:17:35 +0800 Subject: [PATCH 090/116] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=20=E5=90=8C=E4=B8=80=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=9F=A5=E8=AF=A2=E4=BC=A0=E9=80=92=E7=9A=84?= =?UTF-8?q?=20taskId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 4 +- .../bpm/service/task/BpmTaskService.java | 7 +- .../bpm/service/task/BpmTaskServiceImpl.java | 67 ++++++++++++------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 0bb8a29d80..640ac85dc8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -207,9 +207,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 3.1 计算当前登录用户的待办任务 - // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 - // B,会不会表单权限不一致哈。 - BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); + BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); // 3.2 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index a40fadba75..ff8dcca9fe 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -35,13 +35,16 @@ public interface BpmTaskService { PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); /** - * 获得用户在指定流程下,首个需要处理(待办)的任务 + * 获得用户(待办)的任务。 + * 1、根据 id 查询待办任务 + * 2、如果任务不存在,获取指定流程下,首个需要处理任务 * * @param userId 用户编号 + * @param id 任务编号 * @param processInstanceId 流程实例编号 * @return 待办任务 */ - BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId); + BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId); /** * 获得已办的流程任务分页 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 483741868e..ff6eb46186 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -133,32 +133,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId) { - if (processInstanceId == null) { - return null; + public BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId) { + // 1.1 获取指定的用户待办任务 + Task todoTask = getMyTodoTask(userId, id); + if (todoTask == null) { + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 + todoTask = getFirstMyTodoTask(userId, processInstanceId); } - // 1. 查询所有任务 - List tasks = taskService.createTaskQuery() - .active() - .processInstanceId(processInstanceId) - .includeTaskLocalVariables() - .includeProcessVariables() - .orderByTaskCreateTime().asc() // 按创建时间升序 - .list(); - if (CollUtil.isEmpty(tasks)) { - return null; - } - - // 2.1 查询我的首个任务 - Task todoTask = CollUtil.findOne(tasks, task -> { - return isAssignUserTask(userId, task) // 当前用户为审批人 - || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) - }); if (todoTask == null) { return null; } - // 2.2 查询该任务的子任务 - List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), tasks); + + // 2.查询该任务的子任务 + List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); // 3. 转换返回 BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId()); @@ -178,6 +165,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } + private Task getMyTodoTask(Long userId, String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + Task task = getTask(id); + if (task == null) { + return null; + } + if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) { + return null; + } + return task; + } + + private Task getFirstMyTodoTask(Long userId, String processInstanceId) { + if (processInstanceId == null) { + return null; + } + // 1. 查询所有任务 + List tasks = taskService.createTaskQuery() + .active() + .processInstanceId(processInstanceId) + .includeTaskLocalVariables() + .includeProcessVariables() + .orderByTaskCreateTime().asc() // 按创建时间升序 + .list(); + + // 2. 查询我的首个任务 + return CollUtil.findOne(tasks, task -> { + return isAssignUserTask(userId, task) // 当前用户为审批人 + || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) + }); + } + @Override public PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) { HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() @@ -1230,7 +1251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); if (userTaskElement.getId().equals(START_USER_NODE_ID) && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 - || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); From 1e3fd24d651659cf58fcaa613fb74ea2b93674fc Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 23:18:19 +0800 Subject: [PATCH 091/116] =?UTF-8?q?review:=20=E6=B5=81=E7=A8=8B=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E6=97=B6=EF=BC=8C=E5=8F=AA=E9=A2=84=E6=B5=8B=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E5=8F=91=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=E7=9A=84=E8=8A=82=E7=82=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 3 +-- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index fd5e121196..a316925655 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -65,9 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa if (processVariables == null) { return Sets.newLinkedHashSet(); } + // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); - Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", - activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fc61cc038a..080529f778 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,6 +224,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); + // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ + simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + } // 4. 拼接最终数据 return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, From 1f9769a4329ad361b73ab6cb33595b01e29f0bd0 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 00:36:48 +0800 Subject: [PATCH 092/116] =?UTF-8?q?review:=20=E6=8B=86=E5=88=86=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=20=E5=92=8C=20=E5=AE=A1=E6=89=B9=E4=BA=BA=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 3 - .../bpm/service/task/BpmTaskServiceImpl.java | 63 ++++++++++--------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 080529f778..ba72e25b23 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -282,9 +282,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); // 3.2 获取节点的审批人信息 Map userMap = adminUserApi.getUserMap(candidateUserIds); - if (CollUtil.isEmpty(userMap)) { - return null; - } // 3.3 获取节点的审批人部门信息 Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 2a89be970e..30d0e9c5b1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -564,47 +564,48 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 获取流程实例中的审批人自选审批人 - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isEmpty(hisProcessVariables)){ - hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - } - // 校验通过的全部节点和审批人 - Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的审批人自选审批人 - List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) - || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) - && CollUtil.isEmpty(approveUserSelectAssignee)) { - // 判断节点是否为执行节点,仅校验节点 - if (!nextAssignees.containsKey(nextFlowNode.getId())) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + // 3.2 判断节点是否为执行节点,仅校验节点 + if (!nextAssignees.containsKey(nextFlowNode.getId())) { + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + } + // 3.3 获取节点中的审批人 + List assignees = nextAssignees.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { + Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) { + continue; } // 判断节点的审批人是否配置,节点存在,但未配置审批人 - if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { + if (CollUtil.isEmpty(assignees)){ + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + } + // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + // 判断节点的审批人是否配置,节点存在,但未配置审批人 + if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - // 4.3.3 校验通过的全部节点和审批人 - allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (hisProcessVariables == null) { + hisProcessVariables = new HashMap<>(); + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + } + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); } } - // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - Map newVariables = FlowableUtils.filterTaskFormVariable(variables); - // 4. 如果下个节点审批参数不为空,则设置流程变量 - if (CollUtil.isNotEmpty(allNextAssignees)) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - hisProcessVariables.putAll(allNextAssignees); - // 设置流程变量,审批人自选 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - return newVariables; + return variables; } /** From 1e2b56256c5c633566d9c273388ccada080ab6df Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 09:25:00 +0800 Subject: [PATCH 093/116] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ba72e25b23..0aa7dcea57 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -286,9 +286,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 List candidateUsers = new ArrayList<>(); - userMap.forEach((key, value) -> { - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); - }); + userMap.forEach((key, value) -> candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap))); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) From 672a5ef538c2374a142e919ebd40ed3fcb4a2eaf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:30:38 +0800 Subject: [PATCH 094/116] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 30d0e9c5b1..1b17e53351 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -564,6 +564,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + Map> processVariables = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 @@ -574,35 +575,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 3.3 获取节点中的审批人 List assignees = nextAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选 + // 3.4 流程变量 + // 3.5 如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if(processVariables == null){ + processVariables = new HashMap<>(); + } + List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { continue; } - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)){ throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } - // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (hisProcessVariables == null) { - hisProcessVariables = new HashMap<>(); - // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - } - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + // 校验通过的全部节点和审批人 + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } return variables; From ead8e94deb554e19bf99a53237afff23b20ab39f Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:37:32 +0800 Subject: [PATCH 095/116] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 0aa7dcea57..e09c6452a1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,7 +224,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); - // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点 if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } @@ -360,15 +360,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -390,9 +390,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -460,11 +460,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -525,9 +525,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -549,8 +549,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -594,8 +594,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } From a23ce60041a32859e6df916fa9f4d1da03bb322a Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:39:43 +0800 Subject: [PATCH 096/116] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index e09c6452a1..0309ecd8a4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) From 847e51269b8934823963d7e5822353de3c634c4a Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:40:44 +0800 Subject: [PATCH 097/116] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../definition/BpmModelServiceImpl.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fb85b7835..a8cc848e31 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -24,6 +24,7 @@ public interface ErrorCodeConstants { ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); // ========== 流程定义 1-009-003-000 ========== ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 9ccc2f2c92..df91df62ec 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; @@ -23,9 +24,7 @@ import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.StartEvent; -import org.flowable.bpmn.model.UserTask; +import org.flowable.bpmn.model.*; import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; @@ -48,6 +47,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; /** * 流程模型实现:主要进行 Flowable {@link Model} 的维护 @@ -243,7 +243,18 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验 UserTask 的 name 都配置了 + // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 + List outgoingFlows = startEvent.getOutgoingFlows(); + if (CollUtil.isNotEmpty(outgoingFlows)){ + // 2.1 获取第一个用户节点 + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + // 2.2 获取审批人策略 + Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); + } + } + // 3. 校验 UserTask 的 name 都配置了 List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); userTasks.forEach(userTask -> { if (StrUtil.isEmpty(userTask.getName())) { From 1b678bd7a990e4c4c2188b212fcf74bcab9ddade Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:41:01 +0800 Subject: [PATCH 098/116] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/definition/BpmModelServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index df91df62ec..4e3365e070 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -246,7 +246,7 @@ public class BpmModelServiceImpl implements BpmModelService { // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 List outgoingFlows = startEvent.getOutgoingFlows(); if (CollUtil.isNotEmpty(outgoingFlows)){ - // 2.1 获取第一个用户节点 + // 2.1 获取第一个用户任务节点 FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); // 2.2 获取审批人策略 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); From 9ff56403d6782d94a5d1fc75aea8a17b34cecaad Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Mar 2025 07:51:20 +0800 Subject: [PATCH 099/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 4 +- .../enums/BpmTaskCandidateStrategyEnum.java | 4 +- .../core/enums/BpmnVariableConstants.java | 2 + .../flowable/core/util/BpmnModelUtils.java | 14 ++--- .../definition/BpmModelServiceImpl.java | 12 ++-- .../task/BpmProcessInstanceService.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 61 ++++++++----------- .../bpm/service/task/BpmTaskService.java | 8 +++ .../bpm/service/task/BpmTaskServiceImpl.java | 45 ++++++-------- 9 files changed, 73 insertions(+), 79 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index a8cc848e31..65605142c2 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); - ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); + ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】"); // ========== 流程定义 1-009-003-000 ========== ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); @@ -41,7 +41,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 9b8474ebf0..2a45e3a10c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/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 { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人 - START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 + APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index c10d91dcf2..893c4d053c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/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 { * 流程实例的变量 - 发起用户选择的审批人 Map * * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; /** * 流程实例的变量 - 审批人选择的审批人 Map * * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT */ public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index c3d033c05c..af5b0852f6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/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 { // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); } } // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - else if (currentElement instanceof InclusiveGateway) { + else if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Collection matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 matchSequenceFlows.forEach( flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); @@ -889,7 +889,7 @@ public class BpmnModelUtils { private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); @@ -906,7 +906,7 @@ public class BpmnModelUtils { * @param variables 流程变量 * @return 符合条件的路径 */ - private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map variables){ + private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -932,7 +932,7 @@ public class BpmnModelUtils { private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { // 查找满足条件的 SequenceFlow 路径集合 - Collection matchSequenceFlows = findMatchSequenceFlows(gateway, variables); + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables); // 遍历满足条件的 SequenceFlow 路径,获取目标节点 matchSequenceFlows.forEach(flow -> { FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); @@ -949,7 +949,7 @@ public class BpmnModelUtils { * @param variables 流程变量 * @return 符合条件的路径 */ - private static Collection findMatchSequenceFlows(Gateway gateway, Map variables) { + private static Collection findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map variables) { // 查找满足条件的 SequenceFlow 路径 Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 4e3365e070..922013ddba 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/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 { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 + // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况 List outgoingFlows = startEvent.getOutgoingFlows(); - if (CollUtil.isNotEmpty(outgoingFlows)){ - // 2.1 获取第一个用户任务节点 + // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。 + // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务??? + if (CollUtil.isNotEmpty(outgoingFlows)) { FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); - // 2.2 获取审批人策略 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ - throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName()); } } // 3. 校验 UserTask 的 name 都配置了 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 7df913bf77..6c13416ef4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/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 { BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); /** - * 获取下一个执行节点信息。 + * 获取下一个执行节点信息 * * @param loginUserId 登录人的用户编号 * @param reqVO 请求信息 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 00f40c9fb3..7a8e5a2f5a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -179,9 +178,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); // 合并 DB 和前端传递的流量变量,以前端的为主 - Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(historicVariables)) { - processVariables.putAll(historicVariables); + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); } } if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { @@ -222,9 +220,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); - // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ - simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 + // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + simulateActivityNodes.removeIf(node -> + BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } // 4. 拼接最终数据 @@ -234,46 +234,37 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1.1 校验任务存在 - Task task = taskService.getTask(reqVO.getTaskId()); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } - // 1.2 校验任务是否由当前用户审批 - if (StrUtil.isNotBlank(task.getAssignee()) - && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); - } - // 1.3 校验流程实例存在 + // 1.1 校验任务存在,且是当前用户的 + Task task = taskService.validateTask(loginUserId, reqVO.getTaskId()); + // 1.2 校验流程实例存在 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 1.4 校验BpmnModel - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); - if (bpmnModel == null) { - return null; - } - //1.5 流程实例是否存在 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); if (historicProcessInstance == null) { throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); } + // 1.3 校验BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } - // 2 设置流程变量 + // 2. 设置流程变量 Map processVariables = new HashMap<>(); // 2.1 获取历史中流程变量 - Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(historicVariables)) { - processVariables.putAll(historicVariables); + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); } // 2.2 合并前端传递的流程变量,以前端为准 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } + // 3 获取当前任务节点的信息 - FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); // 3.1 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); return convertList(nextFlowNodes, node -> { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), @@ -290,6 +281,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .setName(node.getName()) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + // TODO @小北:先把 candidateUserIds 设置完,然后最后拼接 candidateUsers 信息。这样,如果有多个节点,就不用重复查询啦;类似 buildApprovalDetail 思路; + // TODO 先拼接处 List ActivityNode + // TODO 接着,再起一段,处理 adminUserApi.getUserMap(candidateUserIds)、deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)) .setCandidateUsers(candidateUsers); }); } @@ -745,16 +739,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 - // true,不影响没配置 - // skipExpression 的节点 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); -// // 设置流程变量,审批人自选审批人 -// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, -// startUserSelectAssignees); } // 3. 创建流程 @@ -796,7 +785,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } - // 2.1 移除掉不是发起人或者审批人自选审批人节点 + // 2.1 移除掉不是发起人自选审批人节点 activityNodes.removeIf(task -> ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index ff8dcca9fe..d95e226d59 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -92,6 +92,14 @@ public interface BpmTaskService { */ List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + Task validateTask(Long userId, String taskId); + /** * 获取任务 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index ef04d799f1..11f219850b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -277,13 +276,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return query.list(); } - /** - * 校验任务是否存在,并且是否是分配给自己的任务 - * - * @param userId 用户 id - * @param taskId task id - */ - private Task validateTask(Long userId, String taskId) { + @Override + public Task validateTask(Long userId, String taskId) { Task task = validateTaskExist(taskId); // 为什么判断 assignee 非空的情况下? // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 @@ -552,12 +546,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - // 校验传递的参数中是否为下一个将要执行的任务节点 + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + // 完成任务 runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { + // 完成任务 taskService.complete(task.getId()); } @@ -579,54 +575,52 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param processInstance 流程实例 */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, - Map> nextAssignees, ProcessInstance processInstance) { + Map> nextAssignees, ProcessInstance processInstance) { // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 - if (CollUtil.isEmpty(nextAssignees)){ + // TODO @小北:会出现漏选,其实需要的情况哇? + if (CollUtil.isEmpty(nextAssignees)) { return variables; } - // 1. 获取当前任务节点的信息 + // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + + // 2. 循环下一个将要执行的节点集合 Map> processVariables = new HashMap<>(); - // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1 获取下一个将要执行节点中的审批人策略 - Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 3.3 获取节点中的审批人 List assignees = nextAssignees.get(nextFlowNode.getId()); - // 3.4 流程变量 - // 3.5 如果节点中的审批人策略为 发起人自选 + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if(processVariables == null){ + if (processVariables == null) { processVariables = new HashMap<>(); } List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 如果当前节点已经存在审批人,则不允许覆盖 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { continue; } // 如果节点存在,但未配置审批人 - if (CollUtil.isEmpty(assignees)){ + if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } - // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); + // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖? variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } @@ -1094,6 +1088,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @SuppressWarnings("DataFlowIssue") public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { // 1.1 校验 task 可以被减签 Task task = validateTaskCanSignDelete(reqVO.getId()); From 40b3c49495ddda45f2afe537c8b478bac7d4a9bc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Mar 2025 08:01:23 +0800 Subject: [PATCH 100/116] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=20=E5=90=8C=E4=B8=80=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=9F=A5=E8=AF=A2=E4=BC=A0=E9=80=92=E7=9A=84?= =?UTF-8?q?=20taskId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskService.java | 10 +++--- .../bpm/service/task/BpmTaskServiceImpl.java | 32 +++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index d95e226d59..e99d974356 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -35,16 +35,16 @@ public interface BpmTaskService { PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); /** - * 获得用户(待办)的任务。 - * 1、根据 id 查询待办任务 - * 2、如果任务不存在,获取指定流程下,首个需要处理任务 + * 获得用户(待办)的任务: + * 1. 根据 taskId 查询待办任务 + * 2. 如果任务不存在(或者已审核),获取指定流程下,首个需要处理任务 * * @param userId 用户编号 - * @param id 任务编号 + * @param taskId 任务编号 * @param processInstanceId 流程实例编号 * @return 待办任务 */ - BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId); + BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId); /** * 获得已办的流程任务分页 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 11f219850b..e26b58e793 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -137,18 +137,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId) { + public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) { // 1.1 获取指定的用户待办任务 - Task todoTask = getMyTodoTask(userId, id); + Task todoTask = getMyTodoTask(userId, taskId); + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 if (todoTask == null) { - // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 - todoTask = getFirstMyTodoTask(userId, processInstanceId); + todoTask = getMyFirstTodoTask(userId, processInstanceId); } if (todoTask == null) { return null; } - // 2.查询该任务的子任务 + // 2. 查询该任务的子任务 List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); // 3. 转换返回 @@ -169,11 +169,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } - private Task getMyTodoTask(Long userId, String id) { - if (StrUtil.isEmpty(id)) { + /** + * 获得用户指定 taskId 任务编号的“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @return 任务 + */ + private Task getMyTodoTask(Long userId, String taskId) { + if (StrUtil.isEmpty(taskId)) { return null; } - Task task = getTask(id); + Task task = getTask(taskId); if (task == null) { return null; } @@ -183,7 +190,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { return task; } - private Task getFirstMyTodoTask(Long userId, String processInstanceId) { + /** + * 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param processInstanceId 流程编号 + * @return 任务 + */ + private Task getMyFirstTodoTask(Long userId, String processInstanceId) { if (processInstanceId == null) { return null; } From e5c8be2b053e062fdb884505ad1eac27e358a7ae Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Fri, 7 Mar 2025 22:53:35 +0800 Subject: [PATCH 101/116] =?UTF-8?q?review:=20=E4=BC=98=E5=8C=96TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 7a8e5a2f5a..713b28945c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -221,8 +221,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processDefinitionInfo, processVariables, activities); // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 - // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } From 4054e5fdec80231d955290e16c8f73d0c4071af2 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 9 Mar 2025 22:28:13 +0800 Subject: [PATCH 102/116] =?UTF-8?q?fix:=20=E5=8F=91=E8=B5=B7=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E6=8A=A5=E9=94=99=EF=BC=8C=E8=BF=94=E5=9B=9E=E7=9A=84?= =?UTF-8?q?VO=E7=BC=BA=E5=B0=91=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 6 ++++++ .../vo/process/BpmProcessDefinitionRespVO.java | 9 ++------- .../convert/definition/BpmProcessDefinitionConvert.java | 4 ++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index e00b6d4d3b..a54a5ac716 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -44,6 +44,12 @@ public class BpmModelMetaInfoVO { private Integer formType; @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 8da290440e..71709f7fe8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - 流程定义 Response VO") @Data @@ -19,7 +20,7 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "easy-build") private String key; @Schema(description = "流程分类", example = "1") @@ -27,15 +28,9 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程分类名字", example = "请假") private String categoryName; - @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") - private String modelId; - @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java index 1ef8b6f058..d1a5e5a74f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -74,6 +74,10 @@ public interface BpmProcessDefinitionConvert { if (deployment != null) { respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); } + // 图标为null时,处理成空字符串,否则copyTo的to.setIcon( from.getIcon() );会报错 + if (respVO.getIcon() == null) { + respVO.setIcon(""); + } // BpmProcessDefinitionInfoDO if (processDefinitionInfo != null) { copyTo(processDefinitionInfo, respVO); From b6a9b5dda9f518b2cdc180759157b0562cb564a4 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 10:22:21 +0800 Subject: [PATCH 103/116] =?UTF-8?q?fix:=20=E6=A0=A1=E9=AA=8C=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7=E4=BB=BB=E5=8A=A1=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E4=B8=BA=E2=80=9C=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/BpmModelServiceImpl.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 922013ddba..c7df92a27f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -40,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -243,24 +244,19 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况 - List outgoingFlows = startEvent.getOutgoingFlows(); - // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。 - // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务??? - if (CollUtil.isNotEmpty(outgoingFlows)) { - FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); - Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); - if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName()); - } - } - // 3. 校验 UserTask 的 name 都配置了 + // 2. 校验 UserTask 的 name 都配置了 List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); userTasks.forEach(userTask -> { if (StrUtil.isEmpty(userTask.getName())) { throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); } }); + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” + UserTask userTask = userTasks.get(0); + Integer candidateStrategy = parseCandidateStrategy(userTask); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTask.getName()); + } } @Override From 26dd8b66702230129ba48a6d87bcda399c06a974 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 17:11:41 +0800 Subject: [PATCH 104/116] =?UTF-8?q?fix=EF=BC=9A=E8=8E=B7=E5=8F=96=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=E6=97=B6=EF=BC=8C=E8=BF=94=E5=9B=9Enew=20Has?= =?UTF-8?q?hMap<>()=EF=BC=8C=E9=81=BF=E5=85=8D=E4=B8=8B=E7=BA=A7=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=A9=BA=E6=8C=87=E9=92=88=20review:=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=AE=A1=E6=89=B9=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=90=88=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/FlowableUtils.java | 6 +- .../bpm/service/task/BpmTaskServiceImpl.java | 65 +++++++++---------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index fe83495e35..4178638b5a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/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 { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -214,12 +214,12 @@ public class FlowableUtils { * 获得流程实例的审批用户选择的下一个节点的审批人 Map * * @param processVariables 流程变量 - * @return 审批用户选择的下一个节点的审批人Map Map + * @return 审批用户选择的下一个节点的审批人Map */ @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index e26b58e793..09eea1e52c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -558,18 +558,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 - // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - if (CollUtil.isNotEmpty(reqVO.getVariables())) { - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), - bpmnModel, reqVO.getNextAssignees(), instance); - // 完成任务 - runtimeService.setVariables(task.getProcessInstanceId(), variables); - taskService.complete(task.getId(), variables, true); - } else { - // 完成任务 - taskService.complete(task.getId()); - } + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + bpmnModel, reqVO.getNextAssignees(), instance); + // 完成任务 + runtimeService.setVariables(task.getProcessInstanceId(), variables); + taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); @@ -590,51 +584,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { - // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 - // TODO @小北:会出现漏选,其实需要的情况哇? - if (CollUtil.isEmpty(nextAssignees)) { - return variables; - } // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 2. 循环下一个将要执行的节点集合 - Map> processVariables = new HashMap<>(); + Map> processVariables; for (FlowNode nextFlowNode : nextFlowNodes) { - if (!nextAssignees.containsKey(nextFlowNode.getId())) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); - } - List assignees = nextAssignees.get(nextFlowNode.getId()); - // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + // 获取任务节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null) { - processVariables = new HashMap<>(); - } - List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) { - continue; - } // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isNotEmpty(processVariables)) { + List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) { + continue; + } + } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), assignees); + processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isNotEmpty(processVariables)) { + List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { + continue; + } + } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), assignees); - // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖? + processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } From 494b80d1ebae4099d719f671a4cdfe91313f5f16 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 17:34:13 +0800 Subject: [PATCH 105/116] =?UTF-8?q?review:=20=E4=BC=98=E5=8C=96=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF=E5=90=A6=E5=90=88?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 13 ++++++++++--- .../framework/flowable/core/util/FlowableUtils.java | 6 +++--- .../module/bpm/service/task/BpmTaskServiceImpl.java | 8 ++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index af5b0852f6..c471826306 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -907,9 +907,16 @@ public class BpmnModelUtils { * @return 符合条件的路径 */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, + SequenceFlow matchSequenceFlow; + if (CollUtil.isNotEmpty(variables)){ + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + }else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 4178638b5a..fe83495e35 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/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 { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return new HashMap<>(); + return null; } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -214,12 +214,12 @@ public class FlowableUtils { * 获得流程实例的审批用户选择的下一个节点的审批人 Map * * @param processVariables 流程变量 - * @return 审批用户选择的下一个节点的审批人Map + * @return 审批用户选择的下一个节点的审批人Map Map */ @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return new HashMap<>(); + return null; } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 09eea1e52c..0bfd6b0c6c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -600,7 +600,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isNotEmpty(processVariables)) { + if (processVariables == null){ + processVariables = new HashMap<>(); + }else { List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { @@ -619,7 +621,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isNotEmpty(processVariables)) { + if (processVariables == null){ + processVariables = new HashMap<>(); + }else { List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { From 27ae2a4761739a21feca8bad24544fadeff9d227 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 21:23:19 +0800 Subject: [PATCH 106/116] =?UTF-8?q?review:=20=E7=88=B6=E5=AD=90=E7=B1=BB?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 3 +-- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 3 +++ .../definition/vo/process/BpmProcessDefinitionRespVO.java | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index a54a5ac716..97a8a3ca7b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -44,8 +44,7 @@ public class BpmModelMetaInfoVO { private Integer formType; @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 - @Schema(description = "表单名字", example = "请假表单") - private String formName; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) private String formConf; @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index 7e32596524..bcb193df1e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -21,6 +21,9 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @NotEmpty(message = "流程名称不能为空") private String name; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "流程分类", example = "1") private String category; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 71709f7fe8..81bddf7b11 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -20,7 +20,10 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "easy-build") + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") private String key; @Schema(description = "流程分类", example = "1") @@ -31,6 +34,9 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 From 2123d7c06717113a83aaf9fccf70b71559f8ad0a Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 21:28:29 +0800 Subject: [PATCH 107/116] =?UTF-8?q?remove:=20=E5=88=A0=E9=99=A4=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/convert/definition/BpmProcessDefinitionConvert.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java index d1a5e5a74f..1ef8b6f058 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -74,10 +74,6 @@ public interface BpmProcessDefinitionConvert { if (deployment != null) { respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); } - // 图标为null时,处理成空字符串,否则copyTo的to.setIcon( from.getIcon() );会报错 - if (respVO.getIcon() == null) { - respVO.setIcon(""); - } // BpmProcessDefinitionInfoDO if (processDefinitionInfo != null) { copyTo(processDefinitionInfo, respVO); From cc61bb1a61e837ac62dee76045d3cd77ed4f303b Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 22:37:23 +0800 Subject: [PATCH 108/116] =?UTF-8?q?review=EF=BC=9A=20=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=AE=BE=E8=AE=A1=E5=99=A8=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E7=94=A8=E6=88=B7=E4=BB=BB=E5=8A=A1=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=E2=80=9C=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/BpmModelServiceImpl.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index c7df92a27f..7a737d953a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -40,10 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; @@ -210,11 +207,11 @@ public class BpmModelServiceImpl implements BpmModelService { public void deployModel(Long userId, String id) { // 1.1 校验流程模型存在 Model model = validateModelManager(id, userId); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); // 1.2 校验流程图 byte[] bpmnBytes = getModelBpmnXML(model.getId()); - validateBpmnXml(bpmnBytes); + validateBpmnXml(bpmnBytes, metaInfo.getType()); // 1.3 校验表单已配 - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); BpmFormDO form = validateFormConfig(metaInfo); // 1.4 校验任务分配规则已配置 taskCandidateInvoker.validateBpmnConfig(bpmnBytes); @@ -234,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService { repositoryService.saveModel(model); } - private void validateBpmnXml(byte[] bpmnBytes) { + private void validateBpmnXml(byte[] bpmnBytes, int type) { BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); if (bpmnModel == null) { throw exception(MODEL_NOT_EXISTS); @@ -252,10 +249,14 @@ public class BpmModelServiceImpl implements BpmModelService { } }); // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” - UserTask userTask = userTasks.get(0); - Integer candidateStrategy = parseCandidateStrategy(userTask); + Map userTaskMap = new HashMap<>(); + // BPMN 设计器,校验第一个用户任务节点 + userTaskMap.put(BpmModelTypeEnum.BPMN.getType(), userTasks.get(0)); + // SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 + userTaskMap.put(BpmModelTypeEnum.SIMPLE.getType(), userTasks.get(1)); + Integer candidateStrategy = parseCandidateStrategy(userTaskMap.get(type)); if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTask.getName()); + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTaskMap.get(type).getName()); } } From 42eb89b2433b8197602fb5441078d50c40fbc44d Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 22:40:53 +0800 Subject: [PATCH 109/116] =?UTF-8?q?review=EF=BC=9A=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=A0=A1=E9=AA=8C=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF=E5=90=A6=E5=90=88?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 0bfd6b0c6c..eab70f921b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -610,7 +610,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 @@ -631,7 +631,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } From 9ae18fe53d68ed2331ff9d31f28874ca7db81282 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 06:25:16 +0800 Subject: [PATCH 110/116] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91BPM=EF=BC=9AformConf=E3=80=81formFields=20?= =?UTF-8?q?=E5=9C=A8=20BpmProcessDefinitionRespVO=20=E7=BC=BA=E5=B0=91?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=8F=91=E8=B5=B7=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 4 ---- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 3 --- .../vo/process/BpmProcessDefinitionRespVO.java | 10 +++++++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 97a8a3ca7b..842f396d47 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/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 { @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index bcb193df1e..7e32596524 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/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 { @NotEmpty(message = "流程名称不能为空") private String name; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "流程分类", example = "1") private String category; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 81bddf7b11..0ec737174a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/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 { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") private String key; @@ -37,6 +34,13 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") private String modelId; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 From 24261cf767fea0e71d813e590e12d3debb7b97db Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 06:44:41 +0800 Subject: [PATCH 111/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E8=AE=A1=E7=AE=97=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA=EF=BC=88=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 5 +-- .../flowable/core/util/FlowableUtils.java | 4 +-- .../definition/BpmModelServiceImpl.java | 3 +- .../bpm/service/task/BpmTaskServiceImpl.java | 34 +++++++++---------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index c471826306..2ec26b407e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -908,12 +908,13 @@ public class BpmnModelUtils { */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, + // TODO @小北:是不是还是保证,编辑的时候,如果计算下一个节点,还是 variables 是完整体?而不是空的!!!(可以微信讨论下) SequenceFlow matchSequenceFlow; - if (CollUtil.isNotEmpty(variables)){ + if (CollUtil.isNotEmpty(variables)) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - }else { + } else { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index fe83495e35..67c24bb9f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/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 { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -219,7 +219,7 @@ public class FlowableUtils { @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 7a737d953a..6f3162683f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/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 { repositoryService.saveModel(model); } - private void validateBpmnXml(byte[] bpmnBytes, int type) { + private void validateBpmnXml(byte[] bpmnBytes, Integer type) { BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); if (bpmnModel == null) { throw exception(MODEL_NOT_EXISTS); @@ -248,6 +248,7 @@ public class BpmModelServiceImpl implements BpmModelService { throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); } }); + // TODO @小北:是不是可以 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);然后,最好判空。。。极端情况下,没 usertask ,哈哈哈哈。 // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” Map userTaskMap = new HashMap<>(); // BPMN 设计器,校验第一个用户任务节点 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index eab70f921b..f96d0cb392 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -557,12 +557,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 2.3 调用 BPM complete 去完成任务 - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + // 2.3 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 完成任务 runtimeService.setVariables(task.getProcessInstanceId(), variables); + // 2.4 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 @@ -587,10 +586,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 2. 循环下一个将要执行的节点集合 + + // 2. 校验选择的下一个节点的审批人,是否合法 Map> processVariables; for (FlowNode nextFlowNode : nextFlowNodes) { - // 获取任务节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); // 2.1 情况一:如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { @@ -600,16 +599,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null){ - processVariables = new HashMap<>(); - }else { - List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) { - continue; - } + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + // TODO @小北:【不用改】通过 if return,让逻辑更简洁一点;虽然会多判断一次 processVariables,但是 if else 层级更少。 + if (processVariables != null + && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) { + continue; + } + // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES + if (processVariables == null) { + processVariables = new HashMap<>(); } - // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } @@ -621,16 +620,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null){ + if (processVariables == null) { processVariables = new HashMap<>(); - }else { + } else { List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + // TODO @小北:这种,应该可以覆盖呢。 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { continue; } } - // 校验通过的全部节点和审批人 + // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } From e114a354513140820e3d9add68d22804217a54a6 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Mar 2025 09:29:58 +0800 Subject: [PATCH 112/116] =?UTF-8?q?feat:=20=E6=B5=81=E7=A8=8B=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=BD=AE=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/BpmModelMetaInfoVO.java | 36 ++++ .../BpmProcessDefinitionInfoDO.java | 12 ++ .../BpmProcessInstanceEventListener.java | 6 + .../core/util/BpmHttpRequestUtils.java | 158 ++++++++++++++++++ .../flowable/core/util/SimpleModelUtils.java | 24 --- .../task/BpmProcessInstanceService.java | 6 + .../task/BpmProcessInstanceServiceImpl.java | 95 ++++++++--- .../task/listener/BpmUserTaskListener.java | 78 ++++----- .../http/BpmAbstractHttpRequestTrigger.java | 56 ------- .../trigger/http/BpmHttpCallbackTrigger.java | 28 ++-- .../http/BpmSyncHttpRequestTrigger.java | 73 ++------ 11 files changed, 348 insertions(+), 224 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 842f396d47..9fc52fdc0c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; +import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; @@ -80,6 +82,12 @@ public class BpmModelMetaInfoVO { @Schema(description = "摘要设置", example = "{}") private SummarySetting summarySetting; + @Schema(description = "流程前置通知设置", example = "{}") + private HttpRequestSetting PreProcessNotifySetting; + + @Schema(description = "流程后置通知设置", example = "{}") + private HttpRequestSetting PostProcessNotifySetting; + @Schema(description = "流程 ID 规则") @Data @Valid @@ -132,4 +140,32 @@ public class BpmModelMetaInfoVO { } + @Schema(description = "http 请求通知设置", example = "{}") + @Data + public static class HttpRequestSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index 34dfbece3a..cc4c697e6f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -189,4 +189,16 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.SummarySetting summarySetting; + /** + * 流程前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting; + + /** + * 流程后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java index 5251aea64f..ba2aaa6bcb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -22,6 +22,7 @@ import java.util.Set; public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CREATED) .add(FlowableEngineEventType.PROCESS_COMPLETED) .add(FlowableEngineEventType.PROCESS_CANCELLED) .build(); @@ -34,6 +35,11 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent super(PROCESS_INSTANCE_EVENTS); } + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); + } + @Override protected void processCompleted(FlowableEngineEntityEvent event) { processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java new file mode 100644 index 0000000000..74a8da09f5 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -0,0 +1,158 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; + +/** + * 工作流发起 HTTP 请求工具类 + * + * @author 芋道源码 + */ +@Slf4j +public class BpmHttpRequestUtils { + + public static void executeBpmHttpRequest(ProcessInstance processInstance, + String url, + List headerParam, + List bodyParam, + Boolean handleResponse, + List> response, + RestTemplate restTemplate, + BpmProcessInstanceService processInstanceService) { + + // 1.1 设置请求头 + MultiValueMap headers = BpmHttpRequestUtils.buildHttpHeaders(processInstance, headerParam); + // 1.2 设置请求体 + MultiValueMap body = BpmHttpRequestUtils.buildHttpBody(processInstance, bodyParam); + + // 2. 发起请求 + ResponseEntity responseEntity = BpmHttpRequestUtils.sendHttpRequest(url, headers, body, restTemplate); + + // 3. 处理返回 + if (Boolean.TRUE.equals(handleResponse)) { + // 3.1 判断是否需要解析返回值 + if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) + || !responseEntity.getStatusCode().is2xxSuccessful() + || CollUtil.isEmpty(response)) { + return; + } + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 + CommonResult> respResult = JsonUtils.parseObjectQuietly( + responseEntity.getBody(), new TypeReference<>() { + }); + if (respResult == null || !respResult.isSuccess()) { + return; + } + // 3.3 获取需要更新的流程变量 + Map updateVariables = BpmHttpRequestUtils.getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + // 3.4 更新流程变量 + if (CollUtil.isNotEmpty(updateVariables)) { + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); + } + } + + } + + public static ResponseEntity sendHttpRequest(String url, + MultiValueMap headers, + MultiValueMap body, + RestTemplate restTemplate) { + // 3. 发起请求 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); + } + return responseEntity; + } + + public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + addHttpRequestParam(headers, headerSettings, processVariables); + return headers; + } + + public static MultiValueMap buildHttpBody(ProcessInstance processInstance, + List bodySettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap body = new LinkedMultiValueMap<>(); + addHttpRequestParam(body, bodySettings, processVariables); + body.add("processInstanceId", processInstance.getId()); + return body; + } + + /** + * 从请求返回值获取需要更新的流程变量 + * + * @param result 请求返回结果 + * @param responseSettings 返回设置 + * @return 需要更新的流程变量 + */ + public static Map getNeedUpdatedVariablesFromResponse(Map result, + List> responseSettings) { + Map updateVariables = new HashMap<>(); + if (CollUtil.isEmpty(result)) { + return updateVariables; + } + responseSettings.forEach(responseSetting -> { + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); + } + }); + return updateVariables; + } + + /** + * 添加 HTTP 请求参数。请求头或者请求体 + * + * @param params HTTP 请求参数 + * @param paramSettings HTTP 请求参数设置 + * @param processVariables 流程变量 + */ + public static void addHttpRequestParam(MultiValueMap params, + List paramSettings, + Map processVariables) { + if (CollUtil.isEmpty(paramSettings)) { + return; + } + paramSettings.forEach(item -> { + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { + params.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { + params.add(item.getKey(), processVariables.get(item.getValue()).toString()); + } + }); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 0060d0e9c5..3b5bad52c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -21,7 +21,6 @@ import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.TaskListener; -import org.springframework.util.MultiValueMap; import java.util.*; @@ -1002,27 +1001,4 @@ public class SimpleModelUtils { return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); } - // TODO @芋艿:【高】要不要优化下,抽个 HttpUtils - - /** - * 添加 HTTP 请求参数。请求头或者请求体 - * - * @param params HTTP 请求参数 - * @param paramSettings HTTP 请求参数设置 - * @param processVariables 流程变量 - */ - public static void addHttpRequestParam(MultiValueMap params, - List paramSettings, - Map processVariables) { - if (CollUtil.isEmpty(paramSettings)) { - return; - } - paramSettings.forEach(item -> { - if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { - params.add(item.getKey(), item.getValue()); - } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { - params.add(item.getKey(), processVariables.get(item.getValue()).toString()); - } - }); - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 6c13416ef4..abba2245e2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -182,4 +182,10 @@ public interface BpmProcessInstanceService { */ void processProcessInstanceCompleted(ProcessInstance instance); + /** + * 处理 ProcessInstance 开始事件,例如说:流程前置通知 + * + * @param instance 流程任务 + */ + void processProcessInstanceCreated(ProcessInstance instance); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 713b28945c..4ca5a189ff 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -34,6 +34,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; @@ -62,6 +63,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import org.springframework.web.client.RestTemplate; import java.util.*; @@ -120,6 +122,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Resource private BpmProcessIdRedisDAO processIdRedisDAO; + @Resource + private RestTemplate restTemplate; + // ========== Query 查询相关方法 ========== @Override @@ -148,7 +153,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) @@ -351,15 +356,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -381,9 +386,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -451,11 +456,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -516,9 +521,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -540,8 +545,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -585,8 +590,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } @@ -902,6 +907,46 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); + + // 4. 流程后置通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getPostProcessNotifySetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPostProcessNotifySetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + this); + } + } + }); + } + + @Override + public void processProcessInstanceCreated(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 流程前置通知 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + this); + } }); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java index e89587d471..d02844353d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -1,29 +1,20 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.delegate.TaskListener; -import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.service.delegate.DelegateTask; import org.springframework.context.annotation.Scope; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; -import java.util.Map; - -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; /** @@ -50,46 +41,35 @@ public class BpmUserTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { // 1. 获取所需基础信息 - HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId()); + ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId()); BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); - // 2. 获取请求头和请求体 - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - MultiValueMap body = new LinkedMultiValueMap<>(); - SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables); - SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables); - // 2.1 请求头默认参数 - if (StrUtil.isNotEmpty(delegateTask.getTenantId())) { - headers.add(HEADER_TENANT_ID, delegateTask.getTenantId()); - } - // 2.2 请求体默认参数 + // 2. 发起请求 // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; - body.add("processInstanceId", delegateTask.getProcessInstanceId()); - body.add("assignee", delegateTask.getAssignee()); - body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey()); - body.add("taskId", delegateTask.getId()); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getAssignee())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getId())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + listenerHandler.getPath(), + listenerHandler.getHeader(), + listenerHandler.getBody(), + false, null, + restTemplate, + processInstanceService); - // 3. 异步发起请求 - // TODO @芋艿:确认要同步,还是异步 - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - try { - ResponseEntity responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST, - requestEntity, String.class); - log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]", - DELEGATE_EXPRESSION, - delegateTask.getEventName(), - headers, - body, - responseEntity); - } catch (RestClientException e) { - log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]", - DELEGATE_EXPRESSION, - delegateTask.getEventName(), - headers, - body, - e.getMessage()); - } - // 4. 是否需要后续操作?TODO 芋艿:待定! + // 3. 是否需要后续操作?TODO 芋艿:待定! } } \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java index 0e5e7276f7..b1d81bc146 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java @@ -1,25 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; -import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; /** * BPM 发送 HTTP 请求触发器抽象类 @@ -29,42 +11,4 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTA @Slf4j public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { - @Resource - private RestTemplate restTemplate; - - protected ResponseEntity sendHttpRequest(String url, - MultiValueMap headers, - MultiValueMap body) { - // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢? - // 3. 发起请求 - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity responseEntity; - try { - responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); - } catch (RestClientException e) { - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); - } - return responseEntity; - } - - protected MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); - SimpleModelUtils.addHttpRequestParam(headers, headerSettings, processVariables); - return headers; - } - - protected MultiValueMap buildHttpBody(ProcessInstance processInstance, - List bodySettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap body = new LinkedMultiValueMap<>(); - SimpleModelUtils.addHttpRequestParam(body, bodySettings, processVariables); - body.add("processInstanceId", processInstance.getId()); - return body; - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index 867af70b9d..fa50cf3917 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -2,13 +2,15 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; -import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; /** * BPM HTTP 回调触发器 @@ -19,6 +21,9 @@ import org.springframework.util.MultiValueMap; @Slf4j public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { + @Resource + private RestTemplate restTemplate; + @Resource private BpmProcessInstanceService processInstanceService; @@ -36,16 +41,19 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } - - // 2.1 设置请求头 + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); - // 2.2 设置请求体 - MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 - body.add("taskDefineKey", setting.getCallbackTaskDefineKey()); - - // 3. 发起请求 - sendHttpRequest(setting.getUrl(), headers, body); + setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() + .setKey("taskDefineKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(setting.getCallbackTaskDefineKey())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + false, null, + restTemplate, + processInstanceService); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index 4a506f3b13..cc377fa1f6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -1,24 +1,15 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.util.MultiValueMap; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import org.springframework.web.client.RestTemplate; /** * BPM 发送同步 HTTP 请求触发器 @@ -29,6 +20,9 @@ import java.util.Map; @Slf4j public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { + @Resource + private RestTemplate restTemplate; + @Resource private BpmProcessInstanceService processInstanceService; @@ -45,56 +39,15 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); return; } - - // 2.1 设置请求头 + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); - // 2.2 设置请求体 - MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - - // 3. 发起请求 - ResponseEntity responseEntity = sendHttpRequest(setting.getUrl(), headers, body); - - // 4.1 判断是否需要解析返回值 - if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) - || !responseEntity.getStatusCode().is2xxSuccessful() - || CollUtil.isEmpty(setting.getResponse())) { - return; - } - // 4.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly( - responseEntity.getBody(), new TypeReference<>() { - }); - if (respResult == null || !respResult.isSuccess()) { - return; - } - // 4.3 获取需要更新的流程变量 - Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse()); - // 4.4 更新流程变量 - if (CollUtil.isNotEmpty(updateVariables)) { - processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables); - } - } - - /** - * 从请求返回值获取需要更新的流程变量 - * - * @param result 请求返回结果 - * @param responseSettings 返回设置 - * @return 需要更新的流程变量 - */ - private Map getNeedUpdatedVariablesFromResponse(Map result, - List> responseSettings) { - Map updateVariables = new HashMap<>(); - if (CollUtil.isEmpty(result)) { - return updateVariables; - } - responseSettings.forEach(responseSetting -> { - if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { - updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); - } - }); - return updateVariables; + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + processInstanceService); } } From 4364ef09c533e8f5c336fc48bf5653a88ff18189 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 08:24:57 +0800 Subject: [PATCH 113/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E6=B5=81=E7=A8=8B=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=BD=AE=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/BpmModelMetaInfoVO.java | 2 ++ .../BpmProcessDefinitionInfoDO.java | 7 ++--- .../core/util/BpmHttpRequestUtils.java | 31 ++++++++++--------- .../task/BpmProcessInstanceServiceImpl.java | 3 +- .../task/listener/BpmUserTaskListener.java | 24 +++++--------- .../trigger/http/BpmHttpCallbackTrigger.java | 8 ++--- .../http/BpmSyncHttpRequestTrigger.java | 1 + 7 files changed, 36 insertions(+), 40 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 9fc52fdc0c..254a5178f0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -82,9 +82,11 @@ public class BpmModelMetaInfoVO { @Schema(description = "摘要设置", example = "{}") private SummarySetting summarySetting; + // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 @Schema(description = "流程前置通知设置", example = "{}") private HttpRequestSetting PreProcessNotifySetting; + // TODO @lesan:processAfterTriggerSetting @Schema(description = "流程后置通知设置", example = "{}") private HttpRequestSetting PostProcessNotifySetting; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index cc4c697e6f..549889e3aa 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -182,23 +182,22 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.TitleSetting titleSetting; - /** * 摘要设置 */ @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.SummarySetting summarySetting; + // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 /** * 流程前置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting; - /** * 流程后置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index 74a8da09f5..7f4428aac3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -37,51 +37,52 @@ public class BpmHttpRequestUtils { public static void executeBpmHttpRequest(ProcessInstance processInstance, String url, - List headerParam, - List bodyParam, + List headerParams, + List bodyParams, Boolean handleResponse, List> response, + // TODO @lesan:RestTemplate 直接通过 springUtil 获取好咧; RestTemplate restTemplate, + // TODO @lesan:processInstanceService 直接通过 springUtil 获取好咧; BpmProcessInstanceService processInstanceService) { // 1.1 设置请求头 - MultiValueMap headers = BpmHttpRequestUtils.buildHttpHeaders(processInstance, headerParam); + MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); // 1.2 设置请求体 - MultiValueMap body = BpmHttpRequestUtils.buildHttpBody(processInstance, bodyParam); + MultiValueMap body = buildHttpBody(processInstance, bodyParams); // 2. 发起请求 - ResponseEntity responseEntity = BpmHttpRequestUtils.sendHttpRequest(url, headers, body, restTemplate); + ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); // 3. 处理返回 + // TODO @lesan:可以用 if return,让括号小点 if (Boolean.TRUE.equals(handleResponse)) { // 3.1 判断是否需要解析返回值 - if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) + if (responseEntity == null + || StrUtil.isEmpty(responseEntity.getBody()) || !responseEntity.getStatusCode().is2xxSuccessful() || CollUtil.isEmpty(response)) { return; } // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly( - responseEntity.getBody(), new TypeReference<>() { - }); + CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), + new TypeReference<>() {}); if (respResult == null || !respResult.isSuccess()) { return; } // 3.3 获取需要更新的流程变量 - Map updateVariables = BpmHttpRequestUtils.getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); // 3.4 更新流程变量 if (CollUtil.isNotEmpty(updateVariables)) { processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); } } - } public static ResponseEntity sendHttpRequest(String url, MultiValueMap headers, MultiValueMap body, RestTemplate restTemplate) { - // 3. 发起请求 HttpEntity> requestEntity = new HttpEntity<>(body, headers); ResponseEntity responseEntity; try { @@ -95,7 +96,7 @@ public class BpmHttpRequestUtils { } public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { + List headerSettings) { Map processVariables = processInstance.getProcessVariables(); MultiValueMap headers = new LinkedMultiValueMap<>(); headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); @@ -104,7 +105,7 @@ public class BpmHttpRequestUtils { } public static MultiValueMap buildHttpBody(ProcessInstance processInstance, - List bodySettings) { + List bodySettings) { Map processVariables = processInstance.getProcessVariables(); MultiValueMap body = new LinkedMultiValueMap<>(); addHttpRequestParam(body, bodySettings, processVariables); @@ -120,7 +121,7 @@ public class BpmHttpRequestUtils { * @return 需要更新的流程变量 */ public static Map getNeedUpdatedVariablesFromResponse(Map result, - List> responseSettings) { + List> responseSettings) { Map updateVariables = new HashMap<>(); if (CollUtil.isEmpty(result)) { return updateVariables; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 4ca5a189ff..64abcef42e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -935,10 +935,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 流程前置通知 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. getProcessDefinitionInfo(instance.getProcessDefinitionId()); + // TODO @lesan:if return 哈。减少括号。 if (ObjUtil.isNotNull(processDefinitionInfo) && ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) { BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(instance, setting.getUrl(), setting.getHeader(), @@ -949,4 +949,5 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } }); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java index d02844353d..6048423c91 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -46,22 +46,14 @@ public class BpmUserTaskListener implements TaskListener { // 2. 发起请求 // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getProcessInstanceId())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getAssignee())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getTaskDefinitionKey())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, listenerHandler.getPath(), listenerHandler.getHeader(), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index fa50cf3917..ab1f54abd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -41,13 +41,12 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() - .setKey("taskDefineKey") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(setting.getCallbackTaskDefineKey())); + .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, setting.getUrl(), setting.getHeader(), @@ -56,4 +55,5 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { restTemplate, processInstanceService); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index cc377fa1f6..6c74b066f0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -39,6 +39,7 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); return; } + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, From 045fb584d25b0109c31e8822c6f567d4fa6756b3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 11:21:29 +0800 Subject: [PATCH 114/116] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91BPM=EF=BC=9A=E5=AD=90=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E6=83=B3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/BpmProcessInstanceController.java | 1 + .../flowable/core/candidate/BpmTaskCandidateInvoker.java | 7 ++++++- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 8a3777772e..9316261678 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -148,6 +148,7 @@ public class BpmProcessInstanceController { processDefinition, processDefinitionInfo, startUser, dept)); } + // TODO @lesan:【子流程】子流程如果取消,主流程应该是通过、还是不通过哈?还是禁用掉子流程的取消? @DeleteMapping("/cancel-by-start-user") @Operation(summary = "用户取消流程实例", description = "取消发起的流程") @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java index 952f0f1bea..7f66b29d3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.CallActivity; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; @@ -129,8 +130,12 @@ public class BpmTaskCandidateInvoker { public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, Long startUserId, String processDefinitionId, Map processVariables) { - // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + // 如果是 CallActivity 子流程,不进行计算候选人 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + if (flowElement instanceof CallActivity) { + return new HashSet<>(); + } + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 Integer approveType = BpmnModelUtils.parseApproveType(flowElement); if (ObjectUtils.equalsAny(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 64abcef42e..1bd568c8e5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -390,9 +390,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask - // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities - // 的话,它无法成为一个节点 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会导致timeline不会展示已结束的子流程 + // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程; + // TODO @lesan:【子流程】基于 activities 查询出 usertask、callactivity,然后拼接?如果是子流程,就是可以点击过去? List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -468,6 +468,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance::getActivityId); // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 + // TODO @lesan:【子流程】在子流程进行审批的时候,HistoricActivityInstance 里面可以拿到 runActivities.get(0).getCalledProcessInstanceId()。要不要支持跳转??? Map taskMap = convertMap(tasks, HistoricTaskInstance::getId); return convertList(runningTaskMap.entrySet(), entry -> { String activityId = entry.getKey(); From 71216a50bf933d92846361851634ce2540ec2297 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 13:15:36 +0800 Subject: [PATCH 115/116] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README=EF=BC=9A?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=88=E5=A2=9E=E5=8A=A0=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=EF=BC=89=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 935c0caad8..580fabbf76 100644 --- a/README.md +++ b/README.md @@ -149,22 +149,45 @@ ### 工作流程 -| | 功能 | 描述 | -|----|-------|-----------------------------------------| -| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 | -| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 | -| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 | -| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 | -| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 | -| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 | -| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | +![BPM 功能列表](/img/common/bpm-feature.png) -![功能图](/.image/common/bpm-feature.png) +基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作: | BPMN 设计器 | 钉钉/飞书设计器 | |------------------------------|--------------------------------| | ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) | +> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!! +> +> 前者支持轻量配置简单流程,后者实现复杂场景深度编排 + +| 功能列表 | 功能描述 | 是否完成 | +|------------|-------------------------------------------------------------------------------------|------| +| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 | ✅ | +| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ | +| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ | +| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ | +| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ | +| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ | +| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ | +| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 | ✅ | +| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 | ✅ | +| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ | +| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ | +| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ | +| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ | +| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ | +| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ | +| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ | +| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ | +| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ | +| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ | +| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ | +| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ | +| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ | +| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ | +| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ | + ### 支付系统 | | 功能 | 描述 | From a193322373d5b833b028501f96a69d3ec76330f4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 13:16:31 +0800 Subject: [PATCH 116/116] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README=EF=BC=9A?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=88=E5=A2=9E=E5=8A=A0=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=EF=BC=89=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 580fabbf76..f326bcadf4 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ ### 工作流程 -![BPM 功能列表](/img/common/bpm-feature.png) +![功能图](/.image/common/bpm-feature.png) 基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作: