Просмотр исходного кода

enhancement #I8QJE1 增加映射关键字SER和PAR

everywhere.z 1 год назад
Родитель
Сommit
7e1565178d
28 измененных файлов с 1208 добавлено и 0 удалено
  1. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java
  2. 4 0
      liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java
  3. 26 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELBus.java
  4. 131 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ParELWrapper.java
  5. 123 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SerELWrapper.java
  6. 214 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ParELBuilderTest.java
  7. 211 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SerELBuilderTest.java
  8. 64 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/SerAndParSpringbootTest.java
  9. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ACmp.java
  10. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/BCmp.java
  11. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/CCmp.java
  12. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/DCmp.java
  13. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ESwitchCmp.java
  14. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/FCmp.java
  15. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/GSwitchCmp.java
  16. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/HCmp.java
  17. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ICmp.java
  18. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/JCmp.java
  19. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/KCmp.java
  20. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/MCmp.java
  21. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/NCmp.java
  22. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/PCmp.java
  23. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/QCmp.java
  24. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/RCmp.java
  25. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/XSwitchCmp.java
  26. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ZCmp.java
  27. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/serAndPar/application.properties
  28. 54 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/serAndPar/flow.xml

+ 2 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java

@@ -60,6 +60,8 @@ public class LiteFlowChainELBuilder {
 		// 初始化QLExpress的Runner
 		EXPRESS_RUNNER.addFunction(ChainConstant.THEN, new ThenOperator());
 		EXPRESS_RUNNER.addFunction(ChainConstant.WHEN, new WhenOperator());
+		EXPRESS_RUNNER.addFunction(ChainConstant.SER, new ThenOperator());
+		EXPRESS_RUNNER.addFunction(ChainConstant.PAR, new WhenOperator());
 		EXPRESS_RUNNER.addFunction(ChainConstant.SWITCH, new SwitchOperator());
 		EXPRESS_RUNNER.addFunction(ChainConstant.PRE, new PreOperator());
 		EXPRESS_RUNNER.addFunction(ChainConstant.FINALLY, new FinallyOperator());

+ 4 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java

@@ -38,6 +38,10 @@ public interface ChainConstant {
 
 	String WHEN = "WHEN";
 
+	String SER = "SER";
+
+	String PAR = "PAR";
+
 	String SWITCH = "SWITCH";
 
 	String PRE = "PRE";

+ 26 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELBus.java

@@ -45,6 +45,32 @@ public class ELBus {
         return new WhenELWrapper(elWrappers);
     }
 
+    public static SerELWrapper ser(ELWrapper... elWrappers){
+        checkNotBooleanArgs(elWrappers);
+        return new SerELWrapper(elWrappers);
+    }
+
+    public static SerELWrapper ser(Object ... objects){
+        ELWrapper[] elWrappers = convertToNonLogicOpt(objects);
+        return new SerELWrapper(elWrappers);
+    }
+
+    /**
+     * 创建 when 并行组件
+     *
+     * @param elWrappers 并行组件的子组件
+     * @return {@link WhenELWrapper}
+     */
+    public static ParELWrapper par(ELWrapper... elWrappers){
+        checkNotBooleanArgs(elWrappers);
+        return new ParELWrapper(elWrappers);
+    }
+
+    public static ParELWrapper par(Object ... objects){
+        ELWrapper[] elWrappers = convertToNonLogicOpt(objects);
+        return new ParELWrapper(elWrappers);
+    }
+
 
     /**
      * 创建 if 条件判断表达式

+ 131 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ParELWrapper.java

@@ -0,0 +1,131 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 并行组件
+ * 参数数量任意
+ * 参数类型非与或非表达式
+ *
+ * 支持定义 id tag data maxWaitSeconds 属性
+ * 支持调用 any ignoreError customThreadExecutor mustExecuteList 并行组件特有方法
+ * any 和 must语义冲突,不支持同时定义
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class ParELWrapper extends ELWrapper {
+
+    private boolean any;
+    private boolean ignoreError;
+    private String customThreadExecutor;
+    private final List<String> mustExecuteList;
+
+    public ParELWrapper(ELWrapper... elWrappers) {
+        this.addWrapper(elWrappers);
+        this.mustExecuteList = new ArrayList<>();
+    }
+
+    public ParELWrapper par(Object ... objects){
+        ELWrapper[] elWrappers = ELBus.convertToNonLogicOpt(objects);
+        // 校验与或非表达式
+        this.addWrapper(elWrappers);
+        return this;
+    }
+
+    public ParELWrapper any(boolean any) {
+        this.any = any;
+        return this;
+    }
+
+    public ParELWrapper ignoreError(boolean ignoreError) {
+        this.ignoreError = ignoreError;
+        return this;
+    }
+
+    public ParELWrapper customThreadExecutor(String customThreadExecutor){
+        this.customThreadExecutor = customThreadExecutor;
+        return this;
+    }
+
+    public ParELWrapper must(String ... mustExecuteWrappers){
+        this.mustExecuteList.addAll(Arrays.asList(mustExecuteWrappers));
+        return this;
+    }
+
+    @Override
+    public ParELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public ParELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public ParELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        checkMaxWaitSeconds();
+
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("PAR(");
+        processWrapperNewLine(sb, depth);
+        // 处理子表达式输出
+        for (int i = 0; i < this.getElWrapperList().size(); i++) {
+            if (i > 0){
+                sb.append(",");
+                processWrapperNewLine(sb, depth);
+            }
+            sb.append(this.getElWrapperList().get(i).toEL(sonDepth, paramContext));
+        }
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // WHEN的私有语义输出
+        if (this.any){
+            sb.append(".any(true)");
+        }
+        if(this.ignoreError){
+            sb.append(".ignoreError(true)");
+        }
+        if(StrUtil.isNotBlank(customThreadExecutor)){
+            sb.append(StrUtil.format(".threadPool(\"{}\")", customThreadExecutor));
+        }
+        if(CollectionUtil.isNotEmpty(mustExecuteList)){
+            // 校验must 语义与 any语义冲突
+            if (this.any){
+                throw new IllegalArgumentException("'.must()' and '.any()' can use in when component at the same time!");
+            }
+            // 处理must子表达式输出
+            sb.append(".must(");
+            for(int i = 0; i < mustExecuteList.size(); i++){
+                if(i > 0){
+                    sb.append(", ");
+                }
+                sb.append(StrUtil.format("\"{}\"", mustExecuteList.get(i)));
+            }
+            sb.append(")");
+        }
+
+        // 处理公共属性输出
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 123 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SerELWrapper.java

@@ -0,0 +1,123 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 串行组件
+ * 允许调用PRE、FINALLY任意次
+ * 参数数量任意,参数类型为非与或非表达式
+ *
+ * 支持定义 id tag data maxWaitSeconds属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class SerELWrapper extends ELWrapper {
+    private final List<PreELWrapper> preELWrapperList;
+    private final List<FinallyELWrapper> finallyELWrapperList;
+
+    public SerELWrapper(ELWrapper... elWrappers) {
+        preELWrapperList = new ArrayList<>();
+        finallyELWrapperList = new ArrayList<>();
+        this.addWrapper(elWrappers);
+    }
+
+    public SerELWrapper ser(Object ... objects){
+        ELWrapper[] elWrappers = ELBus.convertToNonLogicOpt(objects);
+        // 校验与或非表达式
+        this.addWrapper(elWrappers);
+        return this;
+    }
+
+    protected void addPreElWrapper(PreELWrapper preElWrapper){
+        this.preELWrapperList.add(preElWrapper);
+    }
+
+    protected void addFinallyElWrapper(FinallyELWrapper finallyElWrapper){
+        this.finallyELWrapperList.add(finallyElWrapper);
+    }
+
+    /**
+     * 在当前串行组件下创建前置组件
+     *
+     * @param objects 前置组件的子组件
+     * @return {@link SerELWrapper}
+     */
+    public SerELWrapper pre(Object ... objects){
+        addPreElWrapper(new PreELWrapper(objects));
+        return this;
+    }
+
+    /**
+     * 在当前串行组件下创建前置组件
+     *
+     * @param objects 后置组件的子组件
+     * @return {@link SerELWrapper}
+     */
+    public SerELWrapper finallyOpt(Object ... objects){
+        addFinallyElWrapper(new FinallyELWrapper(objects));
+        return this;
+    }
+
+    @Override
+    public SerELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public SerELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public SerELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        checkMaxWaitSeconds();
+
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("SER(");
+        processWrapperNewLine(sb, depth);
+
+        // 处理前置组件输出
+        for (PreELWrapper preElWrapper : this.preELWrapperList) {
+            sb.append(StrUtil.format("{},", preElWrapper.toEL(sonDepth, paramContext)));
+            processWrapperNewLine(sb, depth);
+        }
+        // 处理子表达式输出
+        for (int i = 0; i < this.getElWrapperList().size(); i++) {
+            if (i > 0){
+                sb.append(",");
+                processWrapperNewLine(sb, depth);
+            }
+            sb.append(this.getElWrapperList().get(i).toEL(sonDepth, paramContext));
+        }
+        // 处理后置组件输出
+        for (FinallyELWrapper finallyElWrapper : this.finallyELWrapperList) {
+            sb.append(",");
+            processWrapperNewLine(sb, depth);
+            sb.append(finallyElWrapper.toEL(sonDepth, paramContext));
+        }
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // 处理公共属性输出
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+
+
+}

+ 214 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ParELBuilderTest.java

@@ -0,0 +1,214 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
+import com.yomahub.liteflow.builder.el.ParELWrapper;
+import com.yomahub.liteflow.builder.el.WhenELWrapper;
+import com.yomahub.liteflow.test.BaseTest;
+import com.yomahub.liteflow.util.JsonUtil;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 并行组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = ParELBuilderTest.class)
+@EnableAutoConfiguration
+public class ParELBuilderTest extends BaseTest {
+    // then组件测试
+    @Test
+    public void testPar1(){
+        String expectedStr = "PAR(node(\"a\"),node(\"b\"));";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar2(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tnode(\"b\")\n);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").toEL(true)));
+    }
+    // then组件then方法调用测试
+    @Test
+    public void testPar3(){
+        String expectedStr = "PAR(node(\"a\"),node(\"b\"),node(\"c\"));";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").par("c").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").par("c").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar4(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").par("c").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").par("c").toEL(true)));
+    }
+    // then组件嵌套调用测试
+    @Test
+    public void testPar5(){
+        String expectedStr = "PAR(node(\"a\"),PAR(node(\"b\"),node(\"c\")),node(\"d\"));";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par("c")).par("d").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par("c")).par("d").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar6(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tPAR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par("c")).par("d").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par("c")).par("d").toEL(true)));
+    }
+    // PAR特有属性测试 any ignoreError customThreadExecutor must
+    @Test
+    public void testPar7(){
+        String expectedStr = "PAR(node(\"a\"),node(\"b\"),PAR(node(\"c\"),node(\"d\")).any(true).threadPool(\"com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1\").id(\"node1\")).ignoreError(true).must(\"a\", \"task1\", \"node1\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b", ELBus.par("c").par("d").id("node1").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b", ELBus.par("c").par("d").id("node1").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar8(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tPAR(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).any(true).threadPool(\"com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1\").id(\"node1\")\n).ignoreError(true).must(\"a\", \"task1\", \"node1\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b", ELBus.par("c").par("d").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b", ELBus.par("c").par("d").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true)));
+    }
+    // maxWaitSeconds 属性测试
+    @Test
+    public void testPar9(){
+        String expectedStr = "PAR(node(\"a\"),node(\"b\")).maxWaitSeconds(5);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").maxWaitSeconds(5).toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").maxWaitSeconds(5).toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar10(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", "b").maxWaitSeconds(5).toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", "b").maxWaitSeconds(5).toEL(true)));
+    }
+    // 属性设置测试
+    @Test
+    public void testPar11(){
+        String expectedStr = "PAR(node(\"a\"),PAR(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par("c").id("this is a id")).par("d").tag("this is a tag").toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par("c").id("this is a id")).par("d").tag("this is a tag").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar12(){
+        String expectedStr = "PAR(\n\tnode(\"a\"),\n\tPAR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par("c").id("this is a id")).par("d").tag("this is a tag").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par("c").id("this is a id")).par("d").tag("this is a tag").toEL(true)));
+    }
+    // data属性测试
+    @Test
+    public void testPar13(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        System.out.println(JsonUtil.toJsonString(name2Value));
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(node(\"a\"),PAR(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(false));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(false)));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar14(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(\n\tnode(\"a\"),\n\tPAR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(true)));
+    }
+    // data属性测试 Json字符串赋值data
+    @Test
+    public void testPar15(){
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(node(\"a\"),PAR(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}")).id("this is a id")).par("d").tag("this is a tag").toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}")).id("this is a id")).par("d").tag("this is a tag").toEL()));
+    }
+    // 格式化输出测试 Json字符串赋值data
+    @Test
+    public void testPar16(){
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(\n\tnode(\"a\"),\n\tPAR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}")).id("this is a id")).par("d").tag("this is a tag").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}")).id("this is a id")).par("d").tag("this is a tag").toEL(true)));
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data属性测试
+    @Test
+    public void testPar17(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(node(\"a\"),PAR(node(\"b\"),node(\"c\").data(whenData)).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testPar18(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String expectedStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nPAR(\n\tnode(\"a\"),\n\tPAR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\").data(whenData)\n\t).id(\"this is a id\"),\n\tnode(\"d\")\n).tag(\"this is a tag\");";
+        Assertions.assertEquals(expectedStr,
+                ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.par("a", ELBus.par("b").par(ELBus.node("c").data("whenData", name2Value)).id("this is a id")).par("d").tag("this is a tag").toEL(true)));
+    }
+
+    @Test
+    public void testPAR(){
+        ParELWrapper el = ELBus.par("a", "b", "c").customThreadExecutor("com.yomahub.liteflow.test.builder.customTreadExecutor.CustomThreadExecutor1");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(el.toEL()));
+    }
+}

