添加可执行实体对象作为构建流程的中间载体

This commit is contained in:
daiqi
2022-03-13 16:30:13 +08:00
parent fcf1230621
commit 9f8794649b
5 changed files with 349 additions and 38 deletions

View File

@@ -1,10 +1,12 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.entity.flow.Chain;
import com.yomahub.liteflow.entity.flow.Condition;
import com.yomahub.liteflow.entity.data.ExecutableEntity;
import com.yomahub.liteflow.entity.flow.Node;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.ExecutableItemNotFoundException;
@@ -14,6 +16,7 @@ import com.yomahub.liteflow.parser.RegexNodeEntity;
import com.yomahub.liteflow.spi.holder.ContextAwareHolder;
import java.util.ArrayList;
import java.util.List;
/**
* Condition基于代码形式的组装器
@@ -58,51 +61,87 @@ public class LiteFlowConditionBuilder {
RegexEntity regexEntity;
String itemExpression;
RegexNodeEntity item;
for (String s : condArray) {
itemExpression = s.trim();
regexEntity = RegexEntity.parse(itemExpression);
item = regexEntity.getItem();
if (FlowBus.containNode(item.getId())) {
Node node = FlowBus.copyNode(item.getId());
node.setTag(regexEntity.getItem().getTag());
this.condition.getNodeList().add(node);
//这里判断是不是条件节点条件节点会含有realItem也就是括号里的node
if (ObjectUtil.isNotNull(regexEntity.getRealItemArray())) {
for (RegexNodeEntity realItem : regexEntity.getRealItemArray()) {
if (FlowBus.containNode(realItem.getId())) {
Node condNode = FlowBus.copyNode(realItem.getId());
condNode.setTag(realItem.getTag());
node.setCondNode(condNode.getId(), condNode);
} else if (hasChain(realItem.getId())) {
Chain chain = FlowBus.getChain(realItem.getId());
node.setCondNode(chain.getChainName(), chain);
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", realItem.getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
}
} else if (hasChain(item.getId())) {
Chain chain = FlowBus.getChain(item.getId());
this.condition.getNodeList().add(chain);
} else {
//元数据没有的话从spring上下文再取一遍
//这部分有2个目的
//一是为了防止标有@Lazy懒加载的组件二是spring负责扫描而用代码的形式加载chain这种情况。
NodeComponent nodeComponent = ContextAwareHolder.loadContextAware().getBean(item.getId());
if (ObjectUtil.isNotNull(nodeComponent)){
FlowBus.addSpringScanNode(item.getId(), nodeComponent);
setValue(item.getId());
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", regexEntity.getItem().getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
// 先转化为执行实体对象
ExecutableEntity executableEntity = convertExecutableEntity(regexEntity);
// 构建节点或流程
setExecutable(executableEntity);
}
return this;
}
// 将正则表达式实体转化为执行实体
private ExecutableEntity convertExecutableEntity(RegexEntity regexEntity) {
RegexNodeEntity item = regexEntity.getItem();
ExecutableEntity executableEntity = new ExecutableEntity(item.getId(), item.getTag());
if (ObjectUtil.isNotNull(regexEntity.getRealItemArray())) {
for (RegexNodeEntity realItem : regexEntity.getRealItemArray()) {
executableEntity.addNodeCondComponent(new ExecutableEntity(realItem.getId(), realItem.getTag()));
}
}
return executableEntity;
}
// 设置执行节点或流程列表
public LiteFlowConditionBuilder setExecutables(List<ExecutableEntity> executableEntities) {
if (CollUtil.isEmpty(executableEntities)) {
return this;
}
for (ExecutableEntity conditionNodeEntity : executableEntities) {
setExecutable(conditionNodeEntity);
}
return this;
}
// 设置执行节点或者流程
public LiteFlowConditionBuilder setExecutable(ExecutableEntity executableEntity) {
if (FlowBus.containNode(executableEntity.getId())) {
Node node = FlowBus.copyNode(executableEntity.getId());
node.setTag(executableEntity.getTag());
this.condition.getNodeList().add(node);
// 构建条件节点-通过是否包含条件节点列表-解析条件节点会含有realItem也就是括号里的node
buildCondNode(node, executableEntity.getNodeCondComponents());
} else if (hasChain(executableEntity.getId())) {
Chain chain = FlowBus.getChain(executableEntity.getId());
this.condition.getNodeList().add(chain);
} else {
//元数据没有的话从spring上下文再取一遍
//这部分有2个目的
//一是为了防止标有@Lazy懒加载的组件二是spring负责扫描而用代码的形式加载chain这种情况。
NodeComponent nodeComponent = ContextAwareHolder.loadContextAware().getBean(executableEntity.getId());
if (ObjectUtil.isNotNull(nodeComponent)){
FlowBus.addSpringScanNode(executableEntity.getId(), nodeComponent);
setExecutable(executableEntity);
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", executableEntity.getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
return this;
}
// 构建条件节点
private void buildCondNode(Node node, List<ExecutableEntity> executableEntities) {
if (CollUtil.isEmpty(executableEntities)) {
return;
}
for (ExecutableEntity realItem : executableEntities) {
if (FlowBus.containNode(realItem.getId())) {
Node condNode = FlowBus.copyNode(realItem.getId());
condNode.setTag(realItem.getTag());
node.setCondNode(condNode.getId(), condNode);
} else if (hasChain(realItem.getId())) {
Chain chain = FlowBus.getChain(realItem.getId());
node.setCondNode(chain.getChainName(), chain);
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", realItem.getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
}
public Condition build(){
return this.condition;
}

View File

@@ -0,0 +1,65 @@
package com.yomahub.liteflow.entity.data;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import java.util.List;
/**
* <pre>
* 执行器的实体类
* </pre>
*
* @author sikadai
* @version 2.6.11
* @since 2022/3/13 15:28
*/
public class ExecutableEntity {
private String id;
private String tag;
private List<ExecutableEntity> nodeCondComponents;
public ExecutableEntity() {
}
public ExecutableEntity(String id, String tag) {
this.id = id;
this.tag = tag;
}
public String getId() {
return id;
}
public ExecutableEntity setId(String id) {
this.id = id;
return this;
}
public String getTag() {
return tag;
}
public ExecutableEntity setTag(String tag) {
this.tag = tag;
return this;
}
public List<ExecutableEntity> getNodeCondComponents() {
return nodeCondComponents;
}
public ExecutableEntity setNodeCondComponents(List<ExecutableEntity> nodeCondComponents) {
this.nodeCondComponents = nodeCondComponents;
return this;
}
public ExecutableEntity addNodeCondComponent(ExecutableEntity conditionNodeEntity) {
if (CollUtil.isEmpty(this.nodeCondComponents)) {
this.nodeCondComponents = Lists.newArrayList();
}
this.nodeCondComponents.add(conditionNodeEntity);
return this;
}
}

