Jelajahi Sumber

!274 提供在切面处设置 isContinueOnError 参数方法
Merge pull request !274 from luoyi/issues/I91AUT

铂赛东 1 tahun lalu
induk
melakukan
6ce97e6b0d

+ 14 - 8
liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java

@@ -12,21 +12,22 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.ttl.TransmittableThreadLocal;
 import com.alibaba.ttl.TransmittableThreadLocal;
 import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
 import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
+import com.yomahub.liteflow.enums.CmpStepTypeEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
 import com.yomahub.liteflow.flow.LiteflowResponse;
 import com.yomahub.liteflow.flow.LiteflowResponse;
 import com.yomahub.liteflow.flow.element.Node;
 import com.yomahub.liteflow.flow.element.Node;
-import com.yomahub.liteflow.flow.executor.NodeExecutor;
+import com.yomahub.liteflow.flow.entity.CmpStep;
 import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
 import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
-import com.yomahub.liteflow.enums.NodeTypeEnum;
+import com.yomahub.liteflow.flow.executor.NodeExecutor;
 import com.yomahub.liteflow.log.LFLog;
 import com.yomahub.liteflow.log.LFLog;
 import com.yomahub.liteflow.log.LFLoggerManager;
 import com.yomahub.liteflow.log.LFLoggerManager;
-import com.yomahub.liteflow.spi.holder.CmpAroundAspectHolder;
-import com.yomahub.liteflow.util.JsonUtil;
-import com.yomahub.liteflow.flow.entity.CmpStep;
-import com.yomahub.liteflow.enums.CmpStepTypeEnum;
-import com.yomahub.liteflow.slot.DataBus;
-import com.yomahub.liteflow.slot.Slot;
 import com.yomahub.liteflow.monitor.CompStatistics;
 import com.yomahub.liteflow.monitor.CompStatistics;
 import com.yomahub.liteflow.monitor.MonitorBus;
 import com.yomahub.liteflow.monitor.MonitorBus;
+import com.yomahub.liteflow.slot.DataBus;
+import com.yomahub.liteflow.slot.Slot;
+import com.yomahub.liteflow.spi.holder.CmpAroundAspectHolder;
+import com.yomahub.liteflow.util.JsonUtil;
+
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 import java.util.Date;
 import java.util.Date;
 
 
@@ -34,6 +35,7 @@ import java.util.Date;
  * 普通组件抽象类
  * 普通组件抽象类
  *
  *
  * @author Bryan.Zhang
  * @author Bryan.Zhang
+ * @author luo yi
  */
  */
 public abstract class NodeComponent{
 public abstract class NodeComponent{
 
 
@@ -236,6 +238,10 @@ public abstract class NodeComponent{
 		this.refNodeTL.get().setIsEnd(isEnd);
 		this.refNodeTL.get().setIsEnd(isEnd);
 	}
 	}
 
 
+	public void setIsContinueOnError(boolean isContinueOnError) {
+		this.refNodeTL.get().setIsContinueOnErrorResult(isContinueOnError);
+	}
+
 	public Integer getSlotIndex() {
 	public Integer getSlotIndex() {
 		return this.refNodeTL.get().getSlotIndex();
 		return this.refNodeTL.get().getSlotIndex();
 	}
 	}

+ 20 - 2
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java

@@ -71,6 +71,9 @@ public class Node implements Executable, Cloneable, Rollbackable{
 	// 是否结束整个流程,这个只对串行流程有效,并行流程无效
 	// 是否结束整个流程,这个只对串行流程有效,并行流程无效
 	private TransmittableThreadLocal<Boolean> isEndTL = new TransmittableThreadLocal<>();
 	private TransmittableThreadLocal<Boolean> isEndTL = new TransmittableThreadLocal<>();
 
 
+	// isContinueOnError 结果
+	private TransmittableThreadLocal<Boolean> isContinueOnErrorResult =  new TransmittableThreadLocal<>();
+
 	public Node() {
 	public Node() {
 
 
 	}
 	}
@@ -168,7 +171,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 				throw new ChainEndException(errorInfo);
 				throw new ChainEndException(errorInfo);
 			}
 			}
 			// 如果组件覆盖了isContinueOnError方法,返回为true,那即便出了异常,也会继续流程
 			// 如果组件覆盖了isContinueOnError方法,返回为true,那即便出了异常,也会继续流程
