feature #I5RV5D 循环表达式特性的增加

This commit is contained in:
everywhere.z
2022-09-29 01:04:05 +08:00
parent 24443f0f91
commit 1c2b75e08d
34 changed files with 832 additions and 50 deletions

View File

@@ -0,0 +1,10 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowBreakCmpDefine {
}

View File

@@ -0,0 +1,10 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowForCmpDefine {
}

View File

@@ -0,0 +1,10 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowWhileCmpDefine {
}

View File

@@ -36,6 +36,18 @@ public class LiteFlowNodeBuilder {
return new LiteFlowNodeBuilder(NodeTypeEnum.IF);
}
public static LiteFlowNodeBuilder createForNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR);
}
public static LiteFlowNodeBuilder createWhileNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.WHILE);
}
public static LiteFlowNodeBuilder createBreakNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.BREAK);
}
public static LiteFlowNodeBuilder createScriptNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
}
@@ -48,6 +60,18 @@ public class LiteFlowNodeBuilder {
return new LiteFlowNodeBuilder(NodeTypeEnum.IF_SCRIPT);
}
public static LiteFlowNodeBuilder createScriptForNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR_SCRIPT);
}
public static LiteFlowNodeBuilder createScriptWhileNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.WHILE_SCRIPT);
}
public static LiteFlowNodeBuilder createScriptBreakNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.BREAK_SCRIPT);
}
public LiteFlowNodeBuilder() {
this.node = new Node();
}
@@ -114,12 +138,24 @@ public class LiteFlowNodeBuilder {
FlowBus.addSwitchNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.IF)) {
FlowBus.addIfNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.FOR)) {
FlowBus.addForNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.WHILE)) {
FlowBus.addWhileNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.BREAK)) {
FlowBus.addBreakNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.SCRIPT)) {
FlowBus.addCommonScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.SWITCH_SCRIPT)) {
FlowBus.addSwitchScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.IF_SCRIPT)) {
FlowBus.addIfScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.FOR_SCRIPT)) {
FlowBus.addForScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.WHILE_SCRIPT)) {
FlowBus.addWhileScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.BREAK_SCRIPT)) {
FlowBus.addBreakScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
}
} catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(),e.getMessage());

View File

