feature #IBW9PC 上下文表达式搜索&设置,摆脱对上下文的依赖

This commit is contained in:
everywhere.z
2025-03-25 23:21:46 +08:00
parent 5818967e66
commit 9e9d1e28b2
14 changed files with 401 additions and 7 deletions

View File

@@ -476,14 +476,23 @@ public abstract class NodeComponent{
}
}
public <T> List<T> getBindDataList(Class<T> clazz) {
String bindData = getRefNode().getCmpData();
public <T> List<T> getBindDataList(String key, Class<T> clazz) {
String bindData = getRefNode().getBindData(key);
if (StrUtil.isBlank(bindData)) {
return null;
}
return JsonUtil.parseList(bindData, clazz);
}
@SuppressWarnings("unchecked")
public <T> T getContextValue(String expression){
return (T)LiteflowContextRegexMatcher.searchContext(this.getSlot().getContextBeanList(), expression);
}
public void setContextValue(String methodExpress, Object... values){
LiteflowContextRegexMatcher.searchAndSetContext(this.getSlot().getContextBeanList(), methodExpress, values);
}
public Integer getLoopIndex() {
return this.getRefNode().getLoopIndex();
}
@@ -539,9 +548,4 @@ public abstract class NodeComponent{
return originalClass.getName();
}
public static void main(String[] args) {
boolean flag = ReUtil.isMatch(ChainConstant.CONTEXT_SEARCH_REGEX, "${user.name}");
System.out.println(ReUtil.getGroup1(ChainConstant.CONTEXT_SEARCH_REGEX, "${user.name}"));
}
}

View File

@@ -1,14 +1,22 @@
package com.yomahub.liteflow.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* LiteFlow上下文正则表达式匹配器
@@ -54,4 +62,47 @@ public class LiteflowContextRegexMatcher {
return result;
}
public static void searchAndSetContext(List<Tuple> contextList, String methodExpress, Object... args){
// 把上下文数据转换成map形式的key为别名value为上下文
Map<String, Object> contextMap = contextList.stream().collect(
Collectors.toMap(tuple -> tuple.get(0), tuple -> tuple.get(1))
);
List<String> errorList = new ArrayList<>();
boolean flag = false;
String argStr = IntStream.range(0, args.length).mapToObj(
idx -> StrUtil.format("arg{}", idx)
).collect(Collectors.joining(","));
List<TupleOf2<String, Integer>> tupleList = IntStream.range(0, args.length).mapToObj(
idx -> new TupleOf2<>(StrUtil.format("arg{}", idx), idx)
).collect(Collectors.toList());
for(Map.Entry<String, Object> entry : contextMap.entrySet()){
try{
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(StrUtil.format("{}.{}({})", entry.getKey(), methodExpress, argStr));
DefaultContext<String, Object> context = new DefaultContext<>();
context.put(entry.getKey(), entry.getValue());
tupleList.forEach(tuple -> context.put(tuple.getA(), args[tuple.getB()]));
expressRunner.execute(instructionSet, context, errorList, false, false);
flag = true;
break;
}catch (Exception ignore){}
}
// 根据表达式去上下文里搜索相匹配的数据
if (BooleanUtil.isFalse(flag)){
try{
// 如果没有搜到,那么尝试推断表达式是指定的上下文,按照指定上下文的方式去再获取
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(StrUtil.format("contextMap.{}({})", methodExpress, argStr));
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("contextMap", contextMap);
tupleList.forEach(tuple -> context.put(tuple.getA(), args[tuple.getB()]));
expressRunner.execute(instructionSet, context, errorList, false, false);
}catch (Exception ignore){}
}
}
}

View File

