Commit 8a8dc67d72c95ad1bc0d411e319e48014fe3a5d7
1 parent
ca1d9e68
【功能新增】新增Simple 设计器触发器节点
Showing
11 changed files
with
354 additions
and
65 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmListenerParamTypeEnum.java renamed to 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; |
| 7 | 7 | import java.util.Arrays; |
| 8 | 8 | |
| 9 | 9 | /** |
| 10 | - * BPM 任务监听器键值对类型 | |
| 10 | + * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。 | |
| 11 | 11 | * |
| 12 | 12 | * @author Lesan |
| 13 | 13 | */ |
| 14 | 14 | @Getter |
| 15 | 15 | @AllArgsConstructor |
| 16 | -public enum BpmListenerParamTypeEnum implements IntArrayValuable { | |
| 16 | +public enum BpmHttpRequestParamSettingType implements IntArrayValuable { | |
| 17 | 17 | |
| 18 | 18 | FIXED_VALUE(1, "固定值"), |
| 19 | 19 | FROM_FORM(2, "表单"); |
| ... | ... | @@ -21,7 +21,7 @@ public enum BpmListenerParamTypeEnum implements IntArrayValuable { |
| 21 | 21 | private final Integer type; |
| 22 | 22 | private final String name; |
| 23 | 23 | |
| 24 | - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmListenerParamTypeEnum::getType).toArray(); | |
| 24 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmHttpRequestParamSettingType::getType).toArray(); | |
| 25 | 25 | |
| 26 | 26 | @Override |
| 27 | 27 | public int[] array() { | ... | ... |
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 { |
| 27 | 27 | COPY_NODE(12, "抄送人", "serviceTask"), |
| 28 | 28 | |
| 29 | 29 | DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), |
| 30 | + TRIGGER_NODE(15, "触发器", "serviceTask"), | |
| 30 | 31 | |
| 31 | 32 | // 50 ~ 条件分支 |
| 32 | 33 | CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerType.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.enums.definition; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ArrayUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Getter; | |
| 7 | + | |
| 8 | +import java.util.Arrays; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * BPM Simple 触发器类型枚举 | |
| 12 | + * | |
| 13 | + * @author jason | |
| 14 | + */ | |
| 15 | +@Getter | |
| 16 | +@AllArgsConstructor | |
| 17 | +public enum BpmTriggerType implements IntArrayValuable { | |
| 18 | + | |
| 19 | + HTTP_REQUEST(1, "发起 HTTP 请求"); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 触发器执行动作类型 | |
| 23 | + */ | |
| 24 | + private final Integer type; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 触发器执行动作描述 | |
| 28 | + */ | |
| 29 | + private final String desc; | |
| 30 | + | |
| 31 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmTriggerType::getType).toArray(); | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public int[] array() { | |
| 35 | + return ARRAYS; | |
| 36 | + } | |
| 37 | + | |
| 38 | + public static BpmTriggerType typeOf(Integer type) { | |
| 39 | + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); | |
| 40 | + } | |
| 41 | +} | ... | ... |
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; |
| 9 | 9 | import jakarta.validation.constraints.NotEmpty; |
| 10 | 10 | import jakarta.validation.constraints.NotNull; |
| 11 | 11 | import lombok.Data; |
| 12 | +import org.hibernate.validator.constraints.URL; | |
| 12 | 13 | |
| 13 | 14 | import java.util.List; |
| 14 | 15 | import java.util.Map; |
| ... | ... | @@ -115,6 +116,11 @@ public class BpmSimpleModelNodeVO { |
| 115 | 116 | @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成,所以 hidden = true |
| 116 | 117 | private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE |
| 117 | 118 | |
| 119 | + /** | |
| 120 | + * 触发器节点设置 | |
| 121 | + */ | |
| 122 | + private TriggerSetting triggerSetting; | |
| 123 | + | |
| 118 | 124 | @Schema(description = "任务监听器") |
| 119 | 125 | @Valid |
| 120 | 126 | @Data |
| ... | ... | @@ -128,27 +134,28 @@ public class BpmSimpleModelNodeVO { |
| 128 | 134 | private String path; |
| 129 | 135 | |
| 130 | 136 | @Schema(description = "请求头", example = "[]") |
| 131 | - private List<ListenerParam> header; | |
| 137 | + private List<HttpRequestParamSetting> header; | |
| 132 | 138 | |
| 133 | 139 | @Schema(description = "请求体", example = "[]") |
| 134 | - private List<ListenerParam> body; | |
| 135 | - | |
| 136 | - // TODO @芋艿:这里后续要不要复用; | |
| 140 | + private List<HttpRequestParamSetting> body; | |
| 141 | + } | |
| 137 | 142 | |
| 138 | - @Schema(description = "任务监听器键值对") | |
| 139 | - @Data | |
| 140 | - public static class ListenerParam { | |
| 143 | + @Schema(description = "HTTP 请求参数设置") | |
| 144 | + @Data | |
| 145 | + public static class HttpRequestParamSetting { | |
| 141 | 146 | |
| 142 | - @Schema(description = "值类型", example = "1") | |
| 143 | - @InEnum(BpmListenerParamTypeEnum.class) | |
| 144 | - private Integer type; | |
| 147 | + @Schema(description = "值类型", example = "1") | |
| 148 | + @InEnum(BpmHttpRequestParamSettingType.class) | |
| 149 | + @NotNull(message = "值类型不能为空") | |
| 150 | + private Integer type; | |
| 145 | 151 | |
| 146 | - @Schema(description = "键", example = "xxx") | |
| 147 | - private String key; | |
| 152 | + @Schema(description = "键", example = "xxx") | |
| 153 | + @NotEmpty(message = "键不能为空") | |
| 154 | + private String key; | |
| 148 | 155 | |
| 149 | - @Schema(description = "值", example = "xxx") | |
| 150 | - private String value; | |
| 151 | - } | |
| 156 | + @Schema(description = "值", example = "xxx") | |
| 157 | + @NotEmpty(message = "值不能为空") | |
| 158 | + private String value; | |
| 152 | 159 | } |
| 153 | 160 | |
| 154 | 161 | @Schema(description = "审批节点拒绝处理策略") |
| ... | ... | @@ -318,4 +325,39 @@ public class BpmSimpleModelNodeVO { |
| 318 | 325 | @Schema(description = "条件组", example = "{}") |
| 319 | 326 | private ConditionGroups conditionGroups; |
| 320 | 327 | } |
| 328 | + | |
| 329 | + @Schema(description = "触发器节点配置") | |
| 330 | + @Data | |
| 331 | + @Valid | |
| 332 | + public static class TriggerSetting { | |
| 333 | + | |
| 334 | + @Schema(description = "触发器类型", example = "1") | |
| 335 | + @InEnum(BpmTriggerType.class) | |
| 336 | + @NotNull(message = "触发器类型不能为空") | |
| 337 | + private Integer type; | |
| 338 | + | |
| 339 | + /** | |
| 340 | + * http 请求触发器设置 | |
| 341 | + */ | |
| 342 | + @Valid | |
| 343 | + private HttpRequestTriggerSetting httpRequestSetting; | |
| 344 | + | |
| 345 | + @Schema(description = "http 请求触发器设置", example = "{}") | |
| 346 | + @Data | |
| 347 | + public static class HttpRequestTriggerSetting { | |
| 348 | + | |
| 349 | + @Schema(description = "请求路径", example = "http://127.0.0.1") | |
| 350 | + @NotEmpty(message = "请求 URL 不能为空") | |
| 351 | + @URL(message = "请求 URL 格式不正确") | |
| 352 | + private String url; | |
| 353 | + | |
| 354 | + @Schema(description = "请求头参数设置", example = "[]") | |
| 355 | + @Valid | |
| 356 | + private List<HttpRequestParamSetting> header; | |
| 357 | + | |
| 358 | + @Schema(description = "请求头参数设置", example = "[]") | |
| 359 | + @Valid | |
| 360 | + private List<HttpRequestParamSetting> body; | |
| 361 | + } | |
| 362 | + } | |
| 321 | 363 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| ... | ... | @@ -101,6 +101,16 @@ public interface BpmnModelConstants { |
| 101 | 101 | String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; |
| 102 | 102 | |
| 103 | 103 | /** |
| 104 | + * BPMN ExtensionElement 的扩展属性,用于标记触发器的类型 | |
| 105 | + */ | |
| 106 | + String TRIGGER_TYPE = "triggerType"; | |
| 107 | + | |
| 108 | + /** | |
| 109 | + * BPMN ExtensionElement 的扩展属性,用于标记触发器参数 | |
| 110 | + */ | |
| 111 | + String TRIGGER_PARAM = "triggerParam"; | |
| 112 | + | |
| 113 | + /** | |
| 104 | 114 | * BPMN Start Event Node Id |
| 105 | 115 | */ |
| 106 | 116 | String START_EVENT_NODE_ID = "StartEvent"; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; | |
| 4 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 5 | +import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; | |
| 6 | +import jakarta.annotation.PostConstruct; | |
| 7 | +import jakarta.annotation.Resource; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import org.flowable.bpmn.model.FlowElement; | |
| 10 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 11 | +import org.flowable.engine.delegate.JavaDelegate; | |
| 12 | +import org.springframework.stereotype.Component; | |
| 13 | + | |
| 14 | +import java.util.EnumMap; | |
| 15 | +import java.util.List; | |
| 16 | + | |
| 17 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME; | |
| 18 | + | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 处理触发器任务 {@link JavaDelegate} 的实现类 | |
| 22 | + * <p> | |
| 23 | + * 目前只有 Simple 设计器【触发器节点】使用 | |
| 24 | + * | |
| 25 | + * @author jason | |
| 26 | + */ | |
| 27 | +@Component(BEAN_NAME) | |
| 28 | +@Slf4j | |
| 29 | +public class BpmTriggerTaskDelegate implements JavaDelegate { | |
| 30 | + | |
| 31 | + public static final String BEAN_NAME = "bpmTriggerTaskDelegate"; | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private List<BpmTrigger> triggers; | |
| 35 | + | |
| 36 | + private final EnumMap<BpmTriggerType, BpmTrigger> triggerMap = new EnumMap<>(BpmTriggerType.class); | |
| 37 | + | |
| 38 | + @PostConstruct | |
| 39 | + private void init() { | |
| 40 | + triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public void execute(DelegateExecution execution) { | |
| 45 | + FlowElement flowElement = execution.getCurrentFlowElement(); | |
| 46 | + BpmTriggerType bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement); | |
| 47 | + BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType); | |
| 48 | + if (bpmTrigger == null) { | |
| 49 | + log.error("[execute], FlowElement[{}], {} 找不到匹配的 BpmTrigger", execution.getCurrentActivityId(), flowElement); | |
| 50 | + return; | |
| 51 | + } | |
| 52 | + bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement)); | |
| 53 | + } | |
| 54 | +} | ... | ... |
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; |
| 4 | 4 | import cn.hutool.core.convert.Convert; |
| 5 | 5 | import cn.hutool.core.lang.Assert; |
| 6 | 6 | import cn.hutool.core.map.MapUtil; |
| 7 | -import cn.hutool.core.util.*; | |
| 7 | +import cn.hutool.core.util.ArrayUtil; | |
| 8 | +import cn.hutool.core.util.ObjUtil; | |
| 9 | +import cn.hutool.core.util.StrUtil; | |
| 8 | 10 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 9 | 11 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 10 | 12 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 11 | 13 | import cn.iocoder.yudao.framework.common.util.string.StrUtils; |
| 12 | 14 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 13 | 15 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| 14 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; | |
| 15 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; | |
| 16 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; | |
| 17 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | |
| 16 | +import cn.iocoder.yudao.module.bpm.enums.definition.*; | |
| 18 | 17 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 19 | 18 | import com.google.common.collect.Maps; |
| 20 | 19 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -395,6 +394,15 @@ public class BpmnModelUtils { |
| 395 | 394 | return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); |
| 396 | 395 | } |
| 397 | 396 | |
| 397 | + public static BpmTriggerType parserTriggerType(FlowElement flowElement) { | |
| 398 | + Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE)); | |
| 399 | + return BpmTriggerType.typeOf(triggerType); | |
| 400 | + } | |
| 401 | + | |
| 402 | + public static String parserTriggerParam(FlowElement flowElement) { | |
| 403 | + return parseExtensionElement(flowElement, TRIGGER_PARAM); | |
| 404 | + } | |
| 405 | + | |
| 398 | 406 | // ========== BPM 简单查找相关的方法 ========== |
| 399 | 407 | |
| 400 | 408 | /** | ... | ... |
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; |
| 5 | 5 | import cn.hutool.core.map.MapUtil; |
| 6 | 6 | import cn.hutool.core.util.*; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 8 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 9 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; |
| 10 | 11 | import cn.iocoder.yudao.module.bpm.enums.definition.*; |
| 11 | 12 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| 12 | 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 13 | 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; |
| 15 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; | |
| 14 | 16 | import org.flowable.bpmn.BpmnAutoLayout; |
| 15 | 17 | import org.flowable.bpmn.constants.BpmnXMLConstants; |
| 16 | 18 | import org.flowable.bpmn.model.Process; |
| 17 | 19 | import org.flowable.bpmn.model.*; |
| 18 | 20 | import org.flowable.engine.delegate.TaskListener; |
| 21 | +import org.springframework.util.MultiValueMap; | |
| 19 | 22 | |
| 20 | 23 | import java.util.*; |
| 21 | 24 | |
| ... | ... | @@ -39,7 +42,7 @@ public class SimpleModelUtils { |
| 39 | 42 | static { |
| 40 | 43 | List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(), |
| 41 | 44 | new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), |
| 42 | - new DelayTimerNodeConvert(), | |
| 45 | + new DelayTimerNodeConvert(), new TriggerNodeConvert(), | |
| 43 | 46 | new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); |
| 44 | 47 | converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); |
| 45 | 48 | } |
| ... | ... | @@ -93,7 +96,7 @@ public class SimpleModelUtils { |
| 93 | 96 | /** |
| 94 | 97 | * 遍历节点,构建 FlowNode 元素 |
| 95 | 98 | * |
| 96 | - * @param node SIMPLE 节点 | |
| 99 | + * @param node SIMPLE 节点 | |
| 97 | 100 | * @param process BPMN 流程 |
| 98 | 101 | */ |
| 99 | 102 | private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { |
| ... | ... | @@ -124,8 +127,8 @@ public class SimpleModelUtils { |
| 124 | 127 | /** |
| 125 | 128 | * 遍历节点,构建 SequenceFlow 元素 |
| 126 | 129 | * |
| 127 | - * @param process Bpmn 流程 | |
| 128 | - * @param node 当前节点 | |
| 130 | + * @param process Bpmn 流程 | |
| 131 | + * @param node 当前节点 | |
| 129 | 132 | * @param targetNodeId 目标节点 ID |
| 130 | 133 | */ |
| 131 | 134 | private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { |
| ... | ... | @@ -152,8 +155,8 @@ public class SimpleModelUtils { |
| 152 | 155 | /** |
| 153 | 156 | * 遍历普通(非条件)节点,构建 SequenceFlow 元素 |
| 154 | 157 | * |
| 155 | - * @param process Bpmn 流程 | |
| 156 | - * @param node 当前节点 | |
| 158 | + * @param process Bpmn 流程 | |
| 159 | + * @param node 当前节点 | |
| 157 | 160 | * @param targetNodeId 目标节点 ID |
| 158 | 161 | */ |
| 159 | 162 | private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { |
| ... | ... | @@ -161,7 +164,7 @@ public class SimpleModelUtils { |
| 161 | 164 | boolean isChildNodeValid = isValidNode(childNode); |
| 162 | 165 | // 情况一:有“子”节点,则建立连线 |
| 163 | 166 | // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 |
| 164 | - String finalTargetNodeId = isChildNodeValid? childNode.getId() : targetNodeId; | |
| 167 | + String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; | |
| 165 | 168 | SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); |
| 166 | 169 | process.addFlowElement(sequenceFlow); |
| 167 | 170 | |
| ... | ... | @@ -174,8 +177,8 @@ public class SimpleModelUtils { |
| 174 | 177 | /** |
| 175 | 178 | * 遍历条件节点,构建 SequenceFlow 元素 |
| 176 | 179 | * |
| 177 | - * @param process Bpmn 流程 | |
| 178 | - * @param node 当前节点 | |
| 180 | + * @param process Bpmn 流程 | |
| 181 | + * @param node 当前节点 | |
| 179 | 182 | * @param targetNodeId 目标节点 ID |
| 180 | 183 | */ |
| 181 | 184 | private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { |
| ... | ... | @@ -231,7 +234,7 @@ public class SimpleModelUtils { |
| 231 | 234 | String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; |
| 232 | 235 | SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); |
| 233 | 236 | process.addFlowElement(sequenceFlow); |
| 234 | - // 4.2 如果是路由分支,需要连接后续节点为默认路由 | |
| 237 | + // 4.2 如果是路由分支,需要连接后续节点为默认路由 | |
| 235 | 238 | } else if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { |
| 236 | 239 | SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), |
| 237 | 240 | null, null); |
| ... | ... | @@ -449,7 +452,7 @@ public class SimpleModelUtils { |
| 449 | 452 | private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { |
| 450 | 453 | List<FlowableListener> flowableListeners = new ArrayList<>(3); |
| 451 | 454 | if (node.getTaskCreateListener() != null |
| 452 | - && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { | |
| 455 | + && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { | |
| 453 | 456 | FlowableListener flowableListener = new FlowableListener(); |
| 454 | 457 | flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); |
| 455 | 458 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| ... | ... | @@ -458,7 +461,7 @@ public class SimpleModelUtils { |
| 458 | 461 | flowableListeners.add(flowableListener); |
| 459 | 462 | } |
| 460 | 463 | if (node.getTaskAssignListener() != null |
| 461 | - && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { | |
| 464 | + && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { | |
| 462 | 465 | FlowableListener flowableListener = new FlowableListener(); |
| 463 | 466 | flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); |
| 464 | 467 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| ... | ... | @@ -467,7 +470,7 @@ public class SimpleModelUtils { |
| 467 | 470 | flowableListeners.add(flowableListener); |
| 468 | 471 | } |
| 469 | 472 | if (node.getTaskCompleteListener() != null |
| 470 | - && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { | |
| 473 | + && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { | |
| 471 | 474 | FlowableListener flowableListener = new FlowableListener(); |
| 472 | 475 | flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); |
| 473 | 476 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| ... | ... | @@ -693,9 +696,9 @@ public class SimpleModelUtils { |
| 693 | 696 | boundaryEvent.setAttachedToRef(receiveTask); |
| 694 | 697 | // 2.2 定义超时时间 |
| 695 | 698 | TimerEventDefinition eventDefinition = new TimerEventDefinition(); |
| 696 | - if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_DATE_TIME.getType())){ | |
| 699 | + if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_DATE_TIME.getType())) { | |
| 697 | 700 | eventDefinition.setTimeDuration(node.getDelaySetting().getDelayTime()); |
| 698 | - } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_TIME_DURATION.getType())){ | |
| 701 | + } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerType.FIXED_TIME_DURATION.getType())) { | |
| 699 | 702 | eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime()); |
| 700 | 703 | } |
| 701 | 704 | boundaryEvent.addEventDefinition(eventDefinition); |
| ... | ... | @@ -711,6 +714,32 @@ public class SimpleModelUtils { |
| 711 | 714 | } |
| 712 | 715 | } |
| 713 | 716 | |
| 717 | + public static class TriggerNodeConvert implements NodeConvert { | |
| 718 | + | |
| 719 | + @Override | |
| 720 | + public ServiceTask convert(BpmSimpleModelNodeVO node) { | |
| 721 | + // 触发器使用 ServiceTask 来实现 | |
| 722 | + ServiceTask serviceTask = new ServiceTask(); | |
| 723 | + serviceTask.setId(node.getId()); | |
| 724 | + serviceTask.setName(node.getName()); | |
| 725 | + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); | |
| 726 | + serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); | |
| 727 | + if (node.getTriggerSetting() != null) { | |
| 728 | + addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); | |
| 729 | + if (node.getTriggerSetting().getHttpRequestSetting() != null) { | |
| 730 | + addExtensionElement(serviceTask, TRIGGER_PARAM, | |
| 731 | + JsonUtils.toJsonString(node.getTriggerSetting().getHttpRequestSetting())); | |
| 732 | + } | |
| 733 | + } | |
| 734 | + return serviceTask; | |
| 735 | + } | |
| 736 | + | |
| 737 | + @Override | |
| 738 | + public BpmSimpleModelNodeType getType() { | |
| 739 | + return BpmSimpleModelNodeType.TRIGGER_NODE; | |
| 740 | + } | |
| 741 | + } | |
| 742 | + | |
| 714 | 743 | public static class RouteBranchNodeConvert implements NodeConvert { |
| 715 | 744 | |
| 716 | 745 | @Override |
| ... | ... | @@ -751,7 +780,7 @@ public class SimpleModelUtils { |
| 751 | 780 | } |
| 752 | 781 | |
| 753 | 782 | private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables, |
| 754 | - List<BpmSimpleModelNodeVO> resultNodes) { | |
| 783 | + List<BpmSimpleModelNodeVO> resultNodes) { | |
| 755 | 784 | // 如果不合法(包括为空),则直接结束 |
| 756 | 785 | if (!isValidNode(currentNode)) { |
| 757 | 786 | return; |
| ... | ... | @@ -761,10 +790,10 @@ public class SimpleModelUtils { |
| 761 | 790 | |
| 762 | 791 | // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE |
| 763 | 792 | if (nodeType == BpmSimpleModelNodeType.START_NODE |
| 764 | - || nodeType == BpmSimpleModelNodeType.START_USER_NODE | |
| 765 | - || nodeType == BpmSimpleModelNodeType.APPROVE_NODE | |
| 766 | - || nodeType == BpmSimpleModelNodeType.COPY_NODE | |
| 767 | - || nodeType == BpmSimpleModelNodeType.END_NODE) { | |
| 793 | + || nodeType == BpmSimpleModelNodeType.START_USER_NODE | |
| 794 | + || nodeType == BpmSimpleModelNodeType.APPROVE_NODE | |
| 795 | + || nodeType == BpmSimpleModelNodeType.COPY_NODE | |
| 796 | + || nodeType == BpmSimpleModelNodeType.END_NODE) { | |
| 768 | 797 | // 添加元素 |
| 769 | 798 | resultNodes.add(currentNode); |
| 770 | 799 | } |
| ... | ... | @@ -774,7 +803,7 @@ public class SimpleModelUtils { |
| 774 | 803 | // 查找满足条件的 BpmSimpleModelNodeVO 节点 |
| 775 | 804 | BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), |
| 776 | 805 | conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) |
| 777 | - && evalConditionExpress(variables, conditionNode)); | |
| 806 | + && evalConditionExpress(variables, conditionNode)); | |
| 778 | 807 | if (matchConditionNode == null) { |
| 779 | 808 | matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), |
| 780 | 809 | conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); |
| ... | ... | @@ -815,4 +844,25 @@ public class SimpleModelUtils { |
| 815 | 844 | return BpmnModelUtils.evalConditionExpress(variables, ConditionNodeConvert.buildConditionExpression(conditionNode)); |
| 816 | 845 | } |
| 817 | 846 | |
| 847 | + /** | |
| 848 | + * 添加 HTTP 请求参数。请求头或者请求体 | |
| 849 | + * | |
| 850 | + * @param params HTTP 请求参数 | |
| 851 | + * @param paramSettings HTTP 请求参数设置 | |
| 852 | + * @param processVariables 流程变量 | |
| 853 | + */ | |
| 854 | + public static void addHttpRequestParam(MultiValueMap<String, String> params, | |
| 855 | + List<BpmSimpleModelNodeVO.HttpRequestParamSetting> paramSettings, | |
| 856 | + Map<String, Object> processVariables) { | |
| 857 | + if (CollUtil.isEmpty(paramSettings)) { | |
| 858 | + return; | |
| 859 | + } | |
| 860 | + paramSettings.forEach(item -> { | |
| 861 | + if (item.getType().equals(BpmHttpRequestParamSettingType.FIXED_VALUE.getType())) { | |
| 862 | + params.add(item.getKey(), item.getValue()); | |
| 863 | + } else if (item.getType().equals(BpmHttpRequestParamSettingType.FROM_FORM.getType())) { | |
| 864 | + params.add(item.getKey(), processVariables.get(item.getValue()).toString()); | |
| 865 | + } | |
| 866 | + }); | |
| 867 | + } | |
| 818 | 868 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.service.task.listener; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.hutool.core.util.StrUtil; |
| 5 | 4 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 6 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmListenerParamTypeEnum; | |
| 5 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; | |
| 7 | 6 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| 8 | 7 | import jakarta.annotation.Resource; |
| 9 | 8 | import lombok.Setter; |
| ... | ... | @@ -22,7 +21,6 @@ import org.springframework.util.MultiValueMap; |
| 22 | 21 | import org.springframework.web.client.RestClientException; |
| 23 | 22 | import org.springframework.web.client.RestTemplate; |
| 24 | 23 | |
| 25 | -import java.util.List; | |
| 26 | 24 | import java.util.Map; |
| 27 | 25 | |
| 28 | 26 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; |
| ... | ... | @@ -59,8 +57,8 @@ public class BpmUserTaskListener implements TaskListener { |
| 59 | 57 | Map<String, Object> processVariables = processInstance.getProcessVariables(); |
| 60 | 58 | MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); |
| 61 | 59 | MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); |
| 62 | - parseListenerParam(listenerHandler.getHeader(), processVariables, headers); | |
| 63 | - parseListenerParam(listenerHandler.getBody(), processVariables, body); | |
| 60 | + SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables); | |
| 61 | + SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables); | |
| 64 | 62 | // 2.1 请求头默认参数 |
| 65 | 63 | if (StrUtil.isNotEmpty(delegateTask.getTenantId())) { |
| 66 | 64 | headers.add(HEADER_TENANT_ID, delegateTask.getTenantId()); |
| ... | ... | @@ -94,20 +92,4 @@ public class BpmUserTaskListener implements TaskListener { |
| 94 | 92 | } |
| 95 | 93 | // 4. 是否需要后续操作?TODO 芋艿:待定! |
| 96 | 94 | } |
| 97 | - | |
| 98 | - private void parseListenerParam(List<BpmSimpleModelNodeVO.ListenerHandler.ListenerParam> list, | |
| 99 | - Map<String, Object> processVariables, | |
| 100 | - MultiValueMap<String, String> to) { | |
| 101 | - if (CollUtil.isEmpty(list)) { | |
| 102 | - return; | |
| 103 | - } | |
| 104 | - list.forEach(item -> { | |
| 105 | - if (item.getType().equals(BpmListenerParamTypeEnum.FIXED_VALUE.getType())) { | |
| 106 | - to.add(item.getKey(), item.getValue()); | |
| 107 | - } else if (item.getType().equals(BpmListenerParamTypeEnum.FROM_FORM.getType())) { | |
| 108 | - to.add(item.getKey(), processVariables.get(item.getValue()).toString()); | |
| 109 | - } | |
| 110 | - }); | |
| 111 | - } | |
| 112 | - | |
| 113 | 95 | } |
| 114 | 96 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmHttpRequestTrigger.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.service.task.trigger; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 4 | +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; | |
| 5 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; | |
| 7 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 8 | +import jakarta.annotation.Resource; | |
| 9 | +import lombok.extern.slf4j.Slf4j; | |
| 10 | +import org.flowable.engine.runtime.ProcessInstance; | |
| 11 | +import org.springframework.http.HttpEntity; | |
| 12 | +import org.springframework.http.HttpMethod; | |
| 13 | +import org.springframework.http.ResponseEntity; | |
| 14 | +import org.springframework.stereotype.Component; | |
| 15 | +import org.springframework.util.LinkedMultiValueMap; | |
| 16 | +import org.springframework.util.MultiValueMap; | |
| 17 | +import org.springframework.web.client.RestClientException; | |
| 18 | +import org.springframework.web.client.RestTemplate; | |
| 19 | + | |
| 20 | +import java.util.Map; | |
| 21 | + | |
| 22 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * BPM 发送 HTTP 请求触发器 | |
| 26 | + * | |
| 27 | + * @author jason | |
| 28 | + */ | |
| 29 | +@Component | |
| 30 | +@Slf4j | |
| 31 | +public class BpmHttpRequestTrigger implements BpmTrigger { | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private BpmProcessInstanceService processInstanceService; | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private RestTemplate restTemplate; | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public BpmTriggerType getType() { | |
| 41 | + return BpmTriggerType.HTTP_REQUEST; | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public void execute(String processInstanceId, String param) { | |
| 46 | + // 1. 解析 http 请求配置 | |
| 47 | + HttpRequestTriggerSetting httpRequestSetting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class); | |
| 48 | + if (httpRequestSetting == null) { | |
| 49 | + log.error("[execute] HTTP 触发器请求配置为空"); | |
| 50 | + return; | |
| 51 | + } | |
| 52 | + // 2.1 设置请求头 | |
| 53 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); | |
| 54 | + Map<String, Object> processVariables = processInstance.getProcessVariables(); | |
| 55 | + MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); | |
| 56 | + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); | |
| 57 | + SimpleModelUtils.addHttpRequestParam(headers, httpRequestSetting.getHeader(), processVariables); | |
| 58 | + // 2.2 设置请求体 | |
| 59 | + MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | |
| 60 | + SimpleModelUtils.addHttpRequestParam(body, httpRequestSetting.getBody(), processVariables); | |
| 61 | + body.add("processInstanceId", processInstanceId); | |
| 62 | + | |
| 63 | + // 3. 发起请求 | |
| 64 | + HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers); | |
| 65 | + try { | |
| 66 | + ResponseEntity<String> responseEntity = restTemplate.exchange(httpRequestSetting.getUrl(), HttpMethod.POST, | |
| 67 | + requestEntity, String.class); | |
| 68 | + log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); | |
| 69 | + } catch (RestClientException e) { | |
| 70 | + log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); | |
| 71 | + } | |
| 72 | + } | |
| 73 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.service.task.trigger; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerType; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * BPM 触发器接口 | |
| 7 | + * <p> | |
| 8 | + * 处理不同的动作 | |
| 9 | + * | |
| 10 | + * @author jason | |
| 11 | + */ | |
| 12 | +public interface BpmTrigger { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 对应触发器类型 | |
| 16 | + * | |
| 17 | + * @return 触发器类型 | |
| 18 | + */ | |
| 19 | + BpmTriggerType getType(); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 触发器执行 | |
| 23 | + * | |
| 24 | + * @param processInstanceId 流程实例编号 | |
| 25 | + * @param param 触发器参数 | |
| 26 | + */ | |
| 27 | + void execute(String processInstanceId, String param); | |
| 28 | +} | ... | ... |