@@ -61,6 +61,10 @@ public class LiteFlowChainELBuilder {
EXPRESS_RUNNER.addFunctionAndClassMethod("ignoreError", Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("threadPool", Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addFunction("node", new NodeOperator());
EXPRESS_RUNNER.addFunction("FOR", new ForOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("DO", Object.class, new DoOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("BREAK", Object.class, new BreakOperator());
}
public static LiteFlowChainELBuilder createChain() {

View File

@@ -0,0 +1,54 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
import com.yomahub.liteflow.flow.element.condition.WhileCondition;
/**
* EL规则中的BREAK的操作符
* 有两种用法
* FOR...DO...BREAK
* WHILE...DO...BREAK
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class BreakOperator extends BaseOperator<LoopCondition> {
@Override
public LoopCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
//由于BREAK关键字有可能用在FOR后面也有可能用于WHILE后面所以这里要进行判断
LoopCondition condition;
if (objects[0] instanceof ForCondition){
//获得caller也就是ForCondition
condition = OperatorHelper.convert(objects[0], ForCondition.class);
}else if(objects[0] instanceof WhileCondition){
//获得caller也就是WhileCondition
condition = OperatorHelper.convert(objects[0], WhileCondition.class);
}else{
throw new QLException("The caller must be ForCondition or WhileCondition item");
}
//获得需要执行的可执行表达式
if (objects[1] instanceof Node){
Node breakNode = OperatorHelper.convert(objects[1], Node.class);
if (ListUtil.toList(NodeTypeEnum.BREAK, NodeTypeEnum.BREAK_SCRIPT).contains(breakNode.getType())){
condition.setBreakNode(breakNode);
}else{
throw new QLException("The parameter must be node-break item");
}
}else{
throw new QLException("The parameter must be Node item");
}
return condition;
}
}

View File

@@ -0,0 +1,50 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
import com.yomahub.liteflow.flow.element.condition.WhileCondition;
/**
* EL规则中的DO的操作符
* 有两种用法
* FOR...DO...BREAK
* WHILE...DO...BREAK
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class DoOperator extends BaseOperator<LoopCondition> {
@Override
public LoopCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
//由于DO关键字有可能用在FOR后面也有可能用于WHILE后面所以这里要进行判断
LoopCondition condition;
if (objects[0] instanceof ForCondition){
//获得caller也就是ForCondition
condition = OperatorHelper.convert(objects[0], ForCondition.class);
}else if(objects[0] instanceof WhileCondition){
//获得caller也就是WhileCondition
condition = OperatorHelper.convert(objects[0], WhileCondition.class);
}else{
throw new QLException("The caller must be ForCondition or WhileCondition item");
}
//获得需要执行的可执行表达式
if (objects[1] instanceof Executable){
Executable doExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
condition.setExecutableList(ListUtil.toList(doExecutableItem));
}else{
throw new QLException("The parameter must be Executable item");
}
return condition;
}
}

View File

@@ -0,0 +1,46 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.core.NodeForComponent;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
/**
* EL规则中的FOR的操作符
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ForOperator extends BaseOperator<ForCondition> {
@Override
public ForCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
Node node;
if (objects[0] instanceof Node) {
node = (Node) objects[0];
if (!ListUtil.toList(NodeTypeEnum.FOR, NodeTypeEnum.FOR_SCRIPT).contains(node.getType())) {
throw new QLException("The parameter must be for-node item");
}
}else if(objects[0] instanceof Integer){
Integer forCount = (Integer) objects[0];
node = new Node();
node.setInstance(new NodeForComponent() {
@Override
public int processFor(){
return forCount;
}
});
}else{
throw new QLException("The parameter must be Node item");
}
ForCondition forCondition = new ForCondition();
forCondition.setForNode(node);
return forCondition;
}
}

View File

@@ -0,0 +1,36 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.WhileCondition;
/**
* EL规则中的WHILE的操作符
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class WhileOperator extends BaseOperator<WhileCondition> {
@Override
public WhileCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
Node node;
if (objects[0] instanceof Node){
node = (Node) objects[0];
if (!ListUtil.toList(NodeTypeEnum.WHILE, NodeTypeEnum.WHILE_SCRIPT).contains(node.getType())) {
throw new QLException("The parameter must be while-node item");
}
}else{
throw new QLException("The parameter must be Node item");
}
WhileCondition whileCondition = new WhileCondition();
whileCondition.setWhileNode(node);
return whileCondition;
}
}

View File

@@ -126,7 +126,7 @@ public class OperatorHelper {
return (T) object;
}
throw new QLException("The caller must be " + tClass.getName() + " item");
throw new QLException("The parameter must be " + tClass.getName() + " item");
}
/**

View File

@@ -29,7 +29,7 @@ public class ComponentInitializer {
return instance;
}
public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String desc, String nodeId){
public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String name, String nodeId){
nodeComponent.setNodeId(nodeId);
nodeComponent.setSelf(nodeComponent);
nodeComponent.setType(type);
@@ -42,11 +42,9 @@ public class ComponentInitializer {
//先取传进来的name值(配置文件中配置的),再看有没有配置@LiteflowComponent标注
//@LiteflowComponent标注只在spring体系下生效这里用了spi机制取到相应环境下的实现类
nodeComponent.setName(desc);
if (ListUtil.toList(NodeTypeEnum.COMMON, NodeTypeEnum.SWITCH, NodeTypeEnum.IF).contains(type)
&& StrUtil.isBlank(nodeComponent.getName())){
String name = LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent);
nodeComponent.setName(name);
nodeComponent.setName(name);
if (!type.isScript() && StrUtil.isBlank(nodeComponent.getName())){
nodeComponent.setName(LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent));
}
//先从组件上取@RetryCount标注如果没有则看全局配置全局配置如果不配置的话默认是0

View File

@@ -0,0 +1,21 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* 循环跳出节点逻辑抽象类
* @author Bryan.Zhang
* @since 2.9.0
*/
public abstract class NodeBreakComponent extends NodeComponent{
@Override
public void process() throws Exception {
boolean breakFlag = processBreak();
Slot slot = this.getSlot();
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
slot.setBreakResult(originalClass.getName(), breakFlag);
}
public abstract boolean processBreak() throws Exception;
}

View File

@@ -0,0 +1,22 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* FOR计数节点抽象类
* @author Bryan.Zhang
* @since 2.9.0
*/
public abstract class NodeForComponent extends NodeComponent{
@Override
public void process() throws Exception {
int forCount = processFor();
Slot slot = this.getSlot();
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
slot.setForResult(originalClass.getName(), forCount);
}
public abstract int processFor() throws Exception;
}

View File

@@ -0,0 +1,21 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* WHILE条件节点抽象类
* @author Bryan.Zhang
* @since 2.9.0
*/
public abstract class NodeWhileComponent extends NodeComponent{
@Override
public void process() throws Exception {
boolean whileFlag = processWhile();
Slot slot = this.getSlot();
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
slot.setWhileResult(originalClass.getName(), whileFlag);
}
public abstract boolean processWhile() throws Exception;
}

View File

@@ -0,0 +1,19 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
/**
* 脚本BREAK节点
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ScriptBreakComponent extends NodeBreakComponent{
@Override
public boolean processBreak() throws Exception {
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@@ -0,0 +1,19 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
/**
* 脚本FOR节点
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ScriptForComponent extends NodeForComponent{
@Override
public int processFor() throws Exception {
return (int) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@@ -0,0 +1,20 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
/**
* 脚本WHILE节点
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ScriptWhileComponent extends NodeWhileComponent{
@Override
public boolean processWhile() throws Exception {
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@@ -105,7 +105,10 @@ public class ComponentProxy {
boolean existRetry = liteflowRetry != null;
boolean isProcess = liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_IF)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_SWITCH);
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_SWITCH)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_FOR)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_WHILE)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_BREAK);
// 如果是再Process方法上的liteflowRetry注解则默认为真实节点。
if (isProcess && existRetry) {
liteflowRetryAtomicReference.set(liteflowRetry);

View File

@@ -1,8 +1,6 @@
package com.yomahub.liteflow.enums;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import com.yomahub.liteflow.core.*;
/**
* 注解节点类型枚举
@@ -22,7 +20,15 @@ public enum AnnotationNodeTypeEnum {
/**
* 条件节点
*/
IF("条件", NodeIfComponent.class),;
IF("条件", NodeIfComponent.class),
FOR("计数循环",NodeForComponent.class),
WHILE("条件循环", NodeWhileComponent.class),
BREAK("跳出循环", NodeBreakComponent.class)
;
/**
* 描述
*/

View File

@@ -7,7 +7,11 @@ public enum ConditionTypeEnum {
TYPE_IF("if", "if"),
TYPE_PRE("pre","pre"),
TYPE_FINALLY("finally","finally")
TYPE_FINALLY("finally","finally"),
TYPE_FOR("for", "for"),
TYPE_WHILE("while", "while")
;
private String type;
private String name;

View File

@@ -4,6 +4,9 @@ public enum LiteFlowMethodEnum {
PROCESS("process"),
PROCESS_SWITCH("processSwitch"),
PROCESS_IF("processIf"),
PROCESS_FOR("processFor"),
PROCESS_WHILE("processWhile"),
PROCESS_BREAK("processBreak"),
IS_ACCESS("isAccess"),
IS_END("isEnd"),

View File

@@ -1,5 +1,8 @@
package com.yomahub.liteflow.enums;
import cn.hutool.core.util.ClassUtil;
import com.yomahub.liteflow.core.*;
/**
* 节点类型枚举
* @author Bryan.Zhang
@@ -7,22 +10,41 @@ package com.yomahub.liteflow.enums;
*/
public enum NodeTypeEnum {
COMMON("common","普通"),
COMMON("common","普通", false, NodeComponent.class),
SWITCH("switch", "选择"),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
IF("if", "条件"),
SCRIPT("script","脚本"),
SWITCH_SCRIPT("switch_script","选择脚本"),
IF("if", "条件", false, NodeIfComponent.class),
IF_SCRIPT("if_script", "条件脚本")
FOR("for","循环次数", false, NodeForComponent.class),
WHILE("while", "循环条件", false, NodeWhileComponent.class),
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
SCRIPT("script","脚本", true, ScriptComponent.class),
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class)
;
private String code;
private String name;
NodeTypeEnum(String code, String name) {
private boolean isScript;
private Class<?> mappingClazz;
NodeTypeEnum(String code, String name, boolean isScript, Class<?> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
public String getCode() {
@@ -41,6 +63,22 @@ public enum NodeTypeEnum {
this.name = name;
}
public boolean isScript() {
return isScript;
}
public void setScript(boolean script) {
isScript = script;
}
public Class<?> getMappingClazz() {
return mappingClazz;
}
public void setMappingClazz(Class<?> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
@@ -49,4 +87,24 @@ public enum NodeTypeEnum {
}
return null;
}
public static NodeTypeEnum guessTypeByClazz(Class<?> clazz){
Class<?> superClazz = clazz;
while(true){
superClazz = superClazz.getSuperclass();
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")){
break;
}
if(superClazz.equals(Object.class)){
return null;
}
}
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
}

View File

@@ -0,0 +1,25 @@
package com.yomahub.liteflow.exception;
public class NoForNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public NoForNodeException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,25 @@
package com.yomahub.liteflow.exception;
public class NoWhileNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public NoWhileNodeException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -86,14 +86,8 @@ public class FlowBus {
}
public static void addSpringScanNode(String nodeId, NodeComponent nodeComponent) {
NodeTypeEnum type = null;
if (nodeComponent instanceof NodeSwitchComponent){
type = NodeTypeEnum.SWITCH;
} else if(nodeComponent instanceof NodeIfComponent){
type = NodeTypeEnum.IF;
}else if(nodeComponent instanceof NodeComponent) {
type = NodeTypeEnum.COMMON;
}
//根据class来猜测类型
NodeTypeEnum type = NodeTypeEnum.guessTypeByClazz(nodeComponent.getClass());
if (type == null){
throw new NullNodeTypeException(StrUtil.format("node type is null for node[{}]", nodeId));
@@ -144,6 +138,48 @@ public class FlowBus {
addNode(nodeId, name, NodeTypeEnum.IF, cmpClazz, null);
}
public static void addForNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.FOR, cmpClazz, null);
}
public static void addForNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.FOR, cmpClazz, null);
}
public static void addWhileNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.WHILE, cmpClazz, null);
}
public static void addWhileNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.WHILE, cmpClazz, null);
}
public static void addBreakNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.BREAK, cmpClazz, null);
}
public static void addBreakNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.BREAK, cmpClazz, null);
}
public static void addCommonScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.SCRIPT, ScriptComponent.class, script);
}
@@ -156,6 +192,18 @@ public class FlowBus {
addNode(nodeId, name, NodeTypeEnum.IF_SCRIPT, ScriptIfComponent.class, script);
}
public static void addForScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.FOR_SCRIPT, ScriptIfComponent.class, script);
}
public static void addWhileScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.WHILE_SCRIPT, ScriptIfComponent.class, script);
}
public static void addBreakScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.BREAK_SCRIPT, ScriptIfComponent.class, script);
}
private static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz, String script) {
try {
//判断此类是否是声明式的组件,如果是声明式的组件,就用动态代理生成实例
@@ -179,7 +227,7 @@ public class FlowBus {
//以node方式配置本质上是为了适配无spring的环境如果有spring环境其实不用这么配置
//这里的逻辑是判断是否能从spring上下文中取到如果没有spring则就是new instance了
//如果是script类型的节点因为class只有一个所以也不能注册进spring上下文注册的时候需要new Instance
if (!CollectionUtil.newArrayList(NodeTypeEnum.SCRIPT, NodeTypeEnum.SWITCH_SCRIPT, NodeTypeEnum.IF_SCRIPT).contains(type)){
if (!type.isScript()){
cmpInstances = ListUtil.toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz));
}
// 去除null元素

View File

@@ -0,0 +1,75 @@
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.NoForNodeException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* 循环次数Condition
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ForCondition extends LoopCondition{
private Node forNode;
@Override
public void execute(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
if (ObjectUtil.isNull(forNode)){
String errorInfo = StrUtil.format("[{}]:no for-node found", slot.getRequestId());
throw new NoForNodeException(errorInfo);
}
//执行forCount组件
forNode.setCurrChainName(this.getCurrChainName());
forNode.execute(slotIndex);
//这里可能会有spring代理过的bean所以拿到user原始的class
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(this.forNode.getClass());
//获得循环次数
int forCount = slot.getForResult(originalForCountClass.getName());
//获得要循环的可执行对象
Executable executableItem = this.getDoExecutor();
//循环执行
for (int i = 0; i < forCount; i++) {
executableItem.execute(slotIndex);
//如果break组件不为空则去执行
if (ObjectUtil.isNotNull(breakNode)){
breakNode.setCurrChainName(this.getCurrChainName());
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
if (isBreak){
break;
}
}
}
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_FOR;
}
public Executable getDoExecutor() {
return this.getExecutableList().get(0);
}
public Node getForNode() {
return forNode;
}
public void setForNode(Node forNode) {
this.forNode = forNode;
}
}

View File

@@ -74,7 +74,7 @@ public class IfCondition extends Condition {
@Override
public ConditionTypeEnum getConditionType() {
return null;
return ConditionTypeEnum.TYPE_IF;
}
public Executable getTrueCaseExecutableItem() {

View File

@@ -0,0 +1,23 @@
package com.yomahub.liteflow.flow.element.condition;
import com.yomahub.liteflow.flow.element.Node;
/**
* 循环Condition的抽象类
* 主要继承对象有ForCondition和WhileCondition
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public abstract class LoopCondition extends Condition {
protected Node breakNode;
public Node getBreakNode() {
return breakNode;
}
public void setBreakNode(Node breakNode) {
this.breakNode = breakNode;
}
}

View File

@@ -0,0 +1,74 @@
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.NoWhileNodeException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* 循环条件Condition
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class WhileCondition extends LoopCondition{
private Node whileNode;
@Override
public void execute(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
if (ObjectUtil.isNull(whileNode)){
String errorInfo = StrUtil.format("[{}]:no while-node found", slot.getRequestId());
throw new NoWhileNodeException(errorInfo);
}
//获得要循环的可执行对象
Executable executableItem = this.getDoExecutor();
//循环执行
while(getWhileResult(slotIndex)){
executableItem.execute(slotIndex);
//如果break组件不为空则去执行
if (ObjectUtil.isNotNull(breakNode)){
breakNode.setCurrChainName(this.getCurrChainName());
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
if (isBreak){
break;
}
}
}
}
private boolean getWhileResult(Integer slotIndex) throws Exception{
Slot slot = DataBus.getSlot(slotIndex);
//执行while组件
whileNode.setCurrChainName(this.getCurrChainName());
whileNode.execute(slotIndex);
Class<?> originalWhileClass = LiteFlowProxyUtil.getUserClass(this.whileNode.getClass());
return slot.getWhileResult(originalWhileClass.getName());
}
public Executable getDoExecutor() {
return this.getExecutableList().get(0);
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_WHILE;
}
public Node getWhileNode() {
return whileNode;
}
public void setWhileNode(Node whileNode) {
this.whileNode = whileNode;
}
}

View File

@@ -6,9 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowIfCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.annotation.*;
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
@@ -61,18 +59,16 @@ public class ParserHelper {
String type = nodePropBean.getType();
String file = nodePropBean.getFile();
//先尝试自动推断类型
//clazz有值的基本都不是脚本节点
//脚本节点都必须配置type
//非脚本节点的先尝试自动推断类型
if (StrUtil.isNotBlank(clazz)) {
try {
//先尝试从继承的类型中推断
Class<?> c = Class.forName(clazz);
Object o = ReflectUtil.newInstanceIfPossible(c);
if (o instanceof NodeSwitchComponent) {
type = NodeTypeEnum.SWITCH.getCode();
}else if(o instanceof NodeIfComponent){
type = NodeTypeEnum.IF.getCode();
}else if (o instanceof NodeComponent) {
type = NodeTypeEnum.COMMON.getCode();
NodeTypeEnum nodeType = NodeTypeEnum.guessTypeByClazz(c);
if (nodeType != null){
type = nodeType.getCode();
}
//再尝试声明式组件这部分的推断
@@ -96,6 +92,27 @@ public class ParserHelper {
type = NodeTypeEnum.IF.getCode();
}
}
if (type == null) {
LiteflowForCmpDefine liteflowForCmpDefine = AnnotationUtil.getAnnotation(c, LiteflowForCmpDefine.class);
if (liteflowForCmpDefine != null) {
type = NodeTypeEnum.FOR.getCode();
}
}
if (type == null) {
LiteflowWhileCmpDefine liteflowWhileCmpDefine = AnnotationUtil.getAnnotation(c, LiteflowWhileCmpDefine.class);
if (liteflowWhileCmpDefine != null) {
type = NodeTypeEnum.WHILE.getCode();
}
}
if (type == null) {
LiteflowBreakCmpDefine liteflowBreakCmpDefine = AnnotationUtil.getAnnotation(c, LiteflowBreakCmpDefine.class);
if (liteflowBreakCmpDefine != null) {
type = NodeTypeEnum.BREAK.getCode();
}
}
} catch (Exception e) {
throw new NodeClassNotFoundException(StrUtil.format("cannot find the node[{}]", clazz));
}

View File

@@ -46,6 +46,12 @@ public class Slot{
private static final String IF_NODE_PREFIX = "_if_";
private static final String FOR_PREFIX = "_for_";
private static final String WHILE_PREFIX = "_while_";
private static final String BREAK_PREFIX = "_break_";
private static final String NODE_INPUT_PREFIX = "_input_";
private static final String NODE_OUTPUT_PREFIX = "_output_";
@@ -204,6 +210,30 @@ public class Slot{
return (boolean) metaDataMap.get(IF_NODE_PREFIX + key);
}
public void setForResult(String key, int forCount){
putMetaDataMap(FOR_PREFIX + key, forCount);
}
public int getForResult(String key){
return (int) metaDataMap.get(FOR_PREFIX + key);
}
public void setWhileResult(String key, boolean whileFlag){
putMetaDataMap(WHILE_PREFIX + key, whileFlag);
}
public boolean getWhileResult(String key){
return (boolean) metaDataMap.get(WHILE_PREFIX + key);
}
public void setBreakResult(String key, boolean breakFlag){
putMetaDataMap(BREAK_PREFIX + key, breakFlag);
}
public boolean getBreakResult(String key){
return (boolean) metaDataMap.get(BREAK_PREFIX + key);
}
public void setChainName(String chainName) {
if (!hasMetaData(CHAIN_NAME)){
this.putMetaDataMap(CHAIN_NAME, chainName);

View File

@@ -3,6 +3,8 @@ package com.yomahub.liteflow.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
/**
* logo打印器
* @author Bryan.Zhang

View File

@@ -2,13 +2,8 @@ package com.yomahub.liteflow.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowIfCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import com.yomahub.liteflow.annotation.*;
import com.yomahub.liteflow.core.*;
import com.yomahub.liteflow.core.proxy.ComponentProxy;
import com.yomahub.liteflow.exception.ComponentProxyErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
@@ -50,6 +45,9 @@ public class LiteFlowProxyUtil {
LiteflowCmpDefine liteflowCmpDefine = bean.getClass().getAnnotation(LiteflowCmpDefine.class);
LiteflowSwitchCmpDefine liteflowSwitchCmpDefine = bean.getClass().getAnnotation(LiteflowSwitchCmpDefine.class);
LiteflowIfCmpDefine liteflowIfCmpDefine = bean.getClass().getAnnotation(LiteflowIfCmpDefine.class);
LiteflowForCmpDefine liteflowForCmpDefine = bean.getClass().getAnnotation(LiteflowForCmpDefine.class);
LiteflowWhileCmpDefine liteflowWhileCmpDefine = bean.getClass().getAnnotation(LiteflowWhileCmpDefine.class);
LiteflowBreakCmpDefine liteflowBreakCmpDefine = bean.getClass().getAnnotation(LiteflowBreakCmpDefine.class);
ComponentProxy proxy;
if (ObjectUtil.isNotNull(liteflowCmpDefine)){
@@ -66,7 +64,22 @@ public class LiteFlowProxyUtil {
proxy = new ComponentProxy(nodeId, bean, NodeIfComponent.class);
return proxy.getProxyList();
}
return new ComponentProxy(nodeId, bean, NodeIfComponent.class).getProxyList();
if (ObjectUtil.isNotNull(liteflowForCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeForComponent.class);
return proxy.getProxyList();
}
if (ObjectUtil.isNotNull(liteflowWhileCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeWhileComponent.class);
return proxy.getProxyList();
}
if (ObjectUtil.isNotNull(liteflowBreakCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeBreakComponent.class);
return proxy.getProxyList();
}
return new ComponentProxy(nodeId, bean, NodeComponent.class).getProxyList();
}catch (LiteFlowException liteFlowException){
throw liteFlowException;
}

View File

@@ -71,7 +71,7 @@ public class GroovyScriptExecutor implements ScriptExecutor {
Slot slot = DataBus.getSlot(slotIndex);
bindings.put("requestData", slot.getRequestData());
//如果有隐流程,则放入隐式流程的流程参数
//如果有隐流程,则放入隐式流程的流程参数
Object subRequestData = slot.getChainReqData(currChainName);
if (ObjectUtil.isNotNull(subRequestData)){
bindings.put("subRequestData", subRequestData);