feature #I9PVQ7 决策路由中增加namespace,可以执行指定命名空间的决策路由

This commit is contained in:
everywhere.z 2024-05-16 17:21:45 +08:00
parent 1dbcc9bba7
commit c8062d6585
13 changed files with 232 additions and 14 deletions

View File

@ -233,6 +233,11 @@ public class LiteFlowChainELBuilder {
}
}
public LiteFlowChainELBuilder setNamespace(String nameSpace){
this.chain.setNamespace(nameSpace);
return this;
}
/**
* EL表达式校验
* @param elStr EL表达式

View File

@ -32,6 +32,10 @@ public interface ChainConstant {
String LANGUAGE = "language";
String NAMESPACE = "namespace";
String DEFAULT_NAMESPACE = "default";
String VALUE = "value";
String ANY = "any";

View File

@ -13,6 +13,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.enums.ChainExecuteModeEnum;
import com.yomahub.liteflow.enums.InnerChainTypeEnum;
import com.yomahub.liteflow.enums.ParseModeEnum;
@ -283,7 +284,11 @@ public class FlowExecutor {
}
public List<LiteflowResponse> executeRouteChain(Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(param, null, contextBeanClazzArray, null);
return this.executeWithRoute(null, param, null, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(namespace, param, null, contextBeanClazzArray, null);
}
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) {
@ -291,23 +296,35 @@ public class FlowExecutor {
}
public List<LiteflowResponse> executeRouteChain(Object param, Object... contextBeanArray){
return this.executeWithRoute(param, null, null, contextBeanArray);
return this.executeWithRoute(null, param, null, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Object... contextBeanArray){
return this.executeWithRoute(namespace, param, null, null, contextBeanArray);
}
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChainWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(param, requestId, contextBeanClazzArray, null);
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(null, param, requestId, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(namespace, param, requestId, contextBeanClazzArray, null);
}
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
return this.execute2Resp(chainId, param, requestId, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChainWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(param, requestId, null, contextBeanArray);
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(null, param, requestId, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(namespace, param, requestId, null, contextBeanArray);
}
// 调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入
@ -353,8 +370,8 @@ public class FlowExecutor {
return LiteflowResponse.newMainResponse(slot);
}
private List<LiteflowResponse> executeWithRoute(Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
List<Slot> slotList = doExecuteWithRoute(param, requestId, contextBeanClazzArray, contextBeanArray);
private List<LiteflowResponse> executeWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
List<Slot> slotList = doExecuteWithRoute(namespace, param, requestId, contextBeanClazzArray, contextBeanArray);
return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList());
}
@ -522,15 +539,23 @@ public class FlowExecutor {
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
}
private List<Slot> doExecuteWithRoute(Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
private List<Slot> doExecuteWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
if (FlowBus.needInit()) {
init(true);
}
List<Chain> routeChainList = FlowBus.getChainMap().values().stream().filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList());
if (StrUtil.isBlank(namespace)){
namespace = ChainConstant.DEFAULT_NAMESPACE;
}
String finalNamespace = namespace;
List<Chain> routeChainList = FlowBus.getChainMap().values().stream()
.filter(chain -> chain.getNamespace().equals(finalNamespace))
.filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList());
if (CollUtil.isEmpty(routeChainList)){
throw new RouteChainNotFoundException("cannot find any route chain");
String errorMsg = StrUtil.format("no route found for namespace[{}]", finalNamespace);
throw new RouteChainNotFoundException(errorMsg);
}
String finalRequestId;
@ -601,7 +626,7 @@ public class FlowExecutor {
}
}).filter(Objects::nonNull).collect(Collectors.toList());
LOG.info("There are {} chains that matched the route.", resultSlotList.size());
LOG.info("chain namespace:[{}], total size:[{}], matched size:[{}]", namespace, routeChainList.size(), resultSlotList.size());
return resultSlotList;
}

View File

@ -40,6 +40,8 @@ public class Chain implements Executable{
private boolean isCompiled = true;
private String namespace;
public Chain(String chainName) {
this.chainId = chainName;
}
@ -195,4 +197,12 @@ public class Chain implements Executable{
public void setCompiled(boolean compiled) {
isCompiled = compiled;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@ -1,5 +1,6 @@
package com.yomahub.liteflow.parser.helper;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
@ -24,6 +25,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import static com.yomahub.liteflow.common.ChainConstant.*;
@ -310,9 +312,11 @@ public class ParserHelper {
// 构建chainBuilder
String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
String namespace = chainNode.get(NAMESPACE) == null? DEFAULT_NAMESPACE : chainNode.get(NAMESPACE).textValue();
JsonNode routeJsonNode = chainNode.get(ROUTE);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace);
// 如果有route这个标签说明是决策表chain
// 决策表链路必须有route和body这两个标签
@ -339,9 +343,11 @@ public class ParserHelper {
// 构建chainBuilder
String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
String namespace = StrUtil.blankToDefault(e.attributeValue(NAMESPACE), DEFAULT_NAMESPACE);
Element routeElement = e.element(ROUTE);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace);
// 如果有route这个标签说明是决策表chain
// 决策表链路必须有route和body这两个标签
@ -364,6 +370,7 @@ public class ParserHelper {
}
}
builder.build();
}

View File

@ -14,9 +14,12 @@
class CDATA #IMPLIED
file CDATA #IMPLIED
language (qlexpress|groovy|js|python|lua|aviator|java) #IMPLIED
enable (true|false) #IMPLIED
>
<!ATTLIST chain
id CDATA #IMPLIED
name CDATA #IMPLIED
extends CDATA #IMPLIED
enable (true|false) #IMPLIED
namespace CDATA #IMPLIED
>

View File

@ -0,0 +1,66 @@
package com.yomahub.liteflow.test.namespace;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.NoMatchedRouteChainException;
import com.yomahub.liteflow.exception.RouteChainNotFoundException;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import javax.annotation.Resource;
import java.util.List;
/**
* springboot环境EL常规的例子测试
*
* @author Bryan.Zhang
*/
@TestPropertySource(value = "classpath:/namespace/application.properties")
@SpringBootTest(classes = RouteSpringbootNamespaceTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.namespace.cmp" })
public class RouteSpringbootNamespaceTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// n1 space中的两个链路都能匹配
@Test
public void testNamespaceRoute1() throws Exception {
List<LiteflowResponse> responseList = flowExecutor.executeRouteChain("n1", 15, DefaultContext.class);
LiteflowResponse response1 = responseList.stream().filter(
liteflowResponse -> liteflowResponse.getChainId().equals("r_chain1")
).findFirst().orElse(null);
assert response1 != null;
Assertions.assertTrue(response1.isSuccess());
Assertions.assertEquals("b==>a", response1.getExecuteStepStr());
LiteflowResponse response2 = responseList.stream().filter(
liteflowResponse -> liteflowResponse.getChainId().equals("r_chain2")
).findFirst().orElse(null);
assert response2 != null;
Assertions.assertTrue(response2.isSuccess());
Assertions.assertEquals("a==>b", response2.getExecuteStepStr());
}
// n1这个namespace中没有规则被匹配上
@Test
public void testNamespaceRoute2() throws Exception {
Assertions.assertThrows(NoMatchedRouteChainException.class, () -> flowExecutor.executeRouteChain("n1", 8, DefaultContext.class));
}
// 没有n3这个namespace
@Test
public void testNamespaceRoute3() throws Exception {
Assertions.assertThrows(RouteChainNotFoundException.class, () -> flowExecutor.executeRouteChain("n3", 8, DefaultContext.class));
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,13 @@
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeBooleanComponent;
@LiteflowComponent("r1")
public class R1 extends NodeBooleanComponent {
@Override
public boolean processBoolean() throws Exception {
int testInt = this.getRequestData();
return testInt >= 10 && testInt <= 20;
}
}

View File

@ -0,0 +1,13 @@
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeBooleanComponent;
@LiteflowComponent("r2")
public class R2 extends NodeBooleanComponent {
@Override
public boolean processBoolean() throws Exception {
int testInt = this.getRequestData();
return testInt > 100;
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=namespace/flow.el.xml

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
<flow>
<chain name="r_chain1" namespace="n1">
<route>
r1
</route>
<body>
THEN(b,a);
</body>
</chain>
<chain name="r_chain2" namespace="n1">
<route>
OR(r1,r2)
</route>
<body>
THEN(a,b);
</body>
</chain>
<chain name="r_chain3" namespace="n2">
<route>
r2
</route>
<body>
THEN(b,b);
</body>
</chain>
</flow>