+ 211 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SerELBuilderTest.java

@@ -0,0 +1,211 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
+import com.yomahub.liteflow.test.BaseTest;
+import com.yomahub.liteflow.util.JsonUtil;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 串行组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = SerELBuilderTest.class)
+@EnableAutoConfiguration
+public class SerELBuilderTest extends BaseTest {
+
+    // then组件测试
+    @Test
+    public void testSer1(){
+        Assertions.assertEquals("SER(node(\"a\"),node(\"b\"));",
+                ELBus.ser("a", "b").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", "b").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer2(){
+        Assertions.assertEquals("SER(\n\tnode(\"a\")," +
+                        "\n\tnode(\"b\")\n);",
+                ELBus.ser("a", "b").toEL(true));
+        System.out.println("SER(\n\tnode(\"a\")," +
+                "\n\tnode(\"b\")\n);");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", "b").toEL(true)));
+    }
+    // then组件then方法调用测试
+    @Test
+    public void testSer3(){
+        Assertions.assertEquals("SER(node(\"a\"),node(\"b\"),node(\"c\"));",
+                ELBus.ser("a", "b").ser("c").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", "b").ser("c").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer4(){
+        Assertions.assertEquals("SER(\n\tnode(\"a\"),\n\tnode(\"b\")," +
+                        "\n\tnode(\"c\")\n);",
+                ELBus.ser("a", "b").ser("c").toEL(true));
+        System.out.println("SER(\n\tnode(\"a\"),\n\tnode(\"b\")," +
+                "\n\tnode(\"c\")\n);");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", "b").ser("c").toEL(true)));
+    }
+    // then组件嵌套调用测试
+    @Test
+    public void testSer5(){
+        Assertions.assertEquals("SER(node(\"a\"),SER(node(\"b\"),node(\"c\")),node(\"d\"));",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").toEL());
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer6(){
+        Assertions.assertEquals("SER(\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").toEL(true));
+        System.out.println("SER(\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").toEL(true)));
+    }
+    // pre组件测试
+    @Test
+    public void testSer7(){
+        Assertions.assertEquals("SER(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),SER(node(\"b\"),node(\"c\")),node(\"d\"));",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").pre("pp").toEL());
+        System.out.println("SER(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),SER(node(\"b\"),node(\"c\")),node(\"d\"));");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").pre("pp").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer8(){
+        Assertions.assertEquals("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").pre("pp").toEL(true));
+        System.out.println("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n);");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").pre("pp").toEL(true)));
+    }
+    // pre finally 格式测试
+    @Test
+    public void testSer9(){
+        Assertions.assertEquals("SER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")));",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").finallyOpt("f").toEL());
+        System.out.println("SER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")));");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").finallyOpt("f").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer10(){
+        Assertions.assertEquals("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n);",
+                ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").finallyOpt("f").toEL(true));
+        System.out.println("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n);");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c")).ser("d").pre("p").finallyOpt("f").toEL(true)));
+    }
+    // 属性设置测试
+    @Test
+    public void testSer11(){
+        Assertions.assertEquals("SER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser("d").pre("p").finallyOpt("f").toEL());
+        System.out.println("SER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser("d").pre("p").finallyOpt("f").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer12(){
+        Assertions.assertEquals("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser("d").pre("p").finallyOpt("f").toEL(true));
+        System.out.println("SER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser("d").pre("p").finallyOpt("f").toEL(true)));
+    }
+    // data属性测试
+    @Test
+    public void testSer13(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        System.out.println(JsonUtil.toJsonString(name2Value));
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL());
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer14(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true));
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true)));
+    }
+    // data属性测试 Json字符串赋值data
+    @Test
+    public void testSer15(){
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}")).pre("p").finallyOpt("f").toEL());
+//        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}")).pre("p").finallyOpt("f").toEL()));
+    }
+    // 格式化输出测试 Json字符串赋值data
+    @Test
+    public void testSer16(){
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}")).pre("p").finallyOpt("f").toEL(true));
+//        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}")).pre("p").finallyOpt("f").toEL(true)));
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data属性测试
+    @Test
+    public void testSer17(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL());
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(PRE(node(\"p\")),node(\"a\"),SER(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\").data(thenData),FINALLY(node(\"f\"))).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer18(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        Assertions.assertEquals("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");",
+                ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true));
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nSER(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tSER(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\"),\n\tnode(\"d\").data(thenData),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n).tag(\"this is a tag\");");
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a", ELBus.ser("b").ser("c").id("this is a id")).tag("this is a tag").ser(ELBus.node("d").data("thenData", name2Value)).pre("p").finallyOpt("f").toEL(true)));
+    }
+    // maxWaitSecond测试
+    @Test
+    public void testSer19(){
+        String expectedStr = "SER(node(\"a\"),node(\"b\")).maxWaitSeconds(5);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.ser("a").ser("b").maxWaitSeconds(5).toEL());
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a").ser("b").maxWaitSeconds(5).toEL()));
+    }
+    // 格式化输出测试
+    @Test
+    public void testSer20(){
+        String expectedStr = "SER(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5);";
+        Assertions.assertEquals(expectedStr,
+                ELBus.ser("a").ser("b").maxWaitSeconds(5).toEL(true));
+        System.out.println(expectedStr);
+        Assertions.assertTrue(LiteFlowChainELBuilder.validate(ELBus.ser("a").ser("b").maxWaitSeconds(5).toEL(true)));
+    }
+}

