From 8a8dc67d72c95ad1bc0d411e319e48014fe3a5d7 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Wed, 22 Jan 2025 22:18:37 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=91=E6=96=B0=E5=A2=9ESimple=20=E8=AE=BE=E8=AE=A1=E5=99=A8?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=99=A8=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...va => BpmHttpRequestParamSettingType.java} | 6 +- .../definition/BpmSimpleModelNodeType.java | 1 + .../bpm/enums/definition/BpmTriggerType.java | 41 +++++++++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 72 ++++++++++++--- .../core/enums/BpmnModelConstants.java | 10 ++ .../core/listener/BpmTriggerTaskDelegate.java | 54 +++++++++++ .../flowable/core/util/BpmnModelUtils.java | 18 +++- .../flowable/core/util/SimpleModelUtils.java | 92 ++++++++++++++----- .../task/listener/BpmUserTaskListener.java | 24 +---- .../task/trigger/BpmHttpRequestTrigger.java | 73 +++++++++++++++ .../bpm/service/task/trigger/BpmTrigger.java | 28 ++++++ 11 files changed, 354 insertions(+), 65 deletions(-) rename yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/{BpmListenerParamTypeEnum.java => BpmHttpRequestParamSettingType.java} (69%) create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmListenerParamTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmHttpRequestParamSettingType.java similarity index 69% rename from yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmListenerParamTypeEnum.java rename to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmHttpRequestParamSettingType.java index 53b08c9e29..81b0ac5939 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmListenerParamTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmHttpRequestParamSettingType.java @@ -7,13 +7,13 @@ import lombok.Getter; import java.util.Arrays; /** - * BPM 任务监听器键值对类型 + * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。 * * @author Lesan */ @Getter @AllArgsConstructor -public enum BpmListenerParamTypeEnum implements IntArrayValuable { +public enum BpmHttpRequestParamSettingType implements IntArrayValuable { FIXED_VALUE(1, "固定值"), FROM_FORM(2, "表单"); @@ -21,7 +21,7 @@ public enum BpmListenerParamTypeEnum implements IntArrayValuable { private final Integer type; private final String name; - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmListenerParamTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmHttpRequestParamSettingType::getType).toArray(); @Override public int[] array() { diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java index 4dbae58cbe..65ee645637 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java @@ -27,6 +27,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { COPY_NODE(12, "抄送人", "serviceTask"), DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), + TRIGGER_NODE(15, "触发器", "serviceTask"), // 50 ~ 条件分支 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java new file mode 100644 index 0000000000..b1dbadbfda --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM Simple 触发器类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTriggerType implements IntArrayValuable { + + HTTP_REQUEST(1, "发起 HTTP 请求"); + + /** + * 触发器执行动作类型 + */ + private final Integer type; + + /** + * 触发器执行动作描述 + */ + private final String desc; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmTriggerType::getType).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + + public static BpmTriggerType 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 79bc5e0afc..06109e5aba 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 @@ -9,6 +9,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.hibernate.validator.constraints.URL; import java.util.List; import java.util.Map; @@ -115,6 +116,11 @@ public class BpmSimpleModelNodeVO { @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成,所以 hidden = true private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE + /** + * 触发器节点设置 + */ + private TriggerSetting triggerSetting; + @Schema(description = "任务监听器") @Valid @Data @@ -128,27 +134,28 @@ public class BpmSimpleModelNodeVO { private String path; @Schema(description = "请求头", example = "[]") - private List header; + private List header; @Schema(description = "请求体", example = "[]") - private List body; + private List body; + } - // TODO @芋艿:这里后续要不要复用; + @Schema(description = "HTTP 请求参数设置") + @Data + public static class HttpRequestParamSetting { - @Schema(description = "任务监听器键值对") - @Data - public static class ListenerParam { + @Schema(description = "值类型", example = "1") + @InEnum(BpmHttpRequestParamSettingType.class) + @NotNull(message = "值类型不能为空") + private Integer type; - @Schema(description = "值类型", example = "1") - @InEnum(BpmListenerParamTypeEnum.class) - private Integer type; + @Schema(description = "键", example = "xxx") + @NotEmpty(message = "键不能为空") + private String key; - @Schema(description = "键", example = "xxx") - private String key; - - @Schema(description = "值", example = "xxx") - private String value; - } + @Schema(description = "值", example = "xxx") + @NotEmpty(message = "值不能为空") + private String value; } @Schema(description = "审批节点拒绝处理策略") @@ -318,4 +325,39 @@ public class BpmSimpleModelNodeVO { @Schema(description = "条件组", example = "{}") private ConditionGroups conditionGroups; } + + @Schema(description = "触发器节点配置") + @Data + @Valid + public static class TriggerSetting { + + @Schema(description = "触发器类型", example = "1") + @InEnum(BpmTriggerType.class) + @NotNull(message = "触发器类型不能为空") + private Integer type; + + /** + * http 请求触发器设置 + */ + @Valid + private HttpRequestTriggerSetting httpRequestSetting; + + @Schema(description = "http 请求触发器设置", example = "{}") + @Data + public static class HttpRequestTriggerSetting { + + @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; + } + } } 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 fb394cc3d6..3d644fc246 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 @@ -100,6 +100,16 @@ public interface BpmnModelConstants { */ String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器的类型 + */ + String TRIGGER_TYPE = "triggerType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器参数 + */ + String TRIGGER_PARAM = "triggerParam"; + /** * BPMN Start Event Node Id */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java new file mode 100644 index 0000000000..60818800f7 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; + +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +import java.util.EnumMap; +import java.util.List; + +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME; + + +/** + * 处理触发器任务 {@link JavaDelegate} 的实现类 + *

+ * 目前只有 Simple 设计器【触发器节点】使用 + * + * @author jason + */ +@Component(BEAN_NAME) +@Slf4j +public class BpmTriggerTaskDelegate implements JavaDelegate { + + public static final String BEAN_NAME = "bpmTriggerTaskDelegate"; + + @Resource + private List triggers; + + private final EnumMap triggerMap = new EnumMap<>(BpmTriggerType.class); + + @PostConstruct + private void init() { + triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger)); + } + + @Override + public void execute(DelegateExecution execution) { + FlowElement flowElement = execution.getCurrentFlowElement(); + BpmTriggerType bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement); + BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType); + if (bpmTrigger == null) { + log.error("[execute], FlowElement[{}], {} 找不到匹配的 BpmTrigger", execution.getCurrentActivityId(), flowElement); + return; + } + bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement)); + } +} 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 ef7c09b4d4..ba678c49fe 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 @@ -4,17 +4,16 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.*; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 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.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; +import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; @@ -395,6 +394,15 @@ public class BpmnModelUtils { return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); } + public static BpmTriggerType parserTriggerType(FlowElement flowElement) { + Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE)); + return BpmTriggerType.typeOf(triggerType); + } + + public static String parserTriggerParam(FlowElement flowElement) { + return parseExtensionElement(flowElement, TRIGGER_PARAM); + } + // ========== 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 9269ef1f28..a37e10e680 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,17 +5,20 @@ 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.*; 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.listener.BpmCopyTaskDelegate; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; 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.TaskListener; +import org.springframework.util.MultiValueMap; import java.util.*; @@ -39,7 +42,7 @@ public class SimpleModelUtils { static { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), - new DelayTimerNodeConvert(), + new DelayTimerNodeConvert(), new TriggerNodeConvert(), new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); } @@ -93,7 +96,7 @@ public class SimpleModelUtils { /** * 遍历节点,构建 FlowNode 元素 * - * @param node SIMPLE 节点 + * @param node SIMPLE 节点 * @param process BPMN 流程 */ private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { @@ -124,8 +127,8 @@ public class SimpleModelUtils { /** * 遍历节点,构建 SequenceFlow 元素 * - * @param process Bpmn 流程 - * @param node 当前节点 + * @param process Bpmn 流程 + * @param node 当前节点 * @param targetNodeId 目标节点 ID */ private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { @@ -152,8 +155,8 @@ public class SimpleModelUtils { /** * 遍历普通(非条件)节点,构建 SequenceFlow 元素 * - * @param process Bpmn 流程 - * @param node 当前节点 + * @param process Bpmn 流程 + * @param node 当前节点 * @param targetNodeId 目标节点 ID */ private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { @@ -161,7 +164,7 @@ public class SimpleModelUtils { boolean isChildNodeValid = isValidNode(childNode); // 情况一:有“子”节点,则建立连线 // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 - String finalTargetNodeId = isChildNodeValid? childNode.getId() : targetNodeId; + String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); process.addFlowElement(sequenceFlow); @@ -174,8 +177,8 @@ public class SimpleModelUtils { /** * 遍历条件节点,构建 SequenceFlow 元素 * - * @param process Bpmn 流程 - * @param node 当前节点 + * @param process Bpmn 流程 + * @param node 当前节点 * @param targetNodeId 目标节点 ID */ private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { @@ -231,7 +234,7 @@ public class SimpleModelUtils { String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); process.addFlowElement(sequenceFlow); - // 4.2 如果是路由分支,需要连接后续节点为默认路由 + // 4.2 如果是路由分支,需要连接后续节点为默认路由 } else if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), null, null); @@ -449,7 +452,7 @@ public class SimpleModelUtils { private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { List flowableListeners = new ArrayList<>(3); if (node.getTaskCreateListener() != null - && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { + && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); @@ -458,7 +461,7 @@ public class SimpleModelUtils { flowableListeners.add(flowableListener); } if (node.getTaskAssignListener() != null - && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { + && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); @@ -467,7 +470,7 @@ public class SimpleModelUtils { flowableListeners.add(flowableListener); } if (node.getTaskCompleteListener() != null - && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { + && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); @@ -693,9 +696,9 @@ public class SimpleModelUtils { boundaryEvent.setAttachedToRef(receiveTask); // 2.2 定义超时时间 TimerEventDefinition eventDefinition = new TimerEventDefinition(); - if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_DATE_TIME.getType())){ + if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_DATE_TIME.getType())) { eventDefinition.setTimeDuration(node.getDelaySetting().getDelayTime()); - } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_TIME_DURATION.getType())){ + } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_TIME_DURATION.getType())) { eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime()); } boundaryEvent.addEventDefinition(eventDefinition); @@ -711,6 +714,32 @@ public class SimpleModelUtils { } } + public static class TriggerNodeConvert implements NodeConvert { + + @Override + public ServiceTask convert(BpmSimpleModelNodeVO node) { + // 触发器使用 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) { + addExtensionElement(serviceTask, TRIGGER_PARAM, + JsonUtils.toJsonString(node.getTriggerSetting().getHttpRequestSetting())); + } + } + return serviceTask; + } + + @Override + public BpmSimpleModelNodeType getType() { + return BpmSimpleModelNodeType.TRIGGER_NODE; + } + } + public static class RouteBranchNodeConvert implements NodeConvert { @Override @@ -751,7 +780,7 @@ public class SimpleModelUtils { } private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map variables, - List resultNodes) { + List resultNodes) { // 如果不合法(包括为空),则直接结束 if (!isValidNode(currentNode)) { return; @@ -761,10 +790,10 @@ public class SimpleModelUtils { // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE if (nodeType == BpmSimpleModelNodeType.START_NODE - || nodeType == BpmSimpleModelNodeType.START_USER_NODE - || nodeType == BpmSimpleModelNodeType.APPROVE_NODE - || nodeType == BpmSimpleModelNodeType.COPY_NODE - || nodeType == BpmSimpleModelNodeType.END_NODE) { + || nodeType == BpmSimpleModelNodeType.START_USER_NODE + || nodeType == BpmSimpleModelNodeType.APPROVE_NODE + || nodeType == BpmSimpleModelNodeType.COPY_NODE + || nodeType == BpmSimpleModelNodeType.END_NODE) { // 添加元素 resultNodes.add(currentNode); } @@ -774,7 +803,7 @@ public class SimpleModelUtils { // 查找满足条件的 BpmSimpleModelNodeVO 节点 BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode)); + && evalConditionExpress(variables, conditionNode)); if (matchConditionNode == null) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -815,4 +844,25 @@ public class SimpleModelUtils { return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode)); } + /** + * 添加 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(BpmHttpRequestParamSettingType.FIXED_VALUE.getType())) { + params.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamSettingType.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/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 ffe91f277f..e89587d471 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,9 +1,8 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; -import cn.hutool.core.collection.CollUtil; 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.enums.definition.BpmListenerParamTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; @@ -22,7 +21,6 @@ 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; @@ -59,8 +57,8 @@ public class BpmUserTaskListener implements TaskListener { Map processVariables = processInstance.getProcessVariables(); MultiValueMap headers = new LinkedMultiValueMap<>(); MultiValueMap body = new LinkedMultiValueMap<>(); - parseListenerParam(listenerHandler.getHeader(), processVariables, headers); - parseListenerParam(listenerHandler.getBody(), processVariables, body); + 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()); @@ -94,20 +92,4 @@ public class BpmUserTaskListener implements TaskListener { } // 4. 是否需要后续操作?TODO 芋艿:待定! } - - private void parseListenerParam(List list, - Map processVariables, - MultiValueMap to) { - if (CollUtil.isEmpty(list)) { - return; - } - list.forEach(item -> { - if (item.getType().equals(BpmListenerParamTypeEnum.FIXED_VALUE.getType())) { - to.add(item.getKey(), item.getValue()); - } else if (item.getType().equals(BpmListenerParamTypeEnum.FROM_FORM.getType())) { - to.add(item.getKey(), processVariables.get(item.getValue()).toString()); - } - }); - } - } \ 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/BpmHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java new file mode 100644 index 0000000000..0614e6948c --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java @@ -0,0 +1,73 @@ +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.TriggerSetting.HttpRequestTriggerSetting; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +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.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; + +/** + * BPM 发送 HTTP 请求触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmHttpRequestTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Resource + private RestTemplate restTemplate; + + @Override + public BpmTriggerType getType() { + return BpmTriggerType.HTTP_REQUEST; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + HttpRequestTriggerSetting httpRequestSetting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class); + if (httpRequestSetting == null) { + log.error("[execute] HTTP 触发器请求配置为空"); + 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, httpRequestSetting.getHeader(), processVariables); + // 2.2 设置请求体 + MultiValueMap body = new LinkedMultiValueMap<>(); + SimpleModelUtils.addHttpRequestParam(body, httpRequestSetting.getBody(), processVariables); + body.add("processInstanceId", processInstanceId); + + // 3. 发起请求 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + try { + ResponseEntity responseEntity = restTemplate.exchange(httpRequestSetting.getUrl(), HttpMethod.POST, + requestEntity, String.class); + log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + } + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java new file mode 100644 index 0000000000..f0c474784f --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; + +/** + * BPM 触发器接口 + *

+ * 处理不同的动作 + * + * @author jason + */ +public interface BpmTrigger { + + /** + * 对应触发器类型 + * + * @return 触发器类型 + */ + BpmTriggerType getType(); + + /** + * 触发器执行 + * + * @param processInstanceId 流程实例编号 + * @param param 触发器参数 + */ + void execute(String processInstanceId, String param); +}