瀏覽代碼

EL表达式流式装配开发与测试

gezuao 1 年之前
父節點
當前提交
029ff97e53
共有 31 個文件被更改,包括 4092 次插入0 次删除
  1. 28 0
      liteflow-el-builder/pom.xml
  2. 99 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java
  3. 103 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java
  4. 316 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELBus.java
  5. 206 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java
  6. 115 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java
  7. 89 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java
  8. 333 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java
  9. 77 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java
  10. 137 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java
  11. 122 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java
  12. 84 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java
  13. 98 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java
  14. 90 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java
  15. 121 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java
  16. 149 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java
  17. 154 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java
  18. 84 0
      liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java
  19. 56 0
      liteflow-testcase-el/liteflow-testcase-el-builder/pom.xml
  20. 23 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java
  21. 119 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java
  22. 120 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java
  23. 285 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java
  24. 171 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java
  25. 278 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java
  26. 101 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java
  27. 151 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java
  28. 189 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java
  29. 186 0
      liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java
  30. 1 0
      liteflow-testcase-el/pom.xml
  31. 7 0
      pom.xml

+ 28 - 0
liteflow-el-builder/pom.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>liteflow-el-builder</artifactId>
+    <description>liteflow el builder</description>
+
+    <parent>
+        <groupId>com.yomahub</groupId>
+        <artifactId>liteflow</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-json</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 99 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/AndELWrapper.java

