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

This commit is contained in:
jason 2025-01-22 22:18:37 +08:00
parent ca1d9e6896
commit 8a8dc67d72
11 changed files with 354 additions and 65 deletions

View File

@ -7,13 +7,13 @@ import lombok.Getter;
import java.util.Arrays; import java.util.Arrays;
/** /**
* BPM 任务监听器键值对类型 * BPM HTTP 请求参数设置类型用于 Simple 设计器任务监听器和触发器配置
* *
* @author Lesan * @author Lesan
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum BpmListenerParamTypeEnum implements IntArrayValuable { public enum BpmHttpRequestParamSettingType implements IntArrayValuable {
FIXED_VALUE(1, "固定值"), FIXED_VALUE(1, "固定值"),
FROM_FORM(2, "表单"); FROM_FORM(2, "表单");
@ -21,7 +21,7 @@ public enum BpmListenerParamTypeEnum implements IntArrayValuable {
private final Integer type; private final Integer type;
private final String name; 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 @Override
public int[] array() { public int[] array() {

View File

@ -27,6 +27,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
COPY_NODE(12, "抄送人", "serviceTask"), COPY_NODE(12, "抄送人", "serviceTask"),
DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
TRIGGER_NODE(15, "触发器", "serviceTask"),
// 50 ~ 条件分支 // 50 ~ 条件分支
CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式

View File

@ -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());
}
}

View File

@ -9,6 +9,7 @@ import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.URL;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -115,6 +116,11 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成所以 hidden = true @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成所以 hidden = true
private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE
/**
* 触发器节点设置
*/
private TriggerSetting triggerSetting;
@Schema(description = "任务监听器") @Schema(description = "任务监听器")
@Valid @Valid
@Data @Data
@ -128,27 +134,28 @@ public class BpmSimpleModelNodeVO {
private String path; private String path;
@Schema(description = "请求头", example = "[]") @Schema(description = "请求头", example = "[]")
private List<ListenerParam> header; private List<HttpRequestParamSetting> header;
@Schema(description = "请求体", example = "[]") @Schema(description = "请求体", example = "[]")
private List<ListenerParam> body; private List<HttpRequestParamSetting> body;
}
// TODO @芋艿这里后续要不要复用 @Schema(description = "HTTP 请求参数设置")
@Data
public static class HttpRequestParamSetting {
@Schema(description = "任务监听器键值对") @Schema(description = "值类型", example = "1")
@Data @InEnum(BpmHttpRequestParamSettingType.class)
public static class ListenerParam { @NotNull(message = "值类型不能为空")
private Integer type;
@Schema(description = "值类型", example = "1") @Schema(description = "", example = "xxx")
@InEnum(BpmListenerParamTypeEnum.class) @NotEmpty(message = "键不能为空")
private Integer type; private String key;
@Schema(description = "", example = "xxx") @Schema(description = "", example = "xxx")
private String key; @NotEmpty(message = "值不能为空")
private String value;
@Schema(description = "", example = "xxx")
private String value;
}
} }
@Schema(description = "审批节点拒绝处理策略") @Schema(description = "审批节点拒绝处理策略")
@ -318,4 +325,39 @@ public class BpmSimpleModelNodeVO {
@Schema(description = "条件组", example = "{}") @Schema(description = "条件组", example = "{}")
private ConditionGroups conditionGroups; 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;
}
}
} }

View File

@ -100,6 +100,16 @@ public interface BpmnModelConstants {
*/ */
String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable";
/**
* BPMN ExtensionElement 的扩展属性用于标记触发器的类型
*/
String TRIGGER_TYPE = "triggerType";
/**
* BPMN ExtensionElement 的扩展属性用于标记触发器参数
*/
String TRIGGER_PARAM = "triggerParam";
/** /**
* BPMN Start Event Node Id * BPMN Start Event Node Id
*/ */

View File

@ -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));
}
}

View File

