From d60d4ae8581a073170440106c09c156f67c24433 Mon Sep 17 00:00:00 2001 From: luoyi <972849752@qq.com> Date: Fri, 23 Feb 2024 15:29:51 +0800 Subject: [PATCH] =?UTF-8?q?enhancement=20#I91AUT=20=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E5=9C=A8=E5=88=87=E9=9D=A2=E5=87=BA=E8=AE=BE=E7=BD=AE=20isCont?= =?UTF-8?q?inueOnError=20=E5=8F=82=E6=95=B0=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 22 ++++--- .../yomahub/liteflow/flow/element/Node.java | 22 ++++++- .../CustomAOPELOperatorSpringbootTest.java | 58 ++++++++++++++++ .../GlobalAOPELOperatorSpringbootTest.java | 66 +++++++++++++++++++ .../test/aop/aspect/CmpOperatorAspect.java | 34 ++++++++++ .../test/aop/aspect/CustomOperatorAspect.java | 29 ++++++++ .../src/test/resources/aop/flow.el.xml | 9 +++ 7 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/CustomAOPELOperatorSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/GlobalAOPELOperatorSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CmpOperatorAspect.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CustomOperatorAspect.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 9bf9feaca..02140db44 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/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 com.alibaba.ttl.TransmittableThreadLocal; 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.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.enums.NodeTypeEnum; +import com.yomahub.liteflow.flow.executor.NodeExecutor; import com.yomahub.liteflow.log.LFLog; 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.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.util.Date; @@ -34,6 +35,7 @@ import java.util.Date; * 普通组件抽象类 * * @author Bryan.Zhang + * @author luo yi */ public abstract class NodeComponent{ @@ -236,6 +238,10 @@ public abstract class NodeComponent{ this.refNodeTL.get().setIsEnd(isEnd); } + public void setIsContinueOnError(boolean isContinueOnError) { + this.refNodeTL.get().setIsContinueOnErrorResult(isContinueOnError); + } + public Integer getSlotIndex() { return this.refNodeTL.get().getSlotIndex(); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java index e3c137d7f..683bfd8dd 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java +++ b/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 isEndTL = new TransmittableThreadLocal<>(); + // isContinueOnError 结果 + private TransmittableThreadLocal isContinueOnErrorResult = new TransmittableThreadLocal<>(); + public Node() { } @@ -168,7 +171,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ throw new ChainEndException(errorInfo); } // 如果组件覆盖了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); LOG.error(errorMsg); } @@ -185,6 +188,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ removeIsEnd(); removeLoopIndex(); removeAccessResult(); + removeIsContinueOnErrorResult(); } } @@ -262,7 +266,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ public boolean getAccessResult() { Boolean result = this.accessResult.get(); - return result == null ? false : result; + return result != null && result; } public void setAccessResult(boolean accessResult) { @@ -273,6 +277,19 @@ public class Node implements Executable, Cloneable, Rollbackable{ 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) { this.loopIndexTL.set(index); } @@ -342,6 +359,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ node.accessResult = new TransmittableThreadLocal<>(); node.slotIndexTL = new TransmittableThreadLocal<>(); node.isEndTL = new TransmittableThreadLocal<>(); + node.isContinueOnErrorResult = new TransmittableThreadLocal<>(); return node; } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/CustomAOPELOperatorSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/CustomAOPELOperatorSpringbootTest.java new file mode 100644 index 000000000..d6ebc9587 --- /dev/null +++ b/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")); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/GlobalAOPELOperatorSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/GlobalAOPELOperatorSpringbootTest.java new file mode 100644 index 000000000..8a3dd6cf1 --- /dev/null +++ b/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; + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CmpOperatorAspect.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CmpOperatorAspect.java new file mode 100644 index 000000000..783d92ce9 --- /dev/null +++ b/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()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CustomOperatorAspect.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/aop/aspect/CustomOperatorAspect.java new file mode 100644 index 000000000..964546da2 --- /dev/null +++ b/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; + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/aop/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/aop/flow.el.xml index 8b89951a2..4937d9e5b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/aop/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/aop/flow.el.xml @@ -11,4 +11,13 @@ THEN(a,b,c,f); + + + THEN(a, f, b, c); + + + + THEN(a, b, WHEN(f, e), c); + + \ No newline at end of file