@@ -0,0 +1,99 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 与或非表达式中的 与表达式
+ * 参数允许任意数量 参数必须返回true或false
+ *
+ * 使用and()方法加入新的表达式
+ * 支持设置 id tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class AndELWrapper extends ELWrapper{
+
+    public AndELWrapper(ELWrapper ... elWrappers){
+        this.addWrapper(elWrappers);
+    }
+
+    public AndELWrapper and(Object ... object){
+        ELWrapper[] wrapper = ELBus.convertToLogicOpt(object);
+        this.addWrapper(wrapper);
+        return this;
+    }
+
+    @Override
+    public AndELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public AndELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public AndELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public AndELWrapper data(String dataName, String jsonString) {
+        // 校验字符串符合Json格式
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public AndELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    protected AndELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        // 根据depth是否为null,决定输出是否格式化
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("AND(");
+        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(")");
+
+        // 设置共有属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 103 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/CatchELWrapper.java

@@ -0,0 +1,103 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 捕获异常表达式
+ * Catch(a).do(b)
+ * catch()和do()只允许单个参数
+ * a,b非与或非表达式
+ *
+ * 支持设置 id tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class CatchELWrapper extends ELWrapper{
+
+    public CatchELWrapper(ELWrapper elWrapper){
+        this.addWrapper(elWrapper);
+    }
+
+    public CatchELWrapper doOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToNonLogicOpt(object);
+        this.addWrapper(elWrapper);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public CatchELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        // 处理 CATCH() 语句的输出
+        processWrapperTabs(sb, depth);
+        sb.append("CATCH(");
+        processWrapperNewLine(sb, depth);
+        sb.append(this.getElWrapperList().get(0).toEL(sonDepth, paramContext));
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // 处理 DO()语句输出
+        if(this.getElWrapperList().size() > 1){
+            sb.append(".DO(");
+            processWrapperNewLine(sb, depth);
+            sb.append(this.getElWrapperList().get(1).toEL(sonDepth, paramContext));
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+            sb.append(")");
+        }
+
+        // 处理共有属性输出
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

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

@@ -0,0 +1,316 @@
+package com.yomahub.liteflow.builder.el;
+
+import java.util.Arrays;
+
+/**
+ * ELBus
+ * 构建EL表达式的入口类
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class ELBus {
+
+    public static final String TAB = "\t";
+
+    /**
+     * 创建 then 串行组件
+     * 参数为ELWrapper
+     * @param elWrappers
+     * @return
+     */
+    public static ThenELWrapper then(ELWrapper ... elWrappers){
+        checkNotBooleanArgs(elWrappers);
+        return new ThenELWrapper(elWrappers);
+    }
+
+    public static ThenELWrapper then(Object ... objects){
+        ELWrapper[] elWrappers = convertToNonLogicOpt(objects);
+        return new ThenELWrapper(elWrappers);
+    }
+
+    /**
+     * 创建 when 并行组件
+     * @param elWrappers
+     * @return
+     */
+    public static WhenELWrapper when(ELWrapper ... elWrappers){
+        checkNotBooleanArgs(elWrappers);
+        return new WhenELWrapper(elWrappers);
+    }
+
+    public static WhenELWrapper when(Object ... objects){
+        ELWrapper[] elWrappers = convertToNonLogicOpt(objects);
+        return new WhenELWrapper(elWrappers);
+    }
+
+    /**
+     * 创建 if 条件判断表达式
+     * @param ifELWrapper
+     * @param trueELWrapper
+     * @param falseELWrapper
+     * @return
+     */
+    public static IfELWrapper ifOpt(NodeELWrapper ifELWrapper, Object trueELWrapper, Object falseELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper), convertToNonLogicOpt(falseELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(String ifELWrapper, Object trueELWrapper, Object falseELWrapper){
+        return new IfELWrapper((NodeELWrapper) convertToLogicOpt(ifELWrapper), convertToNonLogicOpt(trueELWrapper), convertToNonLogicOpt(falseELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(AndELWrapper ifELWrapper, Object trueELWrapper, Object falseELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper), convertToNonLogicOpt(falseELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(OrELWrapper ifELWrapper, Object trueELWrapper, Object falseELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper), convertToNonLogicOpt(falseELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(NotELWrapper ifELWrapper, Object trueELWrapper, Object falseELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper), convertToNonLogicOpt(falseELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(NodeELWrapper ifELWrapper, Object trueELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(String ifELWrapper, Object trueELWrapper){
+        return new IfELWrapper((NodeELWrapper) convertToLogicOpt(ifELWrapper), convertToNonLogicOpt(trueELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(AndELWrapper ifELWrapper, Object trueELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(OrELWrapper ifELWrapper, Object trueELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper));
+    }
+
+    public static IfELWrapper ifOpt(NotELWrapper ifELWrapper, Object trueELWrapper){
+        return new IfELWrapper(ifELWrapper, convertToNonLogicOpt(trueELWrapper));
+    }
+
+    /**
+     * 创建 node 单节点表达式
+     * @param nodeId
+     * @return
+     */
+    public static NodeELWrapper node(String nodeId){
+        return new NodeELWrapper(nodeId);
+    }
+
+    /**
+     * 创建 switch 选择表达式
+     * @param nodeELWrapper
+     * @return
+     */
+    public static SwitchELWrapper switchOpt(NodeELWrapper nodeELWrapper){
+        return new SwitchELWrapper(nodeELWrapper);
+    }
+
+    public static SwitchELWrapper switchOpt(String nodeELWrapper){
+        return new SwitchELWrapper(convert(nodeELWrapper));
+    }
+
+    /**
+     * 创建 for 次数循环表达式
+     * @param loopNumber
+     * @return
+     */
+    public static ForELWrapper forOpt(Integer loopNumber){
+        return new ForELWrapper(loopNumber, "FOR");
+    }
+
+    public static ForELWrapper forOpt(NodeELWrapper nodeELWrapper){
+        return new ForELWrapper(nodeELWrapper, "FOR");
+    }
+
+    public static ForELWrapper forOpt(String nodeELWrapper){
+        return new ForELWrapper(convert(nodeELWrapper), "FOR");
+    }
+
+    /**
+     * 创建 while 条件循环表达式
+     * @param nodeELWrapper
+     * @return
+     */
+    public static WhileELWrapper whileOpt(NodeELWrapper nodeELWrapper){
+        return new WhileELWrapper(nodeELWrapper, "WHILE");
+    }
+
+    public static WhileELWrapper whileOpt(String nodeELWrapper){
+        return new WhileELWrapper(convert(nodeELWrapper), "WHILE");
+    }
+
+    public static WhileELWrapper whileOpt(AndELWrapper nodeELWrapper){
+        return new WhileELWrapper(nodeELWrapper, "WHILE");
+    }
+
+    public static WhileELWrapper whileOpt(OrELWrapper nodeELWrapper){
+        return new WhileELWrapper(nodeELWrapper, "WHILE");
+    }
+
+    public static WhileELWrapper whileOpt(NotELWrapper nodeELWrapper){
+        return new WhileELWrapper(nodeELWrapper, "WHILE");
+    }
+
+    /**
+     * 创建迭代循环表达式
+     * @param nodeELWrapper
+     * @return
+     */
+    public static IteratorELWrapper iteratorOpt(NodeELWrapper nodeELWrapper){
+        return new IteratorELWrapper(nodeELWrapper, "ITERATOR");
+    }
+
+    public static IteratorELWrapper iteratorOpt(String nodeELWrapper){
+        return new IteratorELWrapper(convert(nodeELWrapper), "ITERATOR");
+    }
+
+    /**
+     * 创建捕获异常表达式
+     * @param object
+     * @return
+     */
+    public static CatchELWrapper catchException(Object object){
+        return new CatchELWrapper(convertToNonLogicOpt(object));
+    }
+
+    /**
+     * 创建与表达式
+     * @param objects
+     * @return
+     */
+    public static AndELWrapper and(Object ... objects){
+        ELWrapper[] elWrappers = convertToLogicOpt(objects);
+        return new AndELWrapper(elWrappers);
+    }
+
+    /**
+     * 创建或表达式
+     * @param objects
+     * @return
+     */
+    public static OrELWrapper or(Object ... objects){
+        ELWrapper[] elWrappers = convertToLogicOpt(objects);
+        return new OrELWrapper(elWrappers);
+    }
+
+    /**
+     * 创建非表达式
+     * @param notElWrapper
+     * @return
+     */
+    public static NotELWrapper not(NodeELWrapper notElWrapper){
+        return new NotELWrapper(notElWrapper);
+    }
+
+    public static NotELWrapper not(String notElWrapper){
+        return new NotELWrapper(convert(notElWrapper));
+    }
+
+    public static NotELWrapper not(AndELWrapper notElWrapper){
+        return new NotELWrapper(notElWrapper);
+    }
+
+    public static NotELWrapper not(OrELWrapper notElWrapper){
+        return new NotELWrapper(notElWrapper);
+    }
+
+    public static NotELWrapper not(NotELWrapper notElWrapper){
+        return new NotELWrapper(notElWrapper);
+    }
+
+    /**
+     * 参数转换并校验参数是否为ELWrapper类型或者String类型
+     * @param objects
+     * @return
+     */
+    public static ELWrapper[] convert(Object... objects){
+        return Arrays.stream(objects).map(o -> {
+            if (o instanceof String) {
+                return new NodeELWrapper(o.toString());
+            } else if (o instanceof ELWrapper) {
+                return (ELWrapper) o;
+            } else {
+                throw new RuntimeException("param is error");
+            }
+        }).toArray(ELWrapper[]::new);
+    }
+
+    public static ELWrapper convert(Object object){
+        if (object instanceof String) {
+            return new NodeELWrapper(object.toString());
+        } else if (object instanceof ELWrapper) {
+            return (ELWrapper) object;
+        } else {
+            throw new RuntimeException("param is error");
+        }
+    }
+
+    /**
+     * 参数校验 只包含与或非的组件
+     * @param objects
+     * @return
+     */
+    public static ELWrapper[] convertToLogicOpt(Object... objects){
+        ELWrapper[] elWrappers = convert(objects);
+        checkBooleanArgs(elWrappers);
+        return elWrappers;
+    }
+
+    public static ELWrapper convertToLogicOpt(Object object){
+        ELWrapper elWrapper = convert(object);
+        checkBooleanArgs(elWrapper);
+        return elWrapper;
+    }
+
+    /**
+     * 参数校验 不包含与或非表达式的组件
+     * @param objects
+     * @return
+     */
+    public static ELWrapper[] convertToNonLogicOpt(Object ... objects){
+        ELWrapper[] elWrappers = convert(objects);
+        checkNotBooleanArgs(elWrappers);
+        return elWrappers;
+    }
+
+    public static ELWrapper convertToNonLogicOpt(Object object){
+        ELWrapper elWrapper = convert(object);
+        checkNotBooleanArgs(elWrapper);
+        return elWrapper;
+    }
+
+    /**
+     * 检查参数都不返回boolean值
+     * @param elWrappers
+     */
+    public static void checkNotBooleanArgs(ELWrapper ... elWrappers) {
+        for(ELWrapper elWrapper : elWrappers){
+            if(elWrapper instanceof AndELWrapper){
+                throw new RuntimeException("param is error");
+            } else if(elWrapper instanceof OrELWrapper){
+                throw new RuntimeException("param is error");
+            } else if(elWrapper instanceof NotELWrapper){
+                throw new RuntimeException("param is error");
+            }
+        }
+    }
+
+    /**
+     * 检查参数是否都能返回boolean值
+     * @param elWrappers
+     */
+    public static void checkBooleanArgs(ELWrapper ... elWrappers) {
+        for(ELWrapper elWrapper : elWrappers){
+            if(!(elWrapper instanceof AndELWrapper)
+            && !(elWrapper instanceof OrELWrapper)
+            && !(elWrapper instanceof NotELWrapper)
+            && !(elWrapper instanceof NodeELWrapper)){
+                throw new RuntimeException("param is error");
+            }
+        }
+    }
+}

+ 206 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java

@@ -0,0 +1,206 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ELWrapper是所有组件的抽象父类
+ * 定义所有EL表达式的公有变量 tag、id、data、maxWaitSeconds 以及 子表达式列表 ELWrapperList
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public abstract class ELWrapper {
+
+    private final List<ELWrapper> elWrapperList = new ArrayList<>();
+
+    private String tag;
+    private String id;
+    private String dataName;
+    private String data;
+    private Integer maxWaitSeconds;
+
+    protected void addWrapper(ELWrapper wrapper){
+        this.elWrapperList.add(wrapper);
+    }
+
+    protected void addWrapper(ELWrapper ... wrappers){
+        this.elWrapperList.addAll(Arrays.asList(wrappers));
+    }
+
+    protected void addWrapper(ELWrapper wrapper, int index){
+        this.elWrapperList.add(index, wrapper);
+    }
+
+    protected void setWrapper(ELWrapper wrapper, int index){
+        this.elWrapperList.set(index, wrapper);
+    }
+
+    protected ELWrapper getFirstWrapper(){
+        return this.elWrapperList.get(0);
+    }
+
+    protected List<ELWrapper> getElWrapperList(){
+        return this.elWrapperList;
+    }
+
+    protected void setTag(String tag){
+        this.tag = tag;
+    }
+
+    protected String getTag(){
+        return this.tag;
+    }
+
+    protected void setId(String id){
+        this.id = id;
+    }
+
+    protected String getId(){
+        return this.id;
+    }
+
+    protected void setData(String data){
+        this.data = data;
+    }
+
+    protected String getData(){
+        return this.data;
+    }
+
+    protected void setDataName(String dataName){
+        this.dataName = dataName;
+    }
+
+    protected String getDataName(){
+        return this.dataName;
+    }
+
+    protected void setMaxWaitSeconds(Integer maxWaitSeconds){
+        this.maxWaitSeconds = maxWaitSeconds;
+    }
+
+    protected Integer getMaxWaitSeconds(){
+        return this.maxWaitSeconds;
+    }
+
+    protected abstract ELWrapper tag(String tag);
+
+    protected abstract ELWrapper id(String id);
+
+    /**
+     * 设置data属性,参数为
+     * @param dataName
+     * @param object JavaBean
+     * @return
+     */
+    protected abstract ELWrapper data(String dataName, Object object);
+
+    /**
+     * 设置data属性,参数为
+     * @param dataName
+     * @param jsonString json格式字符串
+     * @return
+     */
+    protected abstract ELWrapper data(String dataName, String jsonString);
+
+    /**
+     * 设置data属性,参数为
+     * @param dataName
+     * @param jsonMap Map映射
+     * @return
+     */
+    protected abstract ELWrapper data(String dataName, Map<String, Object> jsonMap);
+
+    protected ELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    /**
+     * 输出EL表达式的默认方法
+     * 非格式化输出EL表达式
+     *
+     * @return {@link String}
+     */
+    public String toEL(){
+        StringBuilder paramContext = new StringBuilder();
+        String ELContext = toEL(null, paramContext);
+        return paramContext.append(ELContext).toString();
+    }
+
+    /**
+     * 是否格式化输出树形结构的规则表达式
+     *
+     * @param format 格式
+     * @return {@link String}
+     */
+    public String toEL(boolean format){
+        StringBuilder paramContext = new StringBuilder();
+        String ELContext;
+        if(!format){
+            ELContext = toEL(null, paramContext);
+        } else {
+            ELContext = toEL(0, paramContext);
+        }
+        return paramContext.append(ELContext).toString();
+    }
+
+    /**
+     * 格式化输出EL表达式
+     * @param depth 深度
+     * @param paramContext 参数输出内容,用于输出data属性
+     * @return
+     */
+    protected abstract String toEL(Integer depth, StringBuilder paramContext);
+
+    /**
+     * 处理EL表达式的属性
+     *
+     * @param elContext    EL 输出内容
+     * @param paramContext 参数 输出内容
+     */
+    protected void processWrapperProperty(StringBuilder elContext, StringBuilder paramContext){
+        if(this.getId() != null){
+            elContext.append(StrUtil.format(".id(\"{}\")", this.getId()));
+        }
+        if(this.getTag() != null){
+            elContext.append(StrUtil.format(".tag(\"{}\")", this.getTag()));
+        }
+        if(this.getData() != null){
+            elContext.append(StrUtil.format(".data({})", this.getDataName()));
+            paramContext.append(StrUtil.format("{} = '{}';\n", this.getDataName(), this.getData()));
+        }
+        if(this.getMaxWaitSeconds() != null){
+            elContext.append(StrUtil.format(".maxWaitSeconds({})", String.valueOf(this.getMaxWaitSeconds())));
+        }
+    }
+
+    /**
+     * 处理格式化输出 EL表达式换行符
+     *
+     * @param elContext EL 上下文
+     * @param depth     深度
+     */
+    protected void processWrapperNewLine(StringBuilder elContext, Integer depth){
+        if(depth != null){
+            elContext.append("\n");
+        }
+    }
+
+    /**
+     * 处理格式化输出 EL表达式制表符
+     *
+     * @param elContext EL 上下文
+     * @param depth     深度
+     */
+    protected void processWrapperTabs(StringBuilder elContext, Integer depth){
+        if(depth != null) {
+            elContext.append(StrUtil.repeat(ELBus.TAB, depth));
+        }
+    }
+}

+ 115 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/FinallyELWrapper.java

@@ -0,0 +1,115 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 后置表达式
+ * 只能在THEN组件中调用
+ * 参数数量不限,类型不为与或非表达式
+ * 支持设置 tag、id、data 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class FinallyELWrapper extends ELWrapper{
+    public FinallyELWrapper(Object ... objects){
+        super.addWrapper(ELBus.convertToNonLogicOpt(objects));
+    }
+
+    @Override
+    public FinallyELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public FinallyELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public FinallyELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public FinallyELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public FinallyELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    /**
+     * 后置组件无法设置maxWaitSeconds属性,重载用protected修饰
+     * @param maxWaitSeconds
+     * @return
+     */
+    @Override
+    protected FinallyELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("FINALLY(");
+        processWrapperNewLine(sb, depth);
+        // 处理子表达式输出
+        for (int i = 0; i < this.getElWrapperList().size(); i++) {
+            sb.append(this.getElWrapperList().get(i).toEL(sonDepth, paramContext));
+            if (i != this.getElWrapperList().size() - 1) {
+                sb.append(",");
+                processWrapperNewLine(sb, depth);
+            }
+        }
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // 处理共用属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+
+    /**
+     * FINALLY不允许maxWaitSeconds属性,重载父类
+     *
+     * @param elContext    EL 上下文
+     * @param paramContext 参数上下文
+     */
+    @Override
+    protected void processWrapperProperty(StringBuilder elContext, StringBuilder paramContext){
+        if(this.getId() != null){
+            elContext.append(StrUtil.format(".id(\"{}\")", this.getId()));
+        }
+        if(this.getTag() != null){
+            elContext.append(StrUtil.format(".tag(\"{}\")", this.getTag()));
+        }
+        if(this.getData() != null){
+            elContext.append(StrUtil.format(".data({})", this.getDataName()));
+            paramContext.append(StrUtil.format("{} = '{}'\n", this.getDataName(), this.getData()));
+        }
+    }
+}

+ 89 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ForELWrapper.java

@@ -0,0 +1,89 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 次数循环表达式
+ * FOR只允许一个参数 参数为 Integer 或 返回循环次数的 EL表达式
+ * DO只允许一个参数 参数类型不为与或非表达式
+ * 支持调用break方法,参数为与或非表达式或返回true false的单节点
+ *
+ * 支持设置 id tag data maxWaitSeconds 以及 parallel 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class ForELWrapper extends LoopELWrapper{
+
+    public ForELWrapper(Integer loopNumber, String loopFunction){
+        super(loopNumber, loopFunction);
+    }
+
+    public ForELWrapper(ELWrapper elWrapper, String loopFunction){
+        super(elWrapper, loopFunction);
+    }
+
+    @Override
+    public ForELWrapper doOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToNonLogicOpt(object);
+        super.addWrapper(elWrapper, 1);
+        return this;
+    }
+
+    public ForELWrapper breakOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToLogicOpt(object);
+        super.addWrapper(elWrapper, 2);
+        return this;
+    }
+
+    public ForELWrapper parallel(boolean parallel){
+        setParallel(parallel);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ForELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+}

+ 333 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IfELWrapper.java

@@ -0,0 +1,333 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 条件表达式
+ * 按照调用方法IF(a,b,c) IF(a,b).else(c) IF(a,b).ELIF(c,d)对应输出
+ * 支持设置 id tag data maxWaitSeconds 属性
+ * 支持二元或三元函数初始化
+ * 支持调用 else elif 方法
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class IfELWrapper extends ELWrapper{
+
+    /**
+     * 定义当前条件组件的输出格式
+     */
+    private static final int IF_FORMAT = 1;
+    private static final int IF_ELSE_FORMAT = 2;
+    private static final int ELIF_FORMAT = 3;
+
+    private int format;
+
+    /**
+     * 单节点作为判断条件
+     *
+     * @param ifWrapper    如果包装器
+     * @param trueWrapper  真正包装器
+     * @param falseWrapper 假包装器
+     */
+    public IfELWrapper(NodeELWrapper ifWrapper, ELWrapper trueWrapper, ELWrapper falseWrapper) {
+        this.setIfWrapper(ifWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.setFalseWrapper(falseWrapper);
+        this.format = IF_FORMAT;
+    }
+
+    /**
+     * 如果缠绕器
+     *
+     * @param ifWrapper   如果包装器
+     * @param trueWrapper 真正包装器
+     */
+    public IfELWrapper(NodeELWrapper ifWrapper, ELWrapper trueWrapper) {
+        this.setIfWrapper(ifWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.format = IF_ELSE_FORMAT;
+    }
+
+    /**
+     * 与节点作为判断条件
+     *
+     * @param andELWrapper 和缠绕器
+     * @param trueWrapper
+     * @param falseWrapper
+     */
+    public IfELWrapper(AndELWrapper andELWrapper, ELWrapper trueWrapper, ELWrapper falseWrapper) {
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.setFalseWrapper(falseWrapper);
+        this.format = IF_FORMAT;
+    }
+
+    public IfELWrapper(AndELWrapper andELWrapper, ELWrapper trueWrapper){
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.format = IF_ELSE_FORMAT;
+    }
+
+    /**
+     * 如果缠绕器
+     * 或节点作为判断条件
+     *
+     * @param andELWrapper 和缠绕器
+     * @param trueWrapper  真正包装器
+     * @param falseWrapper 假包装器
+     */
+    public IfELWrapper(OrELWrapper andELWrapper, ELWrapper trueWrapper, ELWrapper falseWrapper) {
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.setFalseWrapper(falseWrapper);
+        this.format = IF_FORMAT;
+    }
+
+    public IfELWrapper(OrELWrapper andELWrapper, ELWrapper trueWrapper){
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.format = IF_ELSE_FORMAT;
+    }
+
+    /**
+     * 非节点作为判断条件
+     * @param andELWrapper
+     * @param trueWrapper
+     * @param falseWrapper
+     */
+    public IfELWrapper(NotELWrapper andELWrapper, ELWrapper trueWrapper, ELWrapper falseWrapper) {
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.setFalseWrapper(falseWrapper);
+        this.format = IF_FORMAT;
+    }
+
+    public IfELWrapper(NotELWrapper andELWrapper, ELWrapper trueWrapper){
+        this.setIfWrapper(andELWrapper);
+        this.setTrueWrapper(trueWrapper);
+        this.format = IF_ELSE_FORMAT;
+    }
+
+    /**
+     * else语句
+     * 设置最深层次条件表达式的 false分支的 表达式
+     *
+     * @param falseObject false分支
+     * @return {@link IfELWrapper}
+     */
+    public IfELWrapper elseOpt(Object falseObject){
+        ELWrapper falseWrapper = ELBus.convertToNonLogicOpt(falseObject);
+        // 找到最深层的if组件
+        ELWrapper prev = this;
+        ELWrapper succ = this;
+        // 设置最深层if组件false组件
+        while(prev instanceof IfELWrapper){
+            succ = prev;
+            prev = prev.getElWrapperList().size() >= 3 ? prev.getElWrapperList().get(2) : null;
+        }
+        ((IfELWrapper) succ).setFalseWrapper(falseWrapper);
+        return this;
+    }
+
+    /**
+     * elif语句
+     * 设置最深层次条件表达式的 判断表达式 和 true分支的 表达式
+     *
+     * @param ifObject   判断组件
+     * @param trueObject true分支
+     * @return {@link IfELWrapper}
+     */
+    public IfELWrapper elIfOpt(Object ifObject, Object trueObject) {
+        // 包装判断表达式和true分支组件
+        ELWrapper ifWrapper = ELBus.convertToLogicOpt(ifObject);
+        ELWrapper trueWrapper = ELBus.convertToNonLogicOpt(trueObject);
+        IfELWrapper elIfWrapper;
+        if(ifWrapper instanceof NodeELWrapper){
+            elIfWrapper = new IfELWrapper((NodeELWrapper) ifWrapper, trueWrapper);
+        } else if (ifWrapper instanceof AndELWrapper){
+            elIfWrapper = new IfELWrapper((AndELWrapper) ifWrapper, trueWrapper);
+        } else if (ifWrapper instanceof OrELWrapper){
+            elIfWrapper = new IfELWrapper((OrELWrapper) ifWrapper, trueWrapper);
+        } else if (ifWrapper instanceof NotELWrapper){
+            elIfWrapper = new IfELWrapper((NotELWrapper) ifWrapper, trueWrapper);
+        } else {
+            throw new RuntimeException("param error!");
+        }
+        elIfWrapper.setFormat(ELIF_FORMAT);
+        // 找到最深层的if组件
+        ELWrapper prev = this;
+        ELWrapper succ = this;
+        // 设置最深层的false分支为新建的条件表达式
+        while(prev instanceof IfELWrapper){
+            succ = prev;
+            prev = prev.getElWrapperList().size() >= 3 ? prev.getElWrapperList().get(2) : null;
+        }
+        ((IfELWrapper) succ).elseOpt(elIfWrapper);
+        return this;
+    }
+
+    private void setIfWrapper(ELWrapper ifWrapper){
+        this.addWrapper(ifWrapper, 0);
+    }
+
+    private ELWrapper getIfWrapper(){
+        return this.getElWrapperList().get(0);
+    }
+
+    private void setTrueWrapper(ELWrapper trueWrapper){
+        this.addWrapper(trueWrapper, 1);
+    }
+
+    private ELWrapper getTrueWrapper(){
+        return this.getElWrapperList().get(1);
+    }
+
+    private void setFalseWrapper(ELWrapper falseWrapper){
+        this.addWrapper(falseWrapper, 2);
+    }
+
+    protected void setFormat(int formatCode){
+        this.format = formatCode;
+    }
+
+    protected int getFormat(){
+        return this.format;
+    }
+
+    private ELWrapper getFalseWrapper(){
+        try{
+            return this.getElWrapperList().get(2);
+        }catch (Exception e){
+            return null;
+        }
+    }
+
+    @Override
+    public IfELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public IfELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public IfELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IfELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IfELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IfELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        // 根据format不同分别按不同的格式输出
+        processWrapperTabs(sb, depth);
+        switch (this.format){
+            // IF(a,b,c) 三元表达式输出格式
+            case IF_FORMAT:
+                sb.append("IF(");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getIfWrapper().toEL(sonDepth, paramContext)).append(",");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getTrueWrapper().toEL(sonDepth, paramContext)).append(",");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getFalseWrapper().toEL(sonDepth, paramContext));
+                processWrapperNewLine(sb, depth);
+                processWrapperTabs(sb, depth);
+                sb.append(")");
+                break;
+            // IF(a,b).ELSE(c) 二元表达式输出格式
+            case IF_ELSE_FORMAT:
+                sb.append("IF(");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getIfWrapper().toEL(sonDepth, paramContext)).append(",");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getTrueWrapper().toEL(sonDepth, paramContext));
+                processWrapperNewLine(sb, depth);
+                processWrapperTabs(sb, depth);
+                sb.append(")");
+                processElseOutPut(depth, sonDepth, sb, paramContext);
+                break;
+            // IF(a,b).ELIF(c) ELIF输出格式
+            case ELIF_FORMAT:
+                // elif 树形结构输出
+                sb.append(".ELIF(");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getIfWrapper().toEL(sonDepth, paramContext)).append(",");
+                processWrapperNewLine(sb, depth);
+                sb.append(this.getTrueWrapper().toEL(sonDepth, paramContext));
+                processWrapperNewLine(sb, depth);
+                processWrapperTabs(sb, depth);
+                sb.append(")");
+                processElseOutPut(depth, sonDepth, sb, paramContext);
+                break;
+            default:
+                break;
+        }
+
+        // 设置共有属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+
+    /**
+     * if(a,b) 和 elif(a,b) 的 else 处理方法相同
+     * 抽象出处理ELSE输出方法
+     *
+     * @param depth        深度
+     * @param sonDepth     儿子深度
+     * @param elContext    EL 上下文
+     * @param paramContext 参数上下文
+     */
+    private void processElseOutPut(Integer depth, Integer sonDepth, StringBuilder elContext, StringBuilder paramContext){
+        if (ObjectUtil.isNotNull(this.getFalseWrapper())){
+            // 如果 使用ELIF 定义子表达式,则放到子表达式中处理
+            if(this.getFalseWrapper() instanceof IfELWrapper && ((IfELWrapper) this.getFalseWrapper()).getFormat() == ELIF_FORMAT){
+                elContext.append(this.getFalseWrapper().toEL(depth, paramContext));
+            } else {
+                // 如果使用 ELSE 处理 false分支表达式,则本层处理表达式输出
+                elContext.append(".ELSE(");
+                processWrapperNewLine(elContext, depth);
+                elContext.append(this.getFalseWrapper().toEL(sonDepth, paramContext));
+                processWrapperNewLine(elContext, depth);
+                processWrapperTabs(elContext, depth);
+                elContext.append(")");
+            }
+        }
+    }
+}

+ 77 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/IteratorELWrapper.java

@@ -0,0 +1,77 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 迭代循环表达式
+ * ITERATOR只允许一个参数 参数为 EL表达式
+ * DO只允许一个参数 参数类型不为与或非表达式
+ *
+ * 支持设置 id tag data maxWaitSeconds 以及 parallel 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class IteratorELWrapper extends LoopELWrapper{
+    public IteratorELWrapper(ELWrapper elWrapper, String loopFunction){
+        super(elWrapper, loopFunction);
+    }
+
+    public IteratorELWrapper parallel(boolean parallel){
+        setParallel(parallel);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper doOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToNonLogicOpt(object);
+        super.addWrapper(elWrapper, 1);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public IteratorELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+}

+ 137 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/LoopELWrapper.java

@@ -0,0 +1,137 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * FOR、WHILE、ITERATOR循环表达式的公共抽象父类
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public abstract class LoopELWrapper extends ELWrapper{
+
+    protected Integer loopNumber;
+
+    /**
+     * 以loopFunction变量进行区分FOR、WHILE、ITERATOR
+     */
+    protected String loopFunction;
+
+    protected boolean parallel;
+
+    public LoopELWrapper(Integer loopNumber, String loopFunction){
+        this.loopNumber = loopNumber;
+        this.loopFunction = loopFunction;
+        this.addWrapper(null, 0);
+    }
+
+    public LoopELWrapper(ELWrapper elWrapper, String loopFunction){
+        this.loopFunction = loopFunction;
+        this.addWrapper(elWrapper, 0);
+    }
+
+    public LoopELWrapper doOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToNonLogicOpt(object);
+        this.addWrapper(elWrapper, 1);
+        return this;
+    }
+
+    protected void setParallel(boolean parallel){
+        this.parallel = parallel;
+    }
+
+    @Override
+    public LoopELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public LoopELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public LoopELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public LoopELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public LoopELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public LoopELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        // 设置循坏组件的类型以及循环节点
+        processWrapperTabs(sb, depth);
+        sb.append(loopFunction).append("(");
+        if(loopNumber != null){
+            sb.append(loopNumber.toString());
+        } else {
+            processWrapperNewLine(sb, depth);
+            sb.append(this.getElWrapperList().get(0).toEL(sonDepth, paramContext));
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+        }
+        sb.append(")");
+
+        // 循环独有的并行语义
+        if(this.parallel){
+            sb.append(".parallel(true)");
+        }
+
+        // 设置循环组件输出
+        if(this.getElWrapperList().size() > 1) {
+            sb.append(".DO(");
+            processWrapperNewLine(sb, depth);
+            sb.append(this.getElWrapperList().get(1).toEL(sonDepth, paramContext));
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+            sb.append(")");
+        }
+
+        // 设置退出循环组件输出
+        if(this.getElWrapperList().size() > 2){
+            sb.append(".BREAK(");
+            processWrapperNewLine(sb, depth);
+            sb.append(this.getElWrapperList().get(2).toEL(sonDepth, paramContext));
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+            sb.append(")");
+        }
+
+        // 设置共有属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 122 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NodeELWrapper.java

@@ -0,0 +1,122 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 单节点表达式
+ * 单节点也应以为一种表达式
+ * 支持设置 tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class NodeELWrapper extends ELWrapper {
+
+    private String nodeId;
+
+    private String tag;
+
+    public NodeELWrapper(String nodeId) {
+        this.nodeId = nodeId;
+        this.setNodeWrapper(this);
+    }
+
+    private void setNodeWrapper(ELWrapper elWrapper){
+        this.addWrapper(elWrapper, 0);
+    }
+
+    private NodeELWrapper getNodeWrapper(){
+        return (NodeELWrapper) this.getFirstWrapper();
+    }
+
+    protected String getNodeId() {
+        return nodeId;
+    }
+
+    protected void setNodeId(String nodeId) {
+        this.nodeId = nodeId;
+    }
+
+    @Override
+    public NodeELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    /**
+     * 单节点不允许定义 id,重载为protected修饰
+     * @param id
+     * @return
+     */
+    @Override
+    protected NodeELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public NodeELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NodeELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NodeELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NodeELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        NodeELWrapper nodeELWrapper = this.getNodeWrapper();
+        StringBuilder sb = new StringBuilder();
+        processWrapperTabs(sb, depth);
+        sb.append(StrUtil.format("node(\"{}\")", nodeELWrapper.getNodeId()));
+
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+
+    /**
+     * Node的公共属性不包括id,对父类方法重载。
+     *
+     * @param elContext    EL 上下文
+     * @param paramContext 参数上下文
+     */
+    @Override
+    protected void processWrapperProperty(StringBuilder elContext, StringBuilder paramContext){
+        if(this.getTag() != null){
+            elContext.append(StrUtil.format(".tag(\"{}\")", this.getTag()));
+        }
+        if(this.getData() != null){
+            elContext.append(StrUtil.format(".data({})", this.getDataName()));
+            paramContext.append(StrUtil.format("{} = '{}'\n", this.getDataName(), this.getData()));
+        }
+        if(this.getMaxWaitSeconds() != null){
+            elContext.append(StrUtil.format(".maxWaitSeconds({})", String.valueOf(this.getMaxWaitSeconds())));
+        }
+    }
+}

+ 84 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/NotELWrapper.java

@@ -0,0 +1,84 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 与或非表达式中的 非表达式
+ * 参数只允许一个 参数必须返回true或false
+ *
+ * 支持设置 id tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class NotELWrapper extends ELWrapper{
+    public NotELWrapper(ELWrapper elWrapper){
+        this.addWrapper(elWrapper);
+    }
+
+    @Override
+    public NotELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public NotELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public NotELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NotELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NotELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public NotELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("NOT(");
+        processWrapperNewLine(sb, depth);
+        // 处理子表达式输出
+        sb.append(this.getElWrapperList().get(0).toEL(sonDepth, paramContext));
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // 设置公共属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 98 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/OrELWrapper.java

@@ -0,0 +1,98 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 与或非表达式中的 或表达式
+ * 参数允许任意数量 参数必须返回true或false
+ *
+ * 使用or()方法加入新的表达式
+ * 支持设置 id tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class OrELWrapper extends ELWrapper{
+
+    public OrELWrapper(ELWrapper ... elWrappers){
+        this.addWrapper(elWrappers);
+    }
+
+    public OrELWrapper or(Object ... object){
+        ELWrapper[] elWrapper = ELBus.convertToLogicOpt(object);
+        this.addWrapper(elWrapper);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public OrELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("OR(");
+        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(")");
+
+        // 处理公共属性输出
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 90 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/PreELWrapper.java

@@ -0,0 +1,90 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 前置表达式
+ * 只能在THEN组件中调用
+ * 参数数量不限,类型不为与或非表达式
+ * 支持设置 tag、id、data、maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class PreELWrapper extends ELWrapper{
+    public PreELWrapper(Object ... objects){
+        super.addWrapper(ELBus.convert(objects));
+    }
+
+    @Override
+    public PreELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public PreELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public PreELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public PreELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public PreELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public PreELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("PRE(");
+        processWrapperNewLine(sb, depth);
+        // 处理子表达式输出
+        for (int i = 0; i < this.getElWrapperList().size(); i++) {
+            sb.append(this.getElWrapperList().get(i).toEL(sonDepth, paramContext));
+            if (i != this.getElWrapperList().size() - 1) {
+                sb.append(",");
+                processWrapperNewLine(sb, depth);
+            }
+        }
+        processWrapperNewLine(sb, depth);
+        processWrapperTabs(sb, depth);
+        sb.append(")");
+
+        // 处理公共属性输出
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 121 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/SwitchELWrapper.java

@@ -0,0 +1,121 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 选择组件
+ * SWITCH(a).TO(b,c,d...).default(x)
+ * SWITCH只允许单个参数,类型为非与或非表达式
+ * TO允许任意个参数,类型为非与或非表达式
+ * DEFAULT只允许单个参数,类型为非与或非表达式
+ *
+ * 支持定义 id tag data maxWaitSeconds 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class SwitchELWrapper extends ELWrapper{
+    /**
+     * default语句的表达式
+     */
+    private ELWrapper defaultELWrapper;
+
+    public SwitchELWrapper(ELWrapper elWrapper){
+        this.addWrapper(elWrapper, 0);
+    }
+
+    public SwitchELWrapper to(Object... objects){
+        ELWrapper[] elWrappers = ELBus.convertToNonLogicOpt(objects);
+        this.addWrapper(elWrappers);
+        return this;
+    }
+
+    public SwitchELWrapper defaultOpt(Object object){
+        defaultELWrapper = ELBus.convertToNonLogicOpt(object);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public SwitchELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append(StrUtil.format("SWITCH({})", this.getFirstWrapper().toEL(null, paramContext)));
+        // 处理子表达式输出
+        if(this.getElWrapperList().size() > 1) {
+            sb.append(".TO(");
+            processWrapperNewLine(sb, depth);
+            for (int i = 1; i < this.getElWrapperList().size(); i++) {
+                sb.append(this.getElWrapperList().get(i).toEL(sonDepth, paramContext));
+                if (i != this.getElWrapperList().size() - 1) {
+                    sb.append(",");
+                    processWrapperNewLine(sb, depth);
+                }
+            }
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+            sb.append(")");
+        }
+        // default可以不存在
+        if(defaultELWrapper != null){
+            sb.append(".DEFAULT(");
+            processWrapperNewLine(sb, depth);
+            sb.append(defaultELWrapper.toEL(sonDepth, paramContext));
+            processWrapperNewLine(sb, depth);
+            processWrapperTabs(sb, depth);
+            sb.append(")");
+        }
+
+        // 处理表达式的共有属性
+        processWrapperProperty(sb, paramContext);
+        return sb.toString();
+    }
+}

+ 149 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ThenELWrapper.java

@@ -0,0 +1,149 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 串行组件
+ * 允许调用PRE、FINALLY任意次
+ * 参数数量任意,参数类型为非与或非表达式
+ *
+ * 支持定义 id tag data maxWaitSeconds属性
+ *
+ * @author gezuao
+ * @date 2023/10/10
+ * @since 2.11.1
+ */
+public class ThenELWrapper extends ELWrapper {
+    private final List<PreELWrapper> preELWrapperList;
+    private final List<FinallyELWrapper> finallyELWrapperList;
+
+    public ThenELWrapper(ELWrapper ... elWrappers) {
+        preELWrapperList = new ArrayList<>();
+        finallyELWrapperList = new ArrayList<>();
+        this.addWrapper(elWrappers);
+    }
+
+    public ThenELWrapper then(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
+     */
+    public ThenELWrapper pre(Object ... objects){
+        addPreELWrapper(new PreELWrapper(objects));
+        return this;
+    }
+
+    /**
+     * 定义子后置组件
+     * @param objects
+     * @return
+     */
+    public ThenELWrapper finallyOpt(Object ... objects){
+        addFinallyELWrapper(new FinallyELWrapper(objects));
+        return this;
+    }
+
+    @Override
+    public ThenELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public ThenELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    // data关键字的约束:允许以Bean、jsonString、map类型输入数据,必须包含dataName参数。
+    @Override
+    public ThenELWrapper data(String dataName, Object javaBean) {
+        setData(JSONUtil.toJsonStr(javaBean));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ThenELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ThenELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public ThenELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("THEN(");
+        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();
+    }
+
+
+}

+ 154 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhenELWrapper.java

@@ -0,0 +1,154 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+
+import java.util.*;
+
+/**
+ * 并行组件
+ * 参数数量任意
+ * 参数类型非与或非表达式
+ *
+ * 支持定义 id tag data maxWaitSeconds 属性
+ * 支持调用 any ignoreError customThreadExecutor mustExecuteList 并行组件特有方法
+ * any 和 must语义冲突,不支持同时定义
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class WhenELWrapper extends ELWrapper {
+
+    private boolean any;
+    private boolean ignoreError;
+    private String customThreadExecutor;
+    private final List<String> mustExecuteList;
+
+    public WhenELWrapper(ELWrapper ... elWrappers) {
+        this.addWrapper(elWrappers);
+        this.mustExecuteList = new ArrayList<>();
+    }
+
+    public WhenELWrapper when(Object ... objects){
+        ELWrapper[] elWrappers = ELBus.convertToNonLogicOpt(objects);
+        // 校验与或非表达式
+        this.addWrapper(elWrappers);
+        return this;
+    }
+
+    public WhenELWrapper any(boolean any) {
+        this.any = any;
+        return this;
+    }
+
+    public WhenELWrapper ignoreError(boolean ignoreError) {
+        this.ignoreError = ignoreError;
+        return this;
+    }
+
+    public WhenELWrapper customThreadExecutor(String customThreadExecutor){
+        this.customThreadExecutor = customThreadExecutor;
+        return this;
+    }
+
+    public WhenELWrapper must(String ... mustExecuteWrappers){
+        this.mustExecuteList.addAll(Arrays.asList(mustExecuteWrappers));
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhenELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+
+    @Override
+    protected String toEL(Integer depth, StringBuilder paramContext) {
+        Integer sonDepth = depth == null ? null : depth + 1;
+        StringBuilder sb = new StringBuilder();
+
+        processWrapperTabs(sb, depth);
+        sb.append("WHEN(");
+        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();
+    }
+}

+ 84 - 0
liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/WhileELWrapper.java

@@ -0,0 +1,84 @@
+package com.yomahub.liteflow.builder.el;
+
+import cn.hutool.json.JSONUtil;
+
+import java.util.Map;
+
+/**
+ * 条件循环表达式
+ * WHILE只允许一个参数 参数为 返回布尔值的EL表达式
+ * DO只允许一个参数 参数类型不为与或非表达式
+ * 支持调用break方法,参数为与或非表达式或返回true false的单节点
+ *
+ * 支持设置 id tag data maxWaitSeconds 以及 parallel 属性
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+public class WhileELWrapper extends LoopELWrapper{
+    public WhileELWrapper(ELWrapper elWrapper, String loopFunction){
+        super(elWrapper, loopFunction);
+    }
+
+    public WhileELWrapper parallel(boolean parallel){
+        setParallel(parallel);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper doOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToNonLogicOpt(object);
+        super.addWrapper(elWrapper, 1);
+        return this;
+    }
+
+    public WhileELWrapper breakOpt(Object object){
+        ELWrapper elWrapper = ELBus.convertToLogicOpt(object);
+        super.addWrapper(elWrapper, 2);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper tag(String tag) {
+        this.setTag(tag);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper id(String id) {
+        this.setId(id);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper data(String dataName, Object object) {
+        setData(JSONUtil.toJsonStr(object));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper data(String dataName, String jsonString) {
+        try {
+            JSONUtil.parseObj(jsonString);
+        } catch (Exception e){
+            throw new RuntimeException("字符串不符合Json格式!");
+        }
+        setData(jsonString);
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper data(String dataName, Map<String, Object> jsonMap) {
+        setData(JSONUtil.toJsonStr(jsonMap));
+        setDataName(dataName);
+        return this;
+    }
+
+    @Override
+    public WhileELWrapper maxWaitSeconds(Integer maxWaitSeconds){
+        setMaxWaitSeconds(maxWaitSeconds);
+        return this;
+    }
+}

+ 56 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/pom.xml

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>liteflow-testcase-el</artifactId>
+        <groupId>com.yomahub</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>liteflow-testcase-el-builder</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.yomahub</groupId>
+            <artifactId>liteflow-spring-boot-starter</artifactId>
+            <version>${revision}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.yomahub</groupId>
+            <artifactId>liteflow-el-builder</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.101tec</groupId>
+            <artifactId>zkclient</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-recipes</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/BaseTest.java

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test;
+
+import com.yomahub.liteflow.core.FlowInitHook;
+import com.yomahub.liteflow.flow.FlowBus;
+import com.yomahub.liteflow.property.LiteflowConfigGetter;
+import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner;
+import com.yomahub.liteflow.spring.ComponentScanner;
+import com.yomahub.liteflow.thread.ExecutorHelper;
+import org.junit.jupiter.api.AfterAll;
+
+public class BaseTest {
+
+	@AfterAll
+	public static void cleanScanCache() {
+		ComponentScanner.cleanCache();
+		FlowBus.cleanCache();
+		ExecutorHelper.loadInstance().clearExecutorServiceMap();
+		SpiFactoryCleaner.clean();
+		LiteflowConfigGetter.clean();
+		FlowInitHook.cleanHook();
+	}
+
+}

+ 119 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/CatchELBuilderTest.java

@@ -0,0 +1,119 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 捕获异常组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = CatchELBuilderTest.class)
+@EnableAutoConfiguration
+public class CatchELBuilderTest extends BaseTest {
+    // catch捕获异常调用测试
+    @Test
+    public void testCatch1(){
+        String actualStr = "CATCH(THEN(node(\"a\"),node(\"b\"))).DO(node(\"c\"))";
+        Assertions.assertEquals(ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testCatch2(){
+        String actualStr = "CATCH(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n)";
+        Assertions.assertEquals(ELBus.catchException(ELBus.then("a", "b")).doOpt("c").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 属性设置测试
+    @Test
+    public void testCatch3(){
+        String actualStr = "CATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testCatch4(){
+        String actualStr = "CATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).id("this is a id").tag("this is a tag").maxWaitSeconds(3).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data 设置 jsonStr
+    @Test
+    public void testCatch5(){
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testCatch6(){
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data 设置 map
+    @Test
+    public void testCatch7(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testCatch8(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data 设置 bean
+    @Test
+    public void testCatch9(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testCatch10(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "catchData = '{\"name\":\"zhangsan\",\"age\":18}';\nCATCH(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(catchData)";
+        Assertions.assertEquals(ELBus.catchException("a").doOpt(ELBus.then("b", "c")).data("catchData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+}

+ 120 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ComplexELBuilderTest.java

@@ -0,0 +1,120 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+import com.yomahub.liteflow.builder.el.ThenELWrapper;
+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;
+
+/**
+ * 复杂编排例子测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = ComplexELBuilderTest.class)
+@EnableAutoConfiguration
+public class ComplexELBuilderTest extends BaseTest {
+    /*
+    复杂编排例子1
+    THEN(
+        A,
+        WHEN(
+            THEN(B, C),
+            THEN(D, E, F),
+            THEN(
+                SWITCH(G).to(
+                    THEN(H, I, WHEN(J, K)).id("t1"),
+                    THEN(L, M).id("t2")
+                ),
+                N
+            )
+        ),
+        Z
+    );
+     */
+    @Test
+    public void testComplexEL1() {
+        ThenELWrapper complexEl1 = ELBus.then(
+                ELBus.node("A"),
+                ELBus.when(
+                        ELBus.then("B", "C"),
+                        ELBus.then(ELBus.node("D")).then("E").then("F"),
+                        ELBus.then(
+                                ELBus.switchOpt("G").to(
+                                        ELBus.then("H", ELBus.node("I"), ELBus.when("J").when("K")).id("t1"),
+                                        ELBus.then("L", "M").id("t2")
+                                ),
+                                "N"
+                        )
+                ),
+                "Z"
+        );
+        String actualStr = "THEN(node(\"A\"),WHEN(THEN(node(\"B\"),node(\"C\")),THEN(node(\"D\"),node(\"E\"),node(\"F\")),THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\"),WHEN(node(\"J\"),node(\"K\"))).id(\"t1\"),THEN(node(\"L\"),node(\"M\")).id(\"t2\")),node(\"N\"))),node(\"Z\"))";
+        Assertions.assertEquals(complexEl1.toEL(),
+                actualStr);
+        System.out.println(actualStr);
+
+        actualStr = "THEN(\n\tnode(\"A\"),\n\tWHEN(\n\t\tTHEN(\n\t\t\tnode(\"B\"),\n\t\t\tnode(\"C\")\n\t\t),\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t),\n\t\tTHEN(\n\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\tnode(\"I\"),\n\t\t\t\t\tWHEN(\n\t\t\t\t\t\tnode(\"J\"),\n\t\t\t\t\t\tnode(\"K\")\n\t\t\t\t\t)\n\t\t\t\t).id(\"t1\"),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t).id(\"t2\")\n\t\t\t),\n\t\t\tnode(\"N\")\n\t\t)\n\t),\n\tnode(\"Z\")\n)";
+        Assertions.assertEquals(complexEl1.toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+
+    }
+
+    /*
+    复杂编排例子2
+    THEN(
+        A,
+        SWITCH(B).to(
+            THEN(D, E, F).id("t1"),
+            THEN(
+                C,
+                WHEN(
+                    THEN(
+                        SWITCH(G).to(THEN(H, I).id("t2"), J),
+                        K
+                    ),
+                    THEN(L, M)
+                )
+            ).id("t3")
+        ),
+        Z
+    );
+     */
+    @Test
+    public void testComplexEl2(){
+        ThenELWrapper complexEl2 = ELBus.then(
+                ELBus.node("A"),
+                ELBus.switchOpt(ELBus.node("B")).to(
+                        ELBus.then("D","E").then(ELBus.node("F")).id("t1"),
+                        ELBus.then(
+                                ELBus.node("C"),
+                                ELBus.when(
+                                        ELBus.then(
+                                                ELBus.switchOpt("G").to(
+                                                        ELBus.then("H", "I").id("t2"),
+                                                        "J"
+                                                ),
+                                                "K"
+                                        ),
+                                        ELBus.then("L", "M")
+                                )
+                        ).id("t3")
+                ),
+                ELBus.node("Z")
+        );
+        String actualStr = "THEN(node(\"A\"),SWITCH(node(\"B\")).TO(THEN(node(\"D\"),node(\"E\"),node(\"F\")).id(\"t1\"),THEN(node(\"C\"),WHEN(THEN(SWITCH(node(\"G\")).TO(THEN(node(\"H\"),node(\"I\")).id(\"t2\"),node(\"J\")),node(\"K\")),THEN(node(\"L\"),node(\"M\")))).id(\"t3\")),node(\"Z\"))";
+        Assertions.assertEquals(complexEl2.toEL(),
+                actualStr);
+        System.out.println(actualStr);
+
+        actualStr = "THEN(\n\tnode(\"A\"),\n\tSWITCH(node(\"B\")).TO(\n\t\tTHEN(\n\t\t\tnode(\"D\"),\n\t\t\tnode(\"E\"),\n\t\t\tnode(\"F\")\n\t\t).id(\"t1\"),\n\t\tTHEN(\n\t\t\tnode(\"C\"),\n\t\t\tWHEN(\n\t\t\t\tTHEN(\n\t\t\t\t\tSWITCH(node(\"G\")).TO(\n\t\t\t\t\t\tTHEN(\n\t\t\t\t\t\t\tnode(\"H\"),\n\t\t\t\t\t\t\tnode(\"I\")\n\t\t\t\t\t\t).id(\"t2\"),\n\t\t\t\t\t\tnode(\"J\")\n\t\t\t\t\t),\n\t\t\t\t\tnode(\"K\")\n\t\t\t\t),\n\t\t\t\tTHEN(\n\t\t\t\t\tnode(\"L\"),\n\t\t\t\t\tnode(\"M\")\n\t\t\t\t)\n\t\t\t)\n\t\t).id(\"t3\")\n\t),\n\tnode(\"Z\")\n)";
+        Assertions.assertEquals(complexEl2.toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+}

+ 285 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/IfELBuilderTest.java

@@ -0,0 +1,285 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 条件组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = IfELBuilderTest.class)
+@EnableAutoConfiguration
+public class IfELBuilderTest extends BaseTest {
+    // if三元函数测试
+    @Test
+    public void testIf1(){
+        String actualStr = "IF(node(\"a\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\")))";
+        Assertions.assertEquals(ELBus.ifOpt("a", ELBus.then("c", "d"), ELBus.when("e", "f")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf2(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.node("a"), ELBus.then("c", "d"), ELBus.when("e", "f")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // If二元函数测试
+    @Test
+    public void testIf3(){
+        String actualStr = "IF(node(\"a\"),THEN(node(\"b\"),node(\"c\"))).ELSE(WHEN(node(\"c\"),node(\"d\")))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.node("a"), ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf4(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).ELSE(\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.ifOpt("a", ELBus.then("b", "c")).elseOpt(ELBus.when("c", "d")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // ELIF调用测试
+    @Test
+    public void testIf5(){
+        String actualStr = "IF(node(\"a\"),node(\"b\")).ELIF(node(\"f1\"),node(\"c\")).ELIF(node(\"f2\"),node(\"d\")).ELSE(node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf6(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELIF(\n\tnode(\"f1\"),\n\tnode(\"c\")\n).ELIF(\n\tnode(\"f2\"),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b").elIfOpt("f1", "c").elIfOpt("f2","d").elseOpt("e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // IF嵌套调用测试
+    @Test
+    public void testIf7(){
+        String actualStr = "IF(node(\"a\"),node(\"b\"),IF(node(\"c\"),node(\"d\")).ELSE(node(\"e\")))";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf8(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).ELSE(\n\t\tnode(\"e\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", ELBus.ifOpt("c", "d").elseOpt("e")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // IF嵌套调用测试
+    @Test
+    public void testIf9(){
+        String actualStr = "IF(node(\"a\"),node(\"b\")).ELSE(IF(node(\"c\"),node(\"d\"),node(\"e\")))";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf10(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\")\n).ELSE(\n\tIF(\n\t\tnode(\"c\"),\n\t\tnode(\"d\"),\n\t\tnode(\"e\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b").elseOpt(ELBus.ifOpt("c", "d", "e")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 与表达式输出测试
+    @Test
+    public void testIf11(){
+        String actualStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(AND(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(AND(node(\"f1\"),node(\"f2\")),node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf12(){
+        String actualStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d", "e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elseOpt("e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tAND(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.and("a", "b", "c"), "d").elIfOpt(ELBus.and("f1", "f2"), "e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 或表达式测试
+    @Test
+    public void testIf13(){
+        String actualStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\"),node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELSE(node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(OR(node(\"a\"),node(\"b\"),node(\"c\")),node(\"d\")).ELIF(OR(node(\"f1\"),node(\"f2\")),node(\"e\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf14(){
+        String actualStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d", "e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELSE(\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elseOpt("e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n).ELIF(\n\tOR(\n\t\tnode(\"f1\"),\n\t\tnode(\"f2\")\n\t),\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.or("a", "b", "c"), "d").elIfOpt(ELBus.or("f1", "f2"), "e").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 非表达式测试
+    @Test
+    public void testIf15(){
+        String actualStr = "IF(NOT(node(\"a\")),node(\"b\"),node(\"c\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(NOT(node(\"a\")),node(\"b\")).ELSE(node(\"c\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(NOT(node(\"a\")),node(\"b\")).ELIF(NOT(node(\"f\")),node(\"c\"))";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testIf16(){
+        String actualStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\"),\n\tnode(\"c\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b", "c").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELSE(\n\tnode(\"c\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b").elseOpt("c").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "IF(\n\tNOT(\n\t\tnode(\"a\")\n\t),\n\tnode(\"b\")\n).ELIF(\n\tNOT(\n\t\tnode(\"f\")\n\t),\n\tnode(\"c\")\n)";
+        Assertions.assertEquals(ELBus.ifOpt(ELBus.not("a"), "b").elIfOpt(ELBus.not("f"), "c").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 属性测试
+    @Test
+    public void testIf17(){
+        String actualStr = "IF(node(\"a\"),node(\"b\"),node(\"c\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出
+    @Test
+    public void testIf18(){
+        String actualStr = "IF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(6)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").id("this is a id").tag("this is a tag").maxWaitSeconds(6).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data map 测试
+    @Test
+    public void testIf19(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出
+    @Test
+    public void testIf20(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data JsonStr 测试
+    @Test
+    public void testIf21(){
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出
+    @Test
+    public void testIf22(){
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data Bean 测试
+    @Test
+    public void testIf23(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(node(\"a\"),node(\"b\"),node(\"c\")).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出
+    @Test
+    public void testIf24(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "ifData = '{\"name\":\"zhangsan\",\"age\":18}';\nIF(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n).data(ifData)";
+        Assertions.assertEquals(ELBus.ifOpt("a", "b", "c").data("ifData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+}

+ 171 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LogicELBuilderTest.java

@@ -0,0 +1,171 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 与或非表达式测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = LogicELBuilderTest.class)
+@EnableAutoConfiguration
+public class LogicELBuilderTest extends BaseTest {
+    // 与或非表达式调用 测试
+    @Test
+    public void testlogic1(){
+        String actualStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")))";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic2(){
+        String actualStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testlogic3(){
+        String actualStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")))";
+        Assertions.assertEquals(ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testlogic4(){
+        String actualStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.and("a").and(ELBus.or("b").or("c")).and(ELBus.not("d")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 属性设置
+    @Test
+    public void testlogic5(){
+        String actualStr = "AND(node(\"a\"),OR(node(\"b\"),node(\"c\")).id(\"this is a id\").maxWaitSeconds(4),NOT(node(\"d\")).tag(\"this is a tag\"))";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c").id("this is a id").maxWaitSeconds(4), ELBus.not("d").tag("this is a tag")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic6(){
+        String actualStr = "AND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").maxWaitSeconds(4),\n\tNOT(\n\t\tnode(\"d\")\n\t).tag(\"this is a tag\")\n)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c").id("this is a id").maxWaitSeconds(4), ELBus.not("d").tag("this is a tag")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic7(){
+        String actualStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\"))).data(andData)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("andData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic8(){
+        String actualStr = "andData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n).data(andData)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("andData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic9(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\"))).data(orData)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("orData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic10(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "orData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t)\n).data(orData)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d")).data("orData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    @Test
+    public void testlogic11(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(node(\"a\"),OR(node(\"b\"),node(\"c\")),NOT(node(\"d\")).data(notData))";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d").data("notData", name2Value)).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testlogic12(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "notData = '{\"name\":\"zhangsan\",\"age\":18}';\nAND(\n\tnode(\"a\"),\n\tOR(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tNOT(\n\t\tnode(\"d\")\n\t).data(notData)\n)";
+        Assertions.assertEquals(ELBus.and("a", ELBus.or("b", "c"), ELBus.not("d").data("notData", name2Value)).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // NOT调用方法补充测试
+    @Test
+    public void testLogic13(){
+        String actualStr = "NOT(node(\"a\"))";
+        Assertions.assertEquals(ELBus.not(ELBus.node("a")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(AND(node(\"a\"),node(\"b\"),node(\"c\")))";
+        Assertions.assertEquals(ELBus.not(ELBus.and("a", "b", "c")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(OR(node(\"a\"),node(\"b\"),node(\"c\")))";
+        Assertions.assertEquals(ELBus.not(ELBus.or("a", "b", "c")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(NOT(node(\"a\")))";
+        Assertions.assertEquals(ELBus.not(ELBus.not(ELBus.node("a"))).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLogic14(){
+        String actualStr = "NOT(\n\tnode(\"a\")\n)";
+        Assertions.assertEquals(ELBus.not(ELBus.node("a")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.not(ELBus.and("a", "b", "c")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.not(ELBus.or("a", "b", "c")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "NOT(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.not(ELBus.not(ELBus.node("a"))).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+}

+ 278 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/LoopELBuilderTest.java

@@ -0,0 +1,278 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 循环组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = LoopELBuilderTest.class)
+@EnableAutoConfiguration
+public class LoopELBuilderTest extends BaseTest {
+    // for 限定次数循环
+    @Test
+    public void testLoop1(){
+        String actualStr = "FOR(3).DO(THEN(node(\"a\"),node(\"b\"),node(\"c\"))).BREAK(node(\"d\"))";
+        Assertions.assertEquals(ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出
+    @Test
+    public void testLoop2(){
+        String actualStr = "FOR(3).DO(\n\tTHEN(\n\t\tnode(\"a\"),\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\")\n)";
+        Assertions.assertEquals(ELBus.forOpt(3).doOpt(ELBus.then("a", "b", "c")).breakOpt("d").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // for 单节点循环测试
+    @Test
+    public void testLoop3(){
+        String actualStr = "FOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(AND(node(\"e\"),node(\"f\")))";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop4(){
+        String actualStr = "FOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tAND(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt(ELBus.and("e", "f")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // parallel语句测试
+    @Test
+    public void testLoop5(){
+        String actualStr = "FOR(node(\"a\")).parallel(true).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\"))";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop6(){
+        String actualStr = "FOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\")\n)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").parallel(true).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 属性测试
+    @Test
+    public void testLoop7(){
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\"),node(\"d\"))).BREAK(node(\"e\")).id(\"this is a id\").tag(\"this is a tag\").data(forData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.when("b", "c", "d")).breakOpt("e").id("this is a id").tag("this is a tag").maxWaitSeconds(3).data("forData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop8(){
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\nFOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t)\n).BREAK(\n\tnode(\"e\")\n).id(\"this is a id\").tag(\"this is a tag\").data(forData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.forOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c", "d")).breakOpt("e").id("this is a id").tag("this is a tag").maxWaitSeconds(3).data("forData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // while调用测试
+    @Test
+    public void testLoop9(){
+        String actualStr = "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"f\"))";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(),
+                actualStr);
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.node("a")).doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(AND(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"))";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(OR(node(\"a\"),node(\"b\"))).DO(node(\"c\")).BREAK(node(\"d\"))";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(NOT(node(\"a\"))).DO(node(\"c\")).BREAK(node(\"d\"))";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop10(){
+        String actualStr = "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"f\")\n)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(true),
+                actualStr);
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.node("a")).doOpt(ELBus.then("b", "c")).breakOpt("f").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(\n\tAND(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.and("a", "b")).doOpt("c").breakOpt("d").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(\n\tOR(\n\t\tnode(\"a\"),\n\t\tnode(\"b\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.or("a", "b")).doOpt("c").breakOpt("d").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "WHILE(\n\tNOT(\n\t\tnode(\"a\")\n\t)\n).DO(\n\tnode(\"c\")\n).BREAK(\n\tnode(\"d\")\n)";
+        Assertions.assertEquals(ELBus.whileOpt(ELBus.not("a")).doOpt("c").breakOpt("d").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // while属性调用测试
+    @Test
+    public void testLoop11(){
+        String actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\"))).BREAK(node(\"d\")).id(\"this is a ig\").tag(\"this is a tag\").data(whileData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("d").id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("whileData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop12(){
+        String actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHILE(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).BREAK(\n\tnode(\"d\")\n).id(\"this is a ig\").tag(\"this is a tag\").data(whileData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).breakOpt("d").id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("whileData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // Iterator 调用测试
+    @Test
+    public void testLoop13(){
+        String actualStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")))";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "ITERATOR(node(\"a\")).DO(WHEN(node(\"b\"),node(\"c\")))";
+        Assertions.assertEquals(ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop14(){
+        String actualStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.when("b", "c")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.iteratorOpt(ELBus.node("a")).doOpt(ELBus.when("b", "c")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // iterator 属性测试
+    @Test
+    public void testLoop15(){
+        String actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(node(\"a\")).parallel(true).DO(THEN(node(\"b\"),node(\"c\"))).id(\"this is a ig\").tag(\"this is a tag\").data(iteratorData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("iteratorData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testLoop16(){
+        String actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\nITERATOR(\n\tnode(\"a\")\n).parallel(true).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).id(\"this is a ig\").tag(\"this is a tag\").data(iteratorData).maxWaitSeconds(3)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).id("this is a ig").tag("this is a tag").maxWaitSeconds(3).parallel(true).data("iteratorData", "{\"name\":\"zhangsan\",\"age\":18}").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data Map 参数 测试
+    @Test
+    public void testLoop17(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(forData)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(whileData)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(iteratorData)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testLoop18(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(forData)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(whileData)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(iteratorData)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data JavaBean参数 测试
+    @Test
+    public void testLoop19(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.age = 18;
+        name2Value.name = "zhangsan";
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "FOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(forData)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "WHILE(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(whileData)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "ITERATOR(node(\"a\")).DO(THEN(node(\"b\"),node(\"c\"))).data(iteratorData)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testLoop20(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.age = 18;
+        name2Value.name = "zhangsan";
+        String actualStr = "forData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "FOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(forData)";
+        Assertions.assertEquals(ELBus.forOpt("a").doOpt(ELBus.then("b", "c")).data("forData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "whileData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "WHILE(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(whileData)";
+        Assertions.assertEquals(ELBus.whileOpt("a").doOpt(ELBus.then("b", "c")).data("whileData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+        actualStr = "iteratorData = '{\"name\":\"zhangsan\",\"age\":18}';\n" +
+                "ITERATOR(\n\tnode(\"a\")\n).DO(\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t)\n).data(iteratorData)";
+        Assertions.assertEquals(ELBus.iteratorOpt("a").doOpt(ELBus.then("b", "c")).data("iteratorData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+}

+ 101 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/NodeELBuilderTest.java

@@ -0,0 +1,101 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+import com.yomahub.liteflow.builder.el.NodeELWrapper;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 单节点组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = NodeELBuilderTest.class)
+@EnableAutoConfiguration
+public class NodeELBuilderTest extends BaseTest {
+    @Test
+    public void testNodeEL1(){
+        String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}";
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", jsonStr);
+        Assertions.assertEquals(node.toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testNodeEL2(){
+        String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}";
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", jsonStr);
+        Assertions.assertEquals(node.toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testNodeEL3(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value);
+        Assertions.assertEquals(node.toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testNodeEL4(){
+        Map<String, Object> name2Value = new HashMap<>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value);
+        Assertions.assertEquals(node.toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    @Test
+    public void testNodeEL5(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.age = 18;
+        name2Value.name = "zhangsan";
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value);
+        Assertions.assertEquals(node.toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    @Test
+    public void testNodeEL6(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.age = 18;
+        name2Value.name = "zhangsan";
+        String actualStr = "nodeData = '{\"name\":\"zhangsan\",\"age\":18}'\n" +
+                "node(\"a\").tag(\"node a tag\").data(nodeData).maxWaitSeconds(4)";
+        NodeELWrapper node = ELBus.node("a").maxWaitSeconds(4).tag("node a tag").data("nodeData", name2Value);
+        Assertions.assertEquals(node.toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+}

+ 151 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/SwitchELBuilderTest.java

@@ -0,0 +1,151 @@
+package com.yomahub.liteflow.test.builder;
+
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 选择组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = SwitchELBuilderTest.class)
+@EnableAutoConfiguration
+public class SwitchELBuilderTest extends BaseTest {
+
+    // Switch调用方法测试
+    @Test
+    public void testSwitch1(){
+        String actualStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).DEFAULT(node(\"f\"))";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // 格式化输出测试
+    @Test
+    public void testSwitch2(){
+        String actualStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).DEFAULT(\n\tnode(\"f\")\n)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").defaultOpt("f").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // switch和THEN when嵌套调用测试
+    @Test
+    public void testSwitch3(){
+        String actualStr = "SWITCH(node(\"a\")).TO(node(\"b\"),THEN(node(\"c\"),node(\"d\")),WHEN(node(\"e\"),node(\"f\"))).DEFAULT(THEN(node(\"g\"),node(\"h\")))";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // 格式化输出测试
+    @Test
+    public void testSwitch4(){
+        String actualStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tTHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t),\n\tWHEN(\n\t\tnode(\"e\"),\n\t\tnode(\"f\")\n\t)\n).DEFAULT(\n\tTHEN(\n\t\tnode(\"g\"),\n\t\tnode(\"h\")\n\t)\n)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", ELBus.then("c", "d"), ELBus.when("e", "f")).defaultOpt(ELBus.then("g", "h")).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // 属性设置测试
+    @Test
+    public void testSwitch5(){
+        String actualStr = "SWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // 格式化输出测试
+    @Test
+    public void testSwitch6(){
+        String actualStr = "SWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).id(\"this is a id\").tag(\"this is a tag\").maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").id("this is a id").tag("this is a tag").maxWaitSeconds(5).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    // data属性测试
+    @Test
+    public void testSwitch7(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testSwitch8(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testSwitch9(){
+        String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}";
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", jsonStr).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testSwitch10(){
+        String jsonStr = "{\"name\":\"zhangsan\",\"age\":18}";
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", jsonStr).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+
+    @Test
+    public void testSwitch11(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(node(\"b\"),node(\"c\"),node(\"d\")).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+    @Test
+    public void testSwitch12(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "switchData = '{\"name\":\"zhangsan\",\"age\":18}';\nSWITCH(node(\"a\")).TO(\n\tnode(\"b\"),\n\tnode(\"c\"),\n\tnode(\"d\")\n).data(switchData)";
+        Assertions.assertEquals(ELBus.switchOpt("a").to("b", "c", "d").data("switchData", name2Value).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+
+}

+ 189 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/ThenELBuilderTest.java

@@ -0,0 +1,189 @@
+package com.yomahub.liteflow.test.builder;
+
+import cn.hutool.json.JSONUtil;
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 串行组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = ThenELBuilderTest.class)
+@EnableAutoConfiguration
+public class ThenELBuilderTest extends BaseTest {
+
+    // then组件测试
+    @Test
+    public void testThen1(){
+        Assertions.assertEquals(ELBus.then("a", "b").toEL(), "THEN(node(\"a\"),node(\"b\"))");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen2(){
+        Assertions.assertEquals(ELBus.then("a", "b").toEL(true),
+                "THEN(\n\tnode(\"a\")," +
+                        "\n\tnode(\"b\")\n)");
+        System.out.println("THEN(\n\tnode(\"a\")," +
+                "\n\tnode(\"b\")\n)");
+    }
+    // then组件then方法调用测试
+    @Test
+    public void testThen3(){
+        Assertions.assertEquals(ELBus.then("a", "b").then("c").toEL(),
+                "THEN(node(\"a\"),node(\"b\"),node(\"c\"))");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen4(){
+        Assertions.assertEquals(ELBus.then("a", "b").then("c").toEL(true),
+                "THEN(\n\tnode(\"a\"),\n\tnode(\"b\")," +
+                        "\n\tnode(\"c\")\n)");
+        System.out.println("THEN(\n\tnode(\"a\"),\n\tnode(\"b\")," +
+                "\n\tnode(\"c\")\n)");
+    }
+    // then组件嵌套调用测试
+    @Test
+    public void testThen5(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL(),
+                "THEN(node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen6(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").toEL(true),
+                "THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)");
+        System.out.println("THEN(\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)");
+    }
+    // pre组件测试
+    @Test
+    public void testThen7(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL(),
+                "THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))");
+        System.out.println("THEN(PRE(node(\"p\")),PRE(node(\"pp\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"))");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen8(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").pre("pp").toEL(true),
+                "THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)");
+        System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tPRE(\n\t\tnode(\"pp\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)");
+    }
+    // pre finally 格式测试
+    @Test
+    public void testThen9(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL(),
+                "THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")))");
+        System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")),node(\"d\"),FINALLY(node(\"f\")))");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen10(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c")).then("d").pre("p").finallyOpt("f").toEL(true),
+                "THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n)");
+        System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\"),\n\tFINALLY(\n\t\tnode(\"f\")\n\t)\n)");
+    }
+    // 属性设置测试
+    @Test
+    public void testThen11(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL(),
+                "THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\")");
+        System.out.println("THEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\")");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen12(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").pre("p").finallyOpt("f").toEL(true),
+                "THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\")");
+        System.out.println("THEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\")");
+    }
+    // data属性测试
+    @Test
+    public void testThen13(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        System.out.println(JSONUtil.toJsonStr(name2Value));
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen14(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(true),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+    }
+    // data属性测试 Json字符串赋值data
+    @Test
+    public void testThen15(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}").pre("p").finallyOpt("f").toEL(),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+    }
+    // 格式化输出测试 Json字符串赋值data
+    @Test
+    public void testThen16(){
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", "{\"name\":\"zhangsan\",\"age\":18}").pre("p").finallyOpt("f").toEL(true),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data属性测试
+    @Test
+    public void testThen17(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(PRE(node(\"p\")),node(\"a\"),THEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\"),FINALLY(node(\"f\"))).tag(\"this is a tag\").data(thenData)");
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen18(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        Assertions.assertEquals(ELBus.then("a", ELBus.then("b").then("c").id("this is a id")).tag("this is a tag").then("d").data("thenData", name2Value).pre("p").finallyOpt("f").toEL(true),
+                "thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+        System.out.println("thenData = '{\"name\":\"zhangsan\",\"age\":18}';\nTHEN(\n\tPRE(\n\t\tnode(\"p\")\n\t),\n\tnode(\"a\"),\n\tTHEN(\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\").data(thenData)");
+    }
+    // maxWaitSecond测试
+    @Test
+    public void testThen19(){
+        String actualStr = "THEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.then("a").then("b").maxWaitSeconds(5).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testThen20(){
+        String actualStr = "THEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.then("a").then("b").maxWaitSeconds(5).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+}

+ 186 - 0
liteflow-testcase-el/liteflow-testcase-el-builder/src/test/java/com/yomahub/liteflow/test/builder/WhenELBuilderTest.java

@@ -0,0 +1,186 @@
+package com.yomahub.liteflow.test.builder;
+
+import cn.hutool.json.JSONUtil;
+import com.yomahub.liteflow.builder.el.ELBus;
+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 java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 并行组件测试
+ *
+ * @author gezuao
+ * @since 2.11.1
+ */
+@SpringBootTest(classes = WhenELBuilderTest.class)
+@EnableAutoConfiguration
+public class WhenELBuilderTest extends BaseTest {
+    // then组件测试
+    @Test
+    public void testWhen1(){
+        String actualStr = "WHEN(node(\"a\"),node(\"b\"))";
+        Assertions.assertEquals(ELBus.when("a", "b").toEL(), actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen2(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n)";
+        Assertions.assertEquals(ELBus.when("a", "b").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // then组件then方法调用测试
+    @Test
+    public void testWhen3(){
+        String actualStr = "WHEN(node(\"a\"),node(\"b\"),node(\"c\"))";
+        Assertions.assertEquals(ELBus.when("a", "b").when("c").toEL(),
+                actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen4(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tnode(\"c\")\n)";
+        Assertions.assertEquals(ELBus.when("a", "b").when("c").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // then组件嵌套调用测试
+    @Test
+    public void testWhen5(){
+        String actualStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")),node(\"d\"))";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL(),
+                actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen6(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t),\n\tnode(\"d\")\n)";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c")).when("d").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // WHEN特有属性测试 any ignoreError customThreadExecutor must
+    @Test
+    public void testWhen7(){
+        String actualStr = "WHEN(node(\"a\"),node(\"b\"),WHEN(node(\"c\"),node(\"d\")).any(true).threadPool(\"WhenELBuilderTest.customThreadPool\").id(\"node1\")).ignoreError(true).must(\"a\", \"task1\", \"node1\")";
+        Assertions.assertEquals(ELBus.when("a", "b",ELBus.when("c").when("d").customThreadExecutor("WhenELBuilderTest.customThreadPool").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen8(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\"),\n\tWHEN(\n\t\tnode(\"c\"),\n\t\tnode(\"d\")\n\t).any(true).threadPool(\"WhenELBuilderTest.customThreadPool\").id(\"node1\")\n).ignoreError(true).must(\"a\", \"task1\", \"node1\")";
+        Assertions.assertEquals(ELBus.when("a", "b",ELBus.when("c").when("d").customThreadExecutor("WhenELBuilderTest.customThreadPool").id("node1").any(true)).ignoreError(true).must("a", "task1", "node1").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // maxWaitSeconds 属性测试
+    @Test
+    public void testWhen9(){
+        String actualStr = "WHEN(node(\"a\"),node(\"b\")).maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.when("a", "b").maxWaitSeconds(5).toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen10(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tnode(\"b\")\n).maxWaitSeconds(5)";
+        Assertions.assertEquals(ELBus.when("a", "b").maxWaitSeconds(5).toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 属性设置测试
+    @Test
+    public void testWhen11(){
+        String actualStr = "WHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\"),node(\"d\")).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen12(){
+        String actualStr = "WHEN(\n\tnode(\"a\"),\n\tWHEN(\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(ELBus.when("a", ELBus.when("b").when("c").id("this is a id")).when("d").tag("this is a tag").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data属性测试
+    @Test
+    public void testWhen13(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        System.out.println(JSONUtil.toJsonStr(name2Value));
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(false),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen14(){
+        Map<String, Object> name2Value = new HashMap<String, Object>();
+        name2Value.put("name", "zhangsan");
+        name2Value.put("age", 18);
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // data属性测试 Json字符串赋值data
+    @Test
+    public void testWhen15(){
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}").id("this is a id")).when("d").tag("this is a tag").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试 Json字符串赋值data
+    @Test
+    public void testWhen16(){
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", "{\"name\":\"zhangsan\",\"age\":18}").id("this is a id")).when("d").tag("this is a tag").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    private static class ParamClass{
+        private String name;
+        private Integer age;
+        public String getName(){
+            return name;
+        }
+        public Integer getAge(){
+            return age;
+        }
+    }
+    // data属性测试
+    @Test
+    public void testWhen17(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(node(\"a\"),WHEN(node(\"b\"),node(\"c\")).id(\"this is a id\").data(whenData),node(\"d\")).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(),
+                actualStr);
+        System.out.println(actualStr);
+    }
+    // 格式化输出测试
+    @Test
+    public void testWhen18(){
+        ParamClass name2Value = new ParamClass();
+        name2Value.name = "zhangsan";
+        name2Value.age = 18;
+        String actualStr = "whenData = '{\"name\":\"zhangsan\",\"age\":18}';\nWHEN(\n\tnode(\"a\"),\n\tWHEN(\n\t\tnode(\"b\"),\n\t\tnode(\"c\")\n\t).id(\"this is a id\").data(whenData),\n\tnode(\"d\")\n).tag(\"this is a tag\")";
+        Assertions.assertEquals(ELBus.when("a", ELBus.when("b").when("c").data("whenData", name2Value).id("this is a id")).when("d").tag("this is a tag").toEL(true),
+                actualStr);
+        System.out.println(actualStr);
+    }
+}

+ 1 - 0
liteflow-testcase-el/pom.xml

@@ -37,6 +37,7 @@
         <module>liteflow-testcase-el-script-aviator-springboot</module>
         <module>liteflow-testcase-el-sql-springboot-dynamic</module>
         <module>liteflow-testcase-el-script-java-springboot</module>
+        <module>liteflow-testcase-el-builder</module>
     </modules>
 
     <build>

+ 7 - 0
pom.xml

@@ -53,6 +53,7 @@
 		<curator.version>5.3.0</curator.version>
 		<junit.version>5.8.2</junit.version>
 		<hutool-core.version>5.8.11</hutool-core.version>
+		<hutool-json.version>5.8.11</hutool-json.version>
 		<transmittable-thread-local.version>2.12.3</transmittable-thread-local.version>
 		<curator-test.version>5.1.0</curator-test.version>
 		<zkclient.version>0.10</zkclient.version>
@@ -175,6 +176,11 @@
 				<artifactId>hutool-core</artifactId>
 				<version>${hutool-core.version}</version>
 			</dependency>
+			<dependency>
+				<groupId>cn.hutool</groupId>
+				<artifactId>hutool-json</artifactId>
+				<version>${hutool-json.version}</version>
+			</dependency>
 			<dependency>
 				<groupId>com.alibaba</groupId>
 				<artifactId>transmittable-thread-local</artifactId>
@@ -425,6 +431,7 @@
 		<module>liteflow-spring</module>
 		<module>liteflow-solon-plugin</module>
 		<module>liteflow-testcase-el</module>
+        <module>liteflow-el-builder</module>
 	</modules>
 
 	<distributionManagement>