+ 64 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/SerAndParSpringbootTest.java

@@ -0,0 +1,64 @@
+package com.yomahub.liteflow.test.serAndPar;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+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:/serAndPar/application.properties")
+@SpringBootTest(classes = SerAndParSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({ "com.yomahub.liteflow.test.serAndPar.cmp" })
+public class SerAndParSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 最简单的情况
+	@Test
+	public void test1() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+	}
+
+	// switch节点最简单的测试用例
+	@Test
+	public void test2() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertTrue(response.isSuccess());
+	}
+
+	// then,when,switch混用的稍微复杂点的用例,switch跳到一个then上
+	@Test
+	public void test3() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertTrue(response.isSuccess());
+	}
+
+	// 一个非常复杂的例子,可以看base目录下的img.png这个图示
+	@Test
+	public void test4() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertTrue(response.isSuccess());
+	}
+
+	// 用变量来声明短流程
+	@Test
+	public void test5() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertTrue(response.isSuccess());
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ACmp.java

@@ -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.serAndPar.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!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/BCmp.java

@@ -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.serAndPar.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!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/CCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("c")
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/DCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("d")
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ESwitchCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("e")
+public class ESwitchCmp extends NodeSwitchComponent {
+
+	@Override
+	public String processSwitch() throws Exception {
+		return "d";
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/FCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("f")
+public class FCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("FCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/GSwitchCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("g")
+public class GSwitchCmp extends NodeSwitchComponent {
+
+	@Override
+	public String processSwitch() throws Exception {
+		return "then_1001";
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/HCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("h")
+public class HCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("HCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ICmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("i")
+public class ICmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ICmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/JCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("j")
+public class JCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("JCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/KCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("k")
+public class KCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("KCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/MCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("m")
+public class MCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("MCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/NCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("n")
+public class NCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("NCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/PCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("p")
+public class PCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("PCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/QCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("q")
+public class QCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("QCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/RCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("r")
+public class RCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("RCmp executed!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/XSwitchCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("x")
+public class XSwitchCmp extends NodeSwitchComponent {
+
+	@Override
+	public String processSwitch() throws Exception {
+		return "w01";
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/serAndPar/cmp/ZCmp.java

@@ -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.serAndPar.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("z")
+public class ZCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ZCmp executed!");
+	}
+
+}

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/serAndPar/application.properties

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

+ 54 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/serAndPar/flow.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
+<flow>
+    <chain name="chain1">
+        SER(a,b,PAR(c,d));
+    </chain>
+
+    <chain name="chain2">
+        SER(
+            a,b,
+            SWITCH(e).to(d,f)
+        );
+    </chain>
+
+    <chain name="chain3">
+        SER(
+            a,
+            PAR(
+                c,
+                SWITCH(g).to(b, d, SER(h,i).id("then_1001"))
+            )
+        );
+    </chain>
+
+    <chain name="chain4">
+        SER(
+            a,b,
+            PAR(
+                SER(c, PAR(j,k)),
+                d,
+                SER(h, i)
+            ),
+            SWITCH(x).to(
+                m,
+                n,
+                PAR(q, SER(p, r)).id("w01")
+            ),
+            z
+        );
+    </chain>
+
+    <chain name="chain5">
+        t1 = SER(c, PAR(j,k));
+        w1 = PAR(q, SER(p, r)).id("w01");
+        t2 = SER(h, i);
+
+        SER(
+            a,b,
+            PAR(t1, d, t2 ),
+            SWITCH(x).to(m, n, w1),
+            z
+        );
+    </chain>
+</flow>