@ -4,17 +4,16 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; 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.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 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.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils; 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.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; 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.*;
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.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -395,6 +394,15 @@ public class BpmnModelUtils {
return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); 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 简单查找相关的方法 ========== // ========== BPM 简单查找相关的方法 ==========
/** /**

View File

@ -5,17 +5,20 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*; import cn.hutool.core.util.*;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 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;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; 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.enums.definition.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 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.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; 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.BpmnAutoLayout;
import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.*;
import org.flowable.engine.delegate.TaskListener; import org.flowable.engine.delegate.TaskListener;
import org.springframework.util.MultiValueMap;
import java.util.*; import java.util.*;
@ -39,7 +42,7 @@ public class SimpleModelUtils {
static { static {
List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(), List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(),
new DelayTimerNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(),
new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert());
converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
} }
@ -93,7 +96,7 @@ public class SimpleModelUtils {
/** /**
* 遍历节点构建 FlowNode 元素 * 遍历节点构建 FlowNode 元素
* *
* @param node SIMPLE 节点 * @param node SIMPLE 节点
* @param process BPMN 流程 * @param process BPMN 流程
*/ */
private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) {
@ -124,8 +127,8 @@ public class SimpleModelUtils {
/** /**
* 遍历节点构建 SequenceFlow 元素 * 遍历节点构建 SequenceFlow 元素
* *
* @param process Bpmn 流程 * @param process Bpmn 流程
* @param node 当前节点 * @param node 当前节点
* @param targetNodeId 目标节点 ID * @param targetNodeId 目标节点 ID
*/ */
private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
@ -152,8 +155,8 @@ public class SimpleModelUtils {
/** /**
* 遍历普通非条件节点构建 SequenceFlow 元素 * 遍历普通非条件节点构建 SequenceFlow 元素
* *
* @param process Bpmn 流程 * @param process Bpmn 流程
* @param node 当前节点 * @param node 当前节点
* @param targetNodeId 目标节点 ID * @param targetNodeId 目标节点 ID
*/ */
private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
@ -161,7 +164,7 @@ public class SimpleModelUtils {
boolean isChildNodeValid = isValidNode(childNode); boolean isChildNodeValid = isValidNode(childNode);
// 情况一节点则建立连线 // 情况一节点则建立连线
// 情况二没有子节点则直接跟 targetNodeId 建立连线例如说结束节点条件分支分支节点的孩子节点或聚合节点的最后一个节点 // 情况二没有子节点则直接跟 targetNodeId 建立连线例如说结束节点条件分支分支节点的孩子节点或聚合节点的最后一个节点
String finalTargetNodeId = isChildNodeValid? childNode.getId() : targetNodeId; String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId;
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId);
process.addFlowElement(sequenceFlow); process.addFlowElement(sequenceFlow);
@ -174,8 +177,8 @@ public class SimpleModelUtils {
/** /**
* 遍历条件节点构建 SequenceFlow 元素 * 遍历条件节点构建 SequenceFlow 元素
* *
* @param process Bpmn 流程 * @param process Bpmn 流程
* @param node 当前节点 * @param node 当前节点
* @param targetNodeId 目标节点 ID * @param targetNodeId 目标节点 ID
*/ */
private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
@ -231,7 +234,7 @@ public class SimpleModelUtils {
String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId);
process.addFlowElement(sequenceFlow); process.addFlowElement(sequenceFlow);
// 4.2 如果是路由分支需要连接后续节点为默认路由 // 4.2 如果是路由分支需要连接后续节点为默认路由
} else if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { } else if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) {
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(),
null, null); null, null);
@ -449,7 +452,7 @@ public class SimpleModelUtils {
private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) {
List<FlowableListener> flowableListeners = new ArrayList<>(3); List<FlowableListener> flowableListeners = new ArrayList<>(3);
if (node.getTaskCreateListener() != null if (node.getTaskCreateListener() != null
&& Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener(); FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); flowableListener.setEvent(TaskListener.EVENTNAME_CREATE);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
@ -458,7 +461,7 @@ public class SimpleModelUtils {
flowableListeners.add(flowableListener); flowableListeners.add(flowableListener);
} }
if (node.getTaskAssignListener() != null if (node.getTaskAssignListener() != null
&& Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener(); FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
@ -467,7 +470,7 @@ public class SimpleModelUtils {
flowableListeners.add(flowableListener); flowableListeners.add(flowableListener);
} }
if (node.getTaskCompleteListener() != null if (node.getTaskCompleteListener() != null
&& Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) {
FlowableListener flowableListener = new FlowableListener(); FlowableListener flowableListener = new FlowableListener();
flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE);
flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
@ -693,9 +696,9 @@ public class SimpleModelUtils {
boundaryEvent.setAttachedToRef(receiveTask); boundaryEvent.setAttachedToRef(receiveTask);
// 2.2 定义超时时间 // 2.2 定义超时时间
TimerEventDefinition eventDefinition = new TimerEventDefinition(); 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()); 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()); eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime());
} }
boundaryEvent.addEventDefinition(eventDefinition); 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 { public static class RouteBranchNodeConvert implements NodeConvert {
@Override @Override
@ -751,7 +780,7 @@ public class SimpleModelUtils {
} }
private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables, private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables,
List<BpmSimpleModelNodeVO> resultNodes) { List<BpmSimpleModelNodeVO> resultNodes) {
// 如果不合法包括为空则直接结束 // 如果不合法包括为空则直接结束
if (!isValidNode(currentNode)) { if (!isValidNode(currentNode)) {
return; return;
@ -761,10 +790,10 @@ public class SimpleModelUtils {
// 情况START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE // 情况START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE
if (nodeType == BpmSimpleModelNodeType.START_NODE if (nodeType == BpmSimpleModelNodeType.START_NODE
|| nodeType == BpmSimpleModelNodeType.START_USER_NODE || nodeType == BpmSimpleModelNodeType.START_USER_NODE
|| nodeType == BpmSimpleModelNodeType.APPROVE_NODE || nodeType == BpmSimpleModelNodeType.APPROVE_NODE
|| nodeType == BpmSimpleModelNodeType.COPY_NODE || nodeType == BpmSimpleModelNodeType.COPY_NODE
|| nodeType == BpmSimpleModelNodeType.END_NODE) { || nodeType == BpmSimpleModelNodeType.END_NODE) {
// 添加元素 // 添加元素
resultNodes.add(currentNode); resultNodes.add(currentNode);
} }
@ -774,7 +803,7 @@ public class SimpleModelUtils {
// 查找满足条件的 BpmSimpleModelNodeVO 节点 // 查找满足条件的 BpmSimpleModelNodeVO 节点
BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())
&& evalConditionExpress(variables, conditionNode)); && evalConditionExpress(variables, conditionNode));
if (matchConditionNode == null) { if (matchConditionNode == null) {
matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(),
conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()));
@ -815,4 +844,25 @@ public class SimpleModelUtils {
return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode)); 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());
}
});
}
} }

View File

@ -1,9 +1,8 @@
package cn.iocoder.yudao.module.bpm.service.task.listener; package cn.iocoder.yudao.module.bpm.service.task.listener;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; 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.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 cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.Setter; import lombok.Setter;
@ -22,7 +21,6 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; 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(); Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
parseListenerParam(listenerHandler.getHeader(), processVariables, headers); SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables);
parseListenerParam(listenerHandler.getBody(), processVariables, body); SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables);
// 2.1 请求头默认参数 // 2.1 请求头默认参数
if (StrUtil.isNotEmpty(delegateTask.getTenantId())) { if (StrUtil.isNotEmpty(delegateTask.getTenantId())) {
headers.add(HEADER_TENANT_ID, delegateTask.getTenantId()); headers.add(HEADER_TENANT_ID, delegateTask.getTenantId());
@ -94,20 +92,4 @@ public class BpmUserTaskListener implements TaskListener {
} }
// 4. 是否需要后续操作TODO 芋艿待定 // 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());
}
});
}
} }

View File

@ -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());
}
}
}

View File

@ -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);
}