Browse Source

【功能新增】新增Simple 设计器触发器节点

jason 6 months ago
parent
commit
8a8dc67d72
11 changed files with 354 additions and 65 deletions
  1. 3 3
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmHttpRequestParamSettingType.java
  2. 1 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java
  3. 41 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java
  4. 57 15
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
  5. 10 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
  6. 54 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java
  7. 13 5
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
  8. 71 21
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
  9. 3 21
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java
  10. 73 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java
  11. 28 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java

+ 3 - 3
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmListenerParamTypeEnum.java → 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() {

+ 1 - 0
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"), // 用于构建流转条件的表达式

+ 41 - 0
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());
+    }
+}

+ 57 - 15
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<ListenerParam> header;
+        private List<HttpRequestParamSetting> header;
 
         @Schema(description = "请求体", example = "[]")
-        private List<ListenerParam> body;
-
-        // TODO @芋艿:这里后续要不要复用;
+        private List<HttpRequestParamSetting> body;
+    }
 
-        @Schema(description = "任务监听器键值对")
-        @Data
-        public static class ListenerParam {
+    @Schema(description = "HTTP 请求参数设置")
+    @Data
+    public static class HttpRequestParamSetting {
 
-            @Schema(description = "值类型", example = "1")
-            @InEnum(BpmListenerParamTypeEnum.class)
-            private Integer type;
+        @Schema(description = "值类型", example = "1")
+        @InEnum(BpmHttpRequestParamSettingType.class)
+        @NotNull(message = "值类型不能为空")
+        private Integer type;
 
-            @Schema(description = "键", example = "xxx")
-            private String key;
+        @Schema(description = "键", example = "xxx")
+        @NotEmpty(message = "键不能为空")
+        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<HttpRequestParamSetting> header;
+
+            @Schema(description = "请求头参数设置", example = "[]")
+            @Valid
+            private List<HttpRequestParamSetting> body;
+        }
+    }
 }

+ 10 - 0
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
      */

+ 54 - 0
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} 的实现类
+ * <p>
+ * 目前只有 Simple 设计器【触发器节点】使用
+ *
+ * @author jason
+ */
+@Component(BEAN_NAME)
+@Slf4j
+public class BpmTriggerTaskDelegate implements JavaDelegate {
+
+    public static final String BEAN_NAME = "bpmTriggerTaskDelegate";
+
+    @Resource
+    private List<BpmTrigger> triggers;
+
+    private final EnumMap<BpmTriggerType, BpmTrigger> 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));
+    }
+}

+ 13 - 5
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 简单查找相关的方法 ==========
 
     /**

+ 71 - 21
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<NodeConvert> 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<FlowableListener> 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<String, Object> variables,
-                                  List<BpmSimpleModelNodeVO> resultNodes) {
+                                         List<BpmSimpleModelNodeVO> 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<String, String> params,
+                                           List<BpmSimpleModelNodeVO.HttpRequestParamSetting> paramSettings,
+                                           Map<String, Object> 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());
+            }
+        });
+    }
 }

+ 3 - 21
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<String, Object> processVariables = processInstance.getProcessVariables();
         MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
         MultiValueMap<String, String> 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<BpmSimpleModelNodeVO.ListenerHandler.ListenerParam> list,
-                                    Map<String, Object> processVariables,
-                                    MultiValueMap<String, String> 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());
-            }
-        });
-    }
-
 }

+ 73 - 0
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<String, Object> processVariables = processInstance.getProcessVariables();
+        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
+        headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
+        SimpleModelUtils.addHttpRequestParam(headers, httpRequestSetting.getHeader(), processVariables);
+        // 2.2 设置请求体
+        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
+        SimpleModelUtils.addHttpRequestParam(body, httpRequestSetting.getBody(), processVariables);
+        body.add("processInstanceId", processInstanceId);
+
+        // 3. 发起请求
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
+        try {
+            ResponseEntity<String> 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());
+        }
+    }
+}

+ 28 - 0
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 触发器接口
+ * <p>
+ * 处理不同的动作
+ *
+ * @author jason
+ */
+public interface BpmTrigger {
+
+    /**
+     * 对应触发器类型
+     *
+     * @return 触发器类型
+     */
+    BpmTriggerType getType();
+
+    /**
+     * 触发器执行
+     *
+     * @param processInstanceId 流程实例编号
+     * @param param 触发器参数
+     */
+    void execute(String processInstanceId, String param);
+}