@@ -0,0 +1,100 @@
package com.yomahub.liteflow.test.searchContext;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.searchContext.context.Member;
import com.yomahub.liteflow.test.searchContext.context.MemberContext;
import com.yomahub.liteflow.test.searchContext.context.OrderContext;
import com.yomahub.liteflow.test.searchContext.context.UserInfoContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
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;
/**
* springboot环境EL常规的例子测试
*
* @author Bryan.Zhang
*/
@TestPropertySource(value = "classpath:/searchContext/application.properties")
@SpringBootTest(classes = SearchContextSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.searchContext.cmp"})
public class SearchContextSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 测试动态取,动态设置的基础情况
@Test
public void testSearchContext1() throws Exception {
MemberContext memberContext = new MemberContext();
memberContext.setId(31);
memberContext.setName("jack");
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", memberContext, new DefaultContext());
Assertions.assertTrue(response.isSuccess());
MemberContext context = response.getContextBean(MemberContext.class);
Assertions.assertEquals("hello,jack", context.getName());
}
// 测试动态取2个上下文智能设置1个参数
@Test
public void testSearchContext2() throws Exception {
MemberContext memberContext = new MemberContext();
memberContext.setId(31);
memberContext.setName("jack");
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg", memberContext, new DefaultContext());
Assertions.assertTrue(response.isSuccess());
MemberContext context = response.getContextBean(MemberContext.class);
Assertions.assertEquals("hello,jack", context.getName());
}
// 多级动态取2个上下文智能设置多个参数
@Test
public void testSearchContext3() throws Exception {
OrderContext orderContext = new OrderContext();
orderContext.setOrderCode("SO1234");
orderContext.setMember(new Member("M0001","jack"));
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg", orderContext, new DefaultContext());
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getContextBean(DefaultContext.class);
Assertions.assertEquals("M0001", context.getData("test"));
}
// 3个上下文指定上下文取和设置
@Test
public void testSearchContext4() throws Exception {
MemberContext memberContext = new MemberContext();
memberContext.setId(2000);
memberContext.setName("jack");
OrderContext orderContext = new OrderContext();
orderContext.setId(3000);
orderContext.setOrderCode("SO1234");
orderContext.setMember(new Member("M0001","jack"));
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg", memberContext, orderContext, new DefaultContext());
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getContextBean(DefaultContext.class);
Assertions.assertEquals(3000, (Integer) context.getData("test"));
}
// 多个上下文,结合@ContextBean测试
@Test
public void testSearchContext5() throws Exception {
UserInfoContext userInfoContext = new UserInfoContext();
userInfoContext.setInfo("test info");
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg", userInfoContext, new DefaultContext());
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getContextBean(DefaultContext.class);
Assertions.assertEquals("test info", context.getData("test"));
}
}

View File

@@ -0,0 +1,23 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.searchContext.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a1")
public class A1Cmp extends NodeComponent {
@Override
public void process() {
String name = this.getContextValue("name");
this.setContextValue("setName", "hello," + name);
}
}

View File

@@ -0,0 +1,23 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.searchContext.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a2")
public class A2Cmp extends NodeComponent {
@Override
public void process() {
String name = this.getContextValue("name");
this.setContextValue("memberContext.setName", "hello," + name);
}
}

View File

@@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.searchContext.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a3")
public class A3Cmp extends NodeComponent {
@Override
public void process() {
String name = this.getContextValue("member.memberCode");
this.setContextValue("setData", "test", name);
}
}

View File

@@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.searchContext.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a4")
public class A4Cmp extends NodeComponent {
@Override
public void process() {
Integer id = this.getContextValue("orderContext.id");
this.setContextValue("setData", "test", id);
}
}

View File

@@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.searchContext.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a5")
public class A5Cmp extends NodeComponent {
@Override
public void process() {
String str = this.getContextValue("userCx.info");
this.setContextValue("setData", "test", str);
}
}

View File

@@ -0,0 +1,28 @@
package com.yomahub.liteflow.test.searchContext.context;
public class Member {
private String memberCode;
private String memberName;
public Member(String memberCode, String memberName) {
this.memberCode = memberCode;
this.memberName = memberName;
}
public String getMemberCode() {
return memberCode;
}
public void setMemberCode(String memberCode) {
this.memberCode = memberCode;
}
public String getMemberName() {
return memberName;
}
public void setMemberName(String memberName) {
this.memberName = memberName;
}
}

View File

@@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.searchContext.context;
public class MemberContext {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,34 @@
package com.yomahub.liteflow.test.searchContext.context;
public class OrderContext {
private Integer id;
private String orderCode;
private Member member;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderCode() {
return orderCode;
}
public void setOrderCode(String orderCode) {
this.orderCode = orderCode;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
}

View File

@@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.searchContext.context;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean("userCx")
public class UserInfoContext {
private String info;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}

View File

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

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
<flow>
<chain name="chain1">
THEN(a1);
</chain>
<chain name="chain2">
THEN(a2);
</chain>
<chain name="chain3">
THEN(a3);
</chain>
<chain name="chain4">
THEN(a4);
</chain>
<chain name="chain5">
THEN(a5);
</chain>
</flow>