View File

@@ -7,6 +7,7 @@ import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.core.FlowExecutorHolder;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.LiteflowResponse;
import com.yomahub.liteflow.entity.data.ExecutableEntity;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.test.BaseTest;
@@ -145,4 +146,72 @@ public class BuilderTest extends BaseTest {
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getSlot().getExecuteStepStr());
}
//基于普通组件的builder模式测试
@Test
public void testBuilderForConditionNode() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ACmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(BCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(CCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(DCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ECmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(FCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(GCmp.class)
.build();
LiteFlowChainBuilder.createChain().setChainName("chain2").setCondition(
LiteFlowConditionBuilder.createThenCondition()
.setExecutable(new ExecutableEntity().setId("c"))
.setExecutable(new ExecutableEntity().setId("d"))
.build()
).build();
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createWhenCondition()
.setExecutable(new ExecutableEntity().setId("a").setTag("hello"))
.setExecutable(new ExecutableEntity().setId("b"))
.build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setExecutable(
new ExecutableEntity().setId("e")
.addNodeCondComponent(new ExecutableEntity().setId("f").setTag("FHello"))
.addNodeCondComponent(new ExecutableEntity().setId("g"))
.addNodeCondComponent(new ExecutableEntity().setId("chain2")
)).build()
).build();
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getSlot().getExecuteStepStr());
}
}

View File

@@ -6,6 +6,7 @@ import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.LiteflowResponse;
import com.yomahub.liteflow.entity.data.ExecutableEntity;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.builder.cmp1.ACmp;
@@ -150,4 +151,73 @@ public class BuilderSpringbootTest1 extends BaseTest {
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getSlot().getExecuteStepStr());
}
//基于普通组件的builder模式测试
@Test
public void testBuilderForConditionNode() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ACmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(BCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(CCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(DCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ECmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(FCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(GCmp.class)
.build();
LiteFlowChainBuilder.createChain().setChainName("chain2").setCondition(
LiteFlowConditionBuilder.createThenCondition()
.setExecutable(new ExecutableEntity().setId("c"))
.setExecutable(new ExecutableEntity().setId("d"))
.build()
).build();
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createWhenCondition()
.setExecutable(new ExecutableEntity().setId("a").setTag("hello"))
.setExecutable(new ExecutableEntity().setId("b"))
.build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setExecutable(
new ExecutableEntity().setId("e")
.addNodeCondComponent(new ExecutableEntity().setId("f").setTag("FHello"))
.addNodeCondComponent(new ExecutableEntity().setId("g"))
.addNodeCondComponent(new ExecutableEntity().setId("chain2")
)).build()
).build();
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getSlot().getExecuteStepStr());
}
}

View File

@@ -6,6 +6,7 @@ import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.LiteflowResponse;
import com.yomahub.liteflow.entity.data.ExecutableEntity;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.builder.cmp1.*;
@@ -131,7 +132,7 @@ public class BuilderSpringTest1 extends BaseTest {
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createWhenCondition()
.setValue("a,b").build()
.setValue("a[hello],b").build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setValue("e(f|g|chain2)").build()
@@ -140,4 +141,71 @@ public class BuilderSpringTest1 extends BaseTest {
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
}
//基于普通组件的builder模式测试
@Test
public void testBuilderForConditionNode() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ACmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(BCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(CCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(DCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(ECmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(FCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setTypeCode(NodeTypeEnum.COMMON.getCode())
.setNodeComponentClazz(GCmp.class)
.build();
LiteFlowChainBuilder.createChain().setChainName("chain2").setCondition(
LiteFlowConditionBuilder.createThenCondition()
.setExecutable(new ExecutableEntity().setId("c"))
.setExecutable(new ExecutableEntity().setId("d"))
.build()
).build();
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createWhenCondition()
.setExecutable(new ExecutableEntity().setId("a").setTag("hello"))
.setExecutable(new ExecutableEntity().setId("b"))
.build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setExecutable(
new ExecutableEntity().setId("e")
.addNodeCondComponent(new ExecutableEntity().setId("f").setTag("FHello"))
.addNodeCondComponent(new ExecutableEntity().setId("g"))
.addNodeCondComponent(new ExecutableEntity().setId("chain2")
)).build()
).build();
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
}
}