-			else if (instance.isContinueOnError()) {
+			else if (getIsContinueOnErrorResult() || instance.isContinueOnError()) {
 				String errorMsg = StrUtil.format("component[{}] cause error,but flow is still go on", id);
 				String errorMsg = StrUtil.format("component[{}] cause error,but flow is still go on", id);
 				LOG.error(errorMsg);
 				LOG.error(errorMsg);
 			}
 			}
@@ -185,6 +188,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 			removeIsEnd();
 			removeIsEnd();
 			removeLoopIndex();
 			removeLoopIndex();
 			removeAccessResult();
 			removeAccessResult();
+			removeIsContinueOnErrorResult();
 		}
 		}
 	}
 	}
 
 
@@ -262,7 +266,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 
 
 	public boolean getAccessResult() {
 	public boolean getAccessResult() {
 		Boolean result = this.accessResult.get();
 		Boolean result = this.accessResult.get();
-		return result == null ? false : result;
+		return result != null && result;
 	}
 	}
 
 
 	public void setAccessResult(boolean accessResult) {
 	public void setAccessResult(boolean accessResult) {
@@ -273,6 +277,19 @@ public class Node implements Executable, Cloneable, Rollbackable{
 		this.accessResult.remove();
 		this.accessResult.remove();
 	}
 	}
 
 
+	public boolean getIsContinueOnErrorResult() {
+		Boolean result = this.isContinueOnErrorResult.get();
+		return result != null && result;
+	}
+
+	public void setIsContinueOnErrorResult(boolean accessResult) {
+		this.isContinueOnErrorResult.set(accessResult);
+	}
+
+	public void removeIsContinueOnErrorResult() {
+		this.isContinueOnErrorResult.remove();
+	}
+
 	public void setLoopIndex(int index) {
 	public void setLoopIndex(int index) {
 		this.loopIndexTL.set(index);
 		this.loopIndexTL.set(index);
 	}
 	}
@@ -342,6 +359,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 		node.accessResult = new TransmittableThreadLocal<>();
 		node.accessResult = new TransmittableThreadLocal<>();
 		node.slotIndexTL = new TransmittableThreadLocal<>();
 		node.slotIndexTL = new TransmittableThreadLocal<>();
 		node.isEndTL = new TransmittableThreadLocal<>();
 		node.isEndTL = new TransmittableThreadLocal<>();
+		node.isContinueOnErrorResult = new TransmittableThreadLocal<>();
 		return node;
 		return node;
 	}
 	}
 }
 }

+ 58 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/CustomAOPELOperatorSpringbootTest.java

@@ -0,0 +1,58 @@
+package com.yomahub.liteflow.test.aop;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import com.yomahub.liteflow.test.aop.aspect.CustomOperatorAspect;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+
+import javax.annotation.Resource;
+
+/**
+ * 切面场景单元测试
+ *
+ * @author luo yi
+ */
+@TestPropertySource(value = "classpath:/aop/application.properties")
+@SpringBootTest(classes = CustomAOPELOperatorSpringbootTest.class)
+@EnableAutoConfiguration
+@Import(CustomOperatorAspect.class)
+@ComponentScan({ "com.yomahub.liteflow.test.aop.cmp1", "com.yomahub.liteflow.test.aop.cmp2" })
+public class CustomAOPELOperatorSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 设置 isContinueOnError 测试全局 AOP,串行场景
+	@Test
+	public void testGlobalAopErrorWithContinueS() {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "it's a request");
+		DefaultContext context = response.getFirstContextBean();
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertEquals("before_after", context.getData("a"));
+		Assertions.assertEquals("before_after", context.getData("b"));
+		Assertions.assertEquals("before_after", context.getData("c"));
+		Assertions.assertEquals("before", context.getData("f"));
+	}
+
+	// 设置 isContinueOnError 测试全局 AOP,并行场景
+	@Test
+	public void testGlobalAopErrorWithContinueP() {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "it's a request");
+		DefaultContext context = response.getFirstContextBean();
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertEquals("before_after", context.getData("a"));
+		Assertions.assertEquals("before_after", context.getData("b"));
+		Assertions.assertEquals("before_after", context.getData("c"));
+		Assertions.assertEquals("before_after", context.getData("e"));
+		Assertions.assertEquals("before", context.getData("f"));
+	}
+
+}

+ 66 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/GlobalAOPELOperatorSpringbootTest.java

@@ -0,0 +1,66 @@
+package com.yomahub.liteflow.test.aop;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.spring.ComponentScanner;
+import com.yomahub.liteflow.test.BaseTest;
+import com.yomahub.liteflow.test.aop.aspect.CmpOperatorAspect;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.TestPropertySource;
+
+import javax.annotation.Resource;
+
+/**
+ * 切面场景单元测试
+ *
+ * @author luo yi
+ */
+@TestPropertySource(value = "classpath:/aop/application.properties")
+@SpringBootTest(classes = GlobalAOPELOperatorSpringbootTest.class)
+@EnableAutoConfiguration
+@Import(CmpOperatorAspect.class)
+@ComponentScan({ "com.yomahub.liteflow.test.aop.cmp1", "com.yomahub.liteflow.test.aop.cmp2" })
+public class GlobalAOPELOperatorSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 设置 isContinueOnError 测试全局 AOP,串行场景
+	@Test
+	public void testGlobalAopErrorWithContinueS() {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "it's a request");
+		DefaultContext context = response.getFirstContextBean();
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertEquals("before_after", context.getData("a"));
+		Assertions.assertEquals("before_after", context.getData("b"));
+		Assertions.assertEquals("before_after", context.getData("c"));
+		Assertions.assertEquals("test error", context.getData("f_error"));
+	}
+
+	// 设置 isContinueOnError 测试全局 AOP,并行场景
+	@Test
+	public void testGlobalAopErrorWithContinueP() {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "it's a request");
+		DefaultContext context = response.getFirstContextBean();
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertEquals("before_after", context.getData("a"));
+		Assertions.assertEquals("before_after", context.getData("b"));
+		Assertions.assertEquals("before_after", context.getData("c"));
+		Assertions.assertEquals("before_after", context.getData("e"));
+		Assertions.assertEquals("test error", context.getData("f_error"));
+	}
+
+	@AfterAll
+	public static void cleanScanCache() {
+		BaseTest.cleanScanCache();
+		ComponentScanner.cmpAroundAspect = null;
+	}
+
+}

+ 34 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CmpOperatorAspect.java

@@ -0,0 +1,34 @@
+package com.yomahub.liteflow.test.aop.aspect;
+
+import cn.hutool.core.util.StrUtil;
+import com.yomahub.liteflow.aop.ICmpAroundAspect;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+
+public class CmpOperatorAspect implements ICmpAroundAspect {
+
+	@Override
+	public void beforeProcess(NodeComponent cmp) {
+		DefaultContext context = cmp.getFirstContextBean();
+		context.setData(cmp.getNodeId(), "before");
+	}
+
+	@Override
+	public void afterProcess(NodeComponent cmp) {
+		DefaultContext context = cmp.getFirstContextBean();
+		context.setData(cmp.getNodeId(), StrUtil.format("{}_{}", context.getData(cmp.getNodeId()), "after"));
+	}
+
+	@Override
+	public void onSuccess(NodeComponent cmp) {
+
+	}
+
+	@Override
+	public void onError(NodeComponent cmp, Exception e) {
+		cmp.setIsContinueOnError(true);
+		DefaultContext context = cmp.getFirstContextBean();
+		context.setData(cmp.getNodeId() + "_error", e.getMessage());
+	}
+
+}

+ 29 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CustomOperatorAspect.java

@@ -0,0 +1,29 @@
+package com.yomahub.liteflow.test.aop.aspect;
+
+import cn.hutool.core.util.StrUtil;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+@Aspect
+public class CustomOperatorAspect {
+
+	@Pointcut("execution(* com.yomahub.liteflow.test.aop.*.*.process())")
+	public void cut() {
+	}
+
+	@Around("cut()")
+	public Object around(ProceedingJoinPoint jp) throws Throwable {
+		NodeComponent cmp = (NodeComponent) jp.getThis();
+		DefaultContext context = cmp.getFirstContextBean();
+		cmp.setIsContinueOnError(true);
+		context.setData(cmp.getNodeId(), "before");
+		Object returnObj = jp.proceed();
+		context.setData(cmp.getNodeId(), StrUtil.format("{}_{}", context.getData(cmp.getNodeId()), "after"));
+		return returnObj;
+	}
+
+}

+ 9 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/aop/flow.el.xml

@@ -11,4 +11,13 @@
     <chain name="chain3">
     <chain name="chain3">
         THEN(a,b,c,f);
         THEN(a,b,c,f);
     </chain>
     </chain>
+
+    <chain name="chain4">
+        THEN(a, f, b, c);
+    </chain>
+
+    <chain name="chain5">
+        THEN(a, b, WHEN(f, e), c);
+    </chain>
+
 </flow>
 </flow>