This is an automated email from the ASF dual-hosted git repository.

jianbin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new d859fef4eb optimize: Replace @LocalTCC with @SagaTransactional in the 
saga annotation pattern (#7443)
d859fef4eb is described below

commit d859fef4eb8be9e814e69de63538cbd0bed8165f
Author: Eric Wang <[email protected]>
AuthorDate: Wed Sep 3 09:48:58 2025 +0800

    optimize: Replace @LocalTCC with @SagaTransactional in the saga annotation 
pattern (#7443)
---
 changes/en-us/2.x.md                               |   2 +
 changes/zh-cn/2.x.md                               |   2 +
 .../remoting/parser/LocalTCCRemotingParser.java    |   2 +
 .../saga/rm/api/CompensationBusinessAction.java    |  29 ++
 .../seata/saga/rm/api/SagaTransactional.java       | 103 ++++++
 .../parser/SagaTransactionalRemotingParser.java    | 155 +++++++++
 ...eata.integration.tx.api.remoting.RemotingParser |  17 +
 .../seata/saga/rm/api/SagaTransactionalTest.java   | 233 +++++++++++++
 .../SagaTransactionalRemotingParserTest.java       | 256 +++++++++++++++
 .../java/org/apache/seata/rm/tcc/api/LocalTCC.java |  38 ++-
 .../seata/rm/tcc/api/TwoPhaseBusinessAction.java   |   9 +-
 .../remoting/parser/LocalTCCRemotingParser.java    |  85 ++++-
 .../parser/LocalTCCRemotingParserTest.java         | 215 ++++++++++--
 .../saga/annotation/AnnotationConflictTest.java    | 364 +++++++++++++++++++++
 .../saga/annotation/DualParserIntegrationTest.java | 308 +++++++++++++++++
 .../SagaTransactionalAnnotationAction.java         |  50 +++
 .../SagaTransactionalAnnotationActionImpl.java     |  57 ++++
 17 files changed, 1872 insertions(+), 53 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index c1b6719f1a..0058d3c60d 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -49,6 +49,7 @@ Add changes here for all PR submitted to the 2.x branch.
 - [[#7608](https://github.com/seata/seata/pull/7608)]  modify the parameter 
name in refreshToken method
 - [[#7603](https://github.com/seata/seata/pull/7603)] upgrade Apache Tomcat 
dependency from 9.0.106 to 9.0.108
 - [[#7614](https://github.com/seata/seata/pull/7614)] update README.md
+- [[#7443](https://github.com/seata/seata/pull/7443)] Replace @LocalTCC with 
@SagaTransactional in the saga annotation pattern
 
 ### security:
 
@@ -88,6 +89,7 @@ Thanks to these contributors for their code commits. Please 
report an unintended
 - [funky-eyes](https://github.com/funky-eyes)
 - [keepConcentration](https://github.com/keepConcentration)
 - [sunheyi6](https://github.com/sunheyi6)
+- [WangzJi](https://github.com/WangzJi)
 
 
 Also, we receive many valuable issues, questions and advices from our 
community. Thanks for you all.
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 49db4360c8..45ba533167 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -48,6 +48,7 @@
 - [[#7608](https://github.com/seata/seata/pull/7608)]  修改refreshToken方法中的参数名称
 - [[#7603](https://github.com/seata/seata/pull/7603)] 将Apache 
Tomcat依赖项从9.0.106升级到9.0.108 
 - [[#7614](https://github.com/seata/seata/pull/7614)] 更新 README.md
+- [[#7443](https://github.com/seata/seata/pull/7443)] 
将saga注释模式中的@LocalTCC替换为@SagaTransactional
 
 ### security:
 
@@ -88,6 +89,7 @@
 - [funky-eyes](https://github.com/funky-eyes)
 - [keepConcentration](https://github.com/keepConcentration)
 - [sunheyi6](https://github.com/sunheyi6)
+- [WangzJi](https://github.com/WangzJi)
 
 
 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
diff --git 
a/compatible/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
 
b/compatible/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
index ae27f82f4c..65eb5264ef 100644
--- 
a/compatible/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
+++ 
b/compatible/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
@@ -27,6 +27,8 @@ import java.util.Set;
 
 /**
  * The type Local tcc remoting parser.
+ * Compatible module maintains backward compatibility with Seata versions 
prior to 2.1.
+ * Only supports @LocalTCC annotation.
  */
 @Deprecated
 public class LocalTCCRemotingParser extends 
org.apache.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser {
diff --git 
a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java
 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java
index 1128154234..8c17eb1d2c 100644
--- 
a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java
+++ 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java
@@ -27,6 +27,35 @@ import java.lang.annotation.Target;
 /**
  * Saga annotation.
  * Define a saga interface, which added on the commit method, if occurs 
rollback, compensation will be called.
+ *
+ * When using this annotation for local (non-remote) services, you should also 
add @SagaTransactional
+ * annotation on the interface or implementation class to enable proper proxy 
enhancement.
+ * This avoids the need to use @LocalTCC annotation in Saga scenarios, which 
can be confusing.
+ *
+ * Recommended Usage Pattern:
+ *
+ * @SagaTransactional  // Use this instead of @LocalTCC for Saga scenarios
+ * public interface PaymentSagaService {
+ *
+ *     @CompensationBusinessAction(compensationMethod = "compensatePayment")
+ *     boolean processPayment(BusinessActionContext context, String orderId, 
double amount);
+ *
+ *     boolean compensatePayment(BusinessActionContext context);
+ * }
+ *
+ * Why Use @SagaTransactional with Saga:
+ * - Semantic clarity: @SagaTransactional indicates general transaction 
participation
+ * - Avoids confusion: @LocalTCC specifically implies TCC mode semantics
+ * - Future compatibility: Works with multiple transaction modes
+ * - Better maintainability: Clear intent for Saga compensation patterns
+ *
+ * Legacy Support:
+ * While @LocalTCC still works with Saga scenarios for backward compatibility,
+ * @SagaTransactional is the recommended approach for new implementations.
+ *
+ * @see SagaTransactional Recommended annotation for Saga scenarios
+ * @see org.apache.seata.rm.tcc.api.LocalTCC Legacy annotation (still 
supported but not recommended for Saga)
+ * @see org.apache.seata.rm.tcc.api.BusinessActionContext Context parameter 
type
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD})
diff --git 
a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/SagaTransactional.java
 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/SagaTransactional.java
new file mode 100644
index 0000000000..c301fd5aa1
--- /dev/null
+++ 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/SagaTransactional.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.rm.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * SagaTransactional annotation for marking Saga transaction participant beans.
+ *
+ * This annotation is specifically designed for Saga transaction scenarios, 
providing
+ * clearer semantic meaning compared to @LocalTCC when used in Saga 
compensation patterns.
+ * It enables proper proxy enhancement and integration with Saga transaction 
management.
+ *
+ * Purpose:
+ * - Marks services that participate in Saga transactions
+ * - Enables automatic proxy creation for compensation action support
+ * - Provides clear semantic separation from TCC-specific functionality
+ * - Supports both interface and implementation class annotation
+ *
+ * Key Benefits over @LocalTCC in Saga scenarios:
+ * 1. Semantic Clarity: "Saga" clearly indicates compensation-based 
transaction mode
+ * 2. Purpose-built: Designed specifically for Saga patterns, not retrofitted 
from TCC
+ * 3. Future-proof: Can evolve independently to support Saga-specific features
+ * 4. Maintainability: Makes codebase intentions clearer for developers
+ *
+ * Typical Usage Pattern:
+ *
+ * @SagaTransactional
+ * public interface OrderSagaService {
+ *
+ *     @CompensationBusinessAction(
+ *         name = "createOrder",
+ *         compensationMethod = "cancelOrder"
+ *     )
+ *     OrderResult createOrder(BusinessActionContext context, 
CreateOrderRequest request);
+ *
+ *     boolean cancelOrder(BusinessActionContext context);
+ * }
+ *
+ * Advanced Usage with Multiple Actions:
+ *
+ * @SagaTransactional
+ * public interface PaymentSagaService {
+ *
+ *     @CompensationBusinessAction(name = "reserveAmount", compensationMethod 
= "releaseAmount")
+ *     boolean reserveAmount(BusinessActionContext context, String accountId, 
BigDecimal amount);
+ *
+ *     @CompensationBusinessAction(name = "deductAmount", compensationMethod = 
"refundAmount")
+ *     boolean deductAmount(BusinessActionContext context, String accountId, 
BigDecimal amount);
+ *
+ *     boolean releaseAmount(BusinessActionContext context);
+ *     boolean refundAmount(BusinessActionContext context);
+ * }
+ *
+ * Annotation Placement:
+ * - Interface level: Recommended for service contracts
+ * - Implementation class level: Alternative for concrete classes
+ * - Inheritance: Annotations are inherited from parent classes/interfaces
+ *
+ * Integration with Spring Framework:
+ * This annotation works seamlessly with Spring's component scanning and proxy 
mechanisms.
+ * Services annotated with @SagaTransactional will be automatically detected 
and enhanced
+ * by Seata's runtime infrastructure.
+ *
+ * Backward Compatibility:
+ * - Existing @LocalTCC annotations continue to work unchanged
+ * - Both annotations can coexist in the same application
+ * - No migration is required for existing TCC implementations
+ * - New Saga implementations should prefer @SagaTransactional
+ *
+ * Performance Characteristics:
+ * - Zero runtime overhead compared to @LocalTCC
+ * - Efficient annotation scanning with caching
+ * - Optimized for high-throughput Saga scenarios
+ *
+ * @see org.apache.seata.saga.rm.api.CompensationBusinessAction Primary 
annotation for defining compensation actions
+ * @see org.apache.seata.rm.tcc.api.LocalTCC Legacy TCC-specific annotation 
(still supported)
+ * @see org.apache.seata.rm.tcc.api.BusinessActionContext Context parameter 
passed to compensation methods
+ * @see 
org.apache.seata.saga.rm.remoting.parser.SagaTransactionalRemotingParser Parser 
that handles this annotation
+ * @since 2.5.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface SagaTransactional {}
diff --git 
a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParser.java
 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParser.java
new file mode 100644
index 0000000000..c261a8141a
--- /dev/null
+++ 
b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParser.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.rm.remoting.parser;
+
+import org.apache.seata.common.exception.FrameworkException;
+import org.apache.seata.common.util.ReflectionUtil;
+import org.apache.seata.integration.tx.api.remoting.Protocols;
+import org.apache.seata.integration.tx.api.remoting.RemotingDesc;
+import 
org.apache.seata.integration.tx.api.remoting.parser.AbstractedRemotingParser;
+import org.apache.seata.saga.rm.api.SagaTransactional;
+
+import java.util.Set;
+
+/**
+ * Remoting parser for Saga transaction participant beans with 
@SagaTransactional annotation
+ *
+ * This parser is specifically designed for Saga transaction mode and handles 
beans annotated
+ * with @SagaTransactional annotation. It provides proper service detection 
and proxy enhancement
+ * for Saga compensation scenarios.
+ *
+ * Key Features:
+ * - Dedicated support for @SagaTransactional annotation
+ * - Optimized for Saga compensation patterns
+ * - Clear semantic separation from TCC mode
+ * - Proper integration with CompensationBusinessAction
+ *
+ * Usage Pattern:
+ * @SagaTransactional
+ * public interface PaymentSagaService {
+ *     @CompensationBusinessAction(compensationMethod = "compensatePayment")
+ *     boolean processPayment(BusinessActionContext context, String orderId, 
double amount);
+ *
+ *     boolean compensatePayment(BusinessActionContext context);
+ * }
+ *
+ * Detection Priority:
+ * 1. Implementation class annotations (higher priority)
+ * 2. Interface annotations (fallback)
+ *
+ * Performance Considerations:
+ * - Annotation detection is cached appropriately for high-throughput scenarios
+ * - Reflection-based scanning is optimized for typical usage patterns
+ *
+ * @see SagaTransactional The annotation this parser handles
+ * @see org.apache.seata.saga.rm.api.CompensationBusinessAction Commonly used 
with @SagaTransactional
+ * @see 
org.apache.seata.integration.tx.api.remoting.parser.AbstractedRemotingParser 
Base class
+ * @since 2.5.0
+ */
+public class SagaTransactionalRemotingParser extends AbstractedRemotingParser {
+
+    @Override
+    public boolean isReference(Object bean, String beanName) {
+        return isSagaTransactional(bean);
+    }
+
+    @Override
+    public boolean isService(Object bean, String beanName) {
+        return isSagaTransactional(bean);
+    }
+
+    @Override
+    public boolean isService(Class<?> beanClass) throws FrameworkException {
+        return isSagaTransactional(beanClass);
+    }
+
+    @Override
+    public RemotingDesc getServiceDesc(Object bean, String beanName) throws 
FrameworkException {
+        if (!this.isRemoting(bean, beanName)) {
+            return null;
+        }
+        RemotingDesc remotingDesc = new RemotingDesc();
+        remotingDesc.setReference(this.isReference(bean, beanName));
+        remotingDesc.setService(this.isService(bean, beanName));
+        remotingDesc.setProtocol(Protocols.IN_JVM);
+        Class<?> classType = bean.getClass();
+
+        // First priority: check if @SagaTransactional is present on the 
implementation class itself
+        // Implementation class annotations take precedence over interface 
annotations
+        if (hasSagaTransactionalAnnotation(classType)) {
+            remotingDesc.setServiceClass(classType);
+            remotingDesc.setServiceClassName(classType.getName());
+            remotingDesc.setTargetBean(bean);
+            return remotingDesc;
+        }
+
+        // Second priority: check if @SagaTransactional is present on any 
implemented interfaces
+        // Fall back to interface annotations if no implementation class 
annotations found
+        Set<Class<?>> interfaceClasses = 
ReflectionUtil.getInterfaces(classType);
+        for (Class<?> interClass : interfaceClasses) {
+            if (hasSagaTransactionalAnnotation(interClass)) {
+                remotingDesc.setServiceClassName(interClass.getName());
+                remotingDesc.setServiceClass(interClass);
+                remotingDesc.setTargetBean(bean);
+                return remotingDesc;
+            }
+        }
+        throw new FrameworkException("Couldn't parse any Remoting info for 
SagaTransactional bean");
+    }
+
+    @Override
+    public short getProtocol() {
+        return Protocols.IN_JVM;
+    }
+
+    /**
+     * Check if the given bean is annotated with @SagaTransactional annotation
+     *
+     * @param bean the bean to check
+     * @return true if the bean or its interfaces have @SagaTransactional 
annotation
+     */
+    private boolean isSagaTransactional(Object bean) {
+        return isSagaTransactional(bean.getClass());
+    }
+
+    /**
+     * Check if the given class or its interfaces are annotated with 
@SagaTransactional annotation
+     *
+     * @param classType the class type to check
+     * @return true if the class has @SagaTransactional annotation
+     */
+    private boolean isSagaTransactional(Class<?> classType) {
+        // Check the class itself first for better performance
+        if (hasSagaTransactionalAnnotation(classType)) {
+            return true;
+        }
+
+        // Check all interfaces
+        Set<Class<?>> interfaceClasses = 
ReflectionUtil.getInterfaces(classType);
+        return 
interfaceClasses.stream().anyMatch(this::hasSagaTransactionalAnnotation);
+    }
+
+    /**
+     * Check if a class has @SagaTransactional annotation
+     *
+     * @param clazz the class to check
+     * @return true if the class has @SagaTransactional annotation
+     */
+    private boolean hasSagaTransactionalAnnotation(Class<?> clazz) {
+        return clazz.isAnnotationPresent(SagaTransactional.class);
+    }
+}
diff --git 
a/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.remoting.RemotingParser
 
b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.remoting.RemotingParser
new file mode 100644
index 0000000000..5f47ddbcbe
--- /dev/null
+++ 
b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.remoting.RemotingParser
@@ -0,0 +1,17 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+org.apache.seata.saga.rm.remoting.parser.SagaTransactionalRemotingParser
\ No newline at end of file
diff --git 
a/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/api/SagaTransactionalTest.java
 
b/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/api/SagaTransactionalTest.java
new file mode 100644
index 0000000000..eff8e3c2e0
--- /dev/null
+++ 
b/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/api/SagaTransactionalTest.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.rm.api;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for @SagaTransactional annotation
+ *
+ * This test suite validates the basic behavior and properties of the 
@SagaTransactional annotation:
+ *
+ * Key test areas:
+ * 1. Annotation presence detection at runtime
+ * 2. Annotation inheritance behavior with @Inherited
+ * 3. Interface vs implementation annotation handling
+ * 4. Reflection-based annotation access
+ * 5. Service instantiation with annotated classes
+ * 6. Complex inheritance hierarchies
+ *
+ * These tests ensure that @SagaTransactional behaves correctly in runtime 
environments
+ * and maintains compatibility with transaction processing frameworks.
+ */
+public class SagaTransactionalTest {
+
+    // Test classes for annotation testing
+    @SagaTransactional
+    public static class AccountService {
+        public boolean debit(String accountId, double amount) {
+            return amount > 0;
+        }
+
+        public boolean credit(String accountId, double amount) {
+            return amount > 0;
+        }
+    }
+
+    @SagaTransactional
+    public interface PaymentService {
+        boolean processPayment(String orderId, double amount);
+
+        boolean refundPayment(String orderId, double amount);
+    }
+
+    public static class PaymentServiceImpl implements PaymentService {
+        @Override
+        public boolean processPayment(String orderId, double amount) {
+            return orderId != null && amount > 0;
+        }
+
+        @Override
+        public boolean refundPayment(String orderId, double amount) {
+            return orderId != null && amount > 0;
+        }
+    }
+
+    public static class NoAnnotationService {
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @Test
+    public void testSagaTransactionalAnnotationExists() {
+        // Verify the annotation exists and can be used
+        
assertTrue(AccountService.class.isAnnotationPresent(SagaTransactional.class));
+        
assertTrue(PaymentService.class.isAnnotationPresent(SagaTransactional.class));
+        
assertFalse(NoAnnotationService.class.isAnnotationPresent(SagaTransactional.class));
+    }
+
+    @Test
+    public void testSagaTransactionalAnnotationProperties() throws Exception {
+        SagaTransactional annotation = 
AccountService.class.getAnnotation(SagaTransactional.class);
+        assertNotNull(annotation);
+
+        // Verify annotation type
+        assertEquals(SagaTransactional.class, annotation.annotationType());
+    }
+
+    @Test
+    public void testAnnotationInheritance() {
+        // Test that @Inherited works properly
+        class InheritedService extends AccountService {
+            public boolean additionalOperation() {
+                return true;
+            }
+        }
+
+        InheritedService inheritedService = new InheritedService();
+
+        // Should inherit the @SagaTransactional annotation
+        
assertTrue(inheritedService.getClass().isAnnotationPresent(SagaTransactional.class));
+    }
+
+    @Test
+    public void testAnnotationOnInterface() {
+        PaymentServiceImpl implementation = new PaymentServiceImpl();
+
+        // Check that the interface has the annotation
+        
assertTrue(PaymentService.class.isAnnotationPresent(SagaTransactional.class));
+
+        // Check that implementation can access interface annotation
+        for (Class<?> interfaceClass : 
implementation.getClass().getInterfaces()) {
+            if (interfaceClass == PaymentService.class) {
+                
assertTrue(interfaceClass.isAnnotationPresent(SagaTransactional.class));
+            }
+        }
+    }
+
+    @Test
+    public void testAnnotationRetentionPolicy() throws Exception {
+        // Verify annotation is retained at runtime
+        SagaTransactional annotation = 
AccountService.class.getAnnotation(SagaTransactional.class);
+        assertNotNull(annotation);
+
+        // Verify it's available through reflection
+        boolean foundAnnotation = false;
+        for (java.lang.annotation.Annotation ann : 
AccountService.class.getAnnotations()) {
+            if (ann instanceof SagaTransactional) {
+                foundAnnotation = true;
+                break;
+            }
+        }
+        assertTrue(foundAnnotation);
+    }
+
+    @Test
+    public void testAnnotationTarget() {
+        // Verify annotation can be applied to types (classes and interfaces)
+        
assertTrue(AccountService.class.isAnnotationPresent(SagaTransactional.class));
+        
assertTrue(PaymentService.class.isAnnotationPresent(SagaTransactional.class));
+    }
+
+    @Test
+    public void testMultipleServicesWithAnnotation() {
+        List<Class<?>> annotatedClasses = new ArrayList<>();
+        List<Class<?>> testClasses = new ArrayList<>();
+        testClasses.add(AccountService.class);
+        testClasses.add(PaymentService.class);
+        testClasses.add(PaymentServiceImpl.class);
+        testClasses.add(NoAnnotationService.class);
+
+        for (Class<?> clazz : testClasses) {
+            if (clazz.isAnnotationPresent(SagaTransactional.class)) {
+                annotatedClasses.add(clazz);
+            }
+
+            // Also check interfaces
+            for (Class<?> interfaceClass : clazz.getInterfaces()) {
+                if 
(interfaceClass.isAnnotationPresent(SagaTransactional.class)) {
+                    annotatedClasses.add(interfaceClass);
+                }
+            }
+        }
+
+        // Should find AccountService and PaymentService
+        assertTrue(annotatedClasses.size() >= 2);
+        assertTrue(annotatedClasses.contains(AccountService.class));
+        assertTrue(annotatedClasses.contains(PaymentService.class));
+    }
+
+    @Test
+    public void testServiceInstantiation() {
+        // Verify services with annotation can be instantiated normally
+        AccountService accountService = new AccountService();
+        assertNotNull(accountService);
+        assertTrue(accountService.debit("123", 100.0));
+
+        PaymentServiceImpl paymentService = new PaymentServiceImpl();
+        assertNotNull(paymentService);
+        assertTrue(paymentService.processPayment("order123", 50.0));
+    }
+
+    @Test
+    public void testReflectionAccess() throws Exception {
+        AccountService service = new AccountService();
+        Class<?> serviceClass = service.getClass();
+
+        // Verify we can access methods through reflection
+        assertNotNull(serviceClass.getMethod("debit", String.class, 
double.class));
+        assertNotNull(serviceClass.getMethod("credit", String.class, 
double.class));
+
+        // Verify annotation is accessible through reflection
+        assertTrue(serviceClass.isAnnotationPresent(SagaTransactional.class));
+    }
+
+    @Test
+    public void testClassHierarchyAnnotationDetection() {
+        // Test complex inheritance scenario
+        @SagaTransactional
+        class BaseService {
+            public void baseMethod() {}
+        }
+
+        class MiddleService extends BaseService {
+            public void middleMethod() {}
+        }
+
+        class FinalService extends MiddleService {
+            public void finalMethod() {}
+        }
+
+        // Test inheritance chain
+        
assertTrue(BaseService.class.isAnnotationPresent(SagaTransactional.class));
+        
assertTrue(MiddleService.class.isAnnotationPresent(SagaTransactional.class)); 
// inherited
+        
assertTrue(FinalService.class.isAnnotationPresent(SagaTransactional.class)); // 
inherited
+
+        // Test instances
+        FinalService finalService = new FinalService();
+        
assertTrue(finalService.getClass().isAnnotationPresent(SagaTransactional.class));
+    }
+}
diff --git 
a/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParserTest.java
 
b/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParserTest.java
new file mode 100644
index 0000000000..bcb48433c1
--- /dev/null
+++ 
b/saga/seata-saga-annotation/src/test/java/org/apache/seata/saga/rm/remoting/parser/SagaTransactionalRemotingParserTest.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.rm.remoting.parser;
+
+import org.apache.seata.common.exception.FrameworkException;
+import org.apache.seata.integration.tx.api.remoting.Protocols;
+import org.apache.seata.integration.tx.api.remoting.RemotingDesc;
+import org.apache.seata.saga.rm.api.SagaTransactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit tests for SagaTransactionalRemotingParser
+ * Tests the @SagaTransactional annotation support for Saga transaction 
scenarios
+ *
+ * Test scenarios covered:
+ * 1. @SagaTransactional annotation on implementation class
+ * 2. @SagaTransactional annotation on interface with implementation
+ * 3. No annotations (negative test cases)
+ * 4. Multiple interfaces with different annotations
+ * 5. Inheritance scenarios
+ *
+ * The tests verify:
+ * - Annotation detection accuracy
+ * - Service/reference identification
+ * - RemotingDesc generation correctness
+ * - Proper separation from LocalTCC functionality
+ */
+public class SagaTransactionalRemotingParserTest {
+
+    private SagaTransactionalRemotingParser parser;
+
+    // Test classes with different annotation patterns
+    @SagaTransactional
+    public static class SagaTransactionalService {
+        public String doSomething() {
+            return "saga-transactional";
+        }
+    }
+
+    public static class NoAnnotationService {
+        public String doSomething() {
+            return "none";
+        }
+    }
+
+    @SagaTransactional
+    public interface SagaTransactionalInterface {
+        String doSomething();
+    }
+
+    public interface NoAnnotationInterface {
+        String doSomething();
+    }
+
+    public static class SagaTransactionalInterfaceImpl implements 
SagaTransactionalInterface {
+        @Override
+        public String doSomething() {
+            return "impl-saga-transactional";
+        }
+    }
+
+    public static class NoAnnotationInterfaceImpl implements 
NoAnnotationInterface {
+        @Override
+        public String doSomething() {
+            return "impl-none";
+        }
+    }
+
+    @SagaTransactional
+    public static class InheritedSagaService extends SagaTransactionalService {
+        @Override
+        public String doSomething() {
+            return "inherited-saga";
+        }
+    }
+
+    @BeforeEach
+    public void setUp() {
+        parser = new SagaTransactionalRemotingParser();
+    }
+
+    @Test
+    public void testIsReference_SagaTransactional() {
+        SagaTransactionalService service = new SagaTransactionalService();
+        assertTrue(parser.isReference(service, "sagaTransactionalService"));
+    }
+
+    @Test
+    public void testIsReference_NoAnnotations() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertFalse(parser.isReference(service, "noAnnotationService"));
+    }
+
+    @Test
+    public void testIsService_SagaTransactional() {
+        SagaTransactionalService service = new SagaTransactionalService();
+        assertTrue(parser.isService(service, "sagaTransactionalService"));
+    }
+
+    @Test
+    public void testIsService_NoAnnotations() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertFalse(parser.isService(service, "noAnnotationService"));
+    }
+
+    @Test
+    public void testIsService_Class_SagaTransactional() throws 
FrameworkException {
+        assertTrue(parser.isService(SagaTransactionalService.class));
+    }
+
+    @Test
+    public void testIsService_Class_NoAnnotations() throws FrameworkException {
+        assertFalse(parser.isService(NoAnnotationService.class));
+    }
+
+    @Test
+    public void testGetServiceDesc_SagaTransactionalOnImplementation() throws 
FrameworkException {
+        SagaTransactionalService service = new SagaTransactionalService();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"sagaTransactionalService");
+
+        assertRemotingDescWithServiceClass(desc, service, 
SagaTransactionalService.class);
+    }
+
+    @Test
+    public void testGetServiceDesc_SagaTransactionalOnInterface() throws 
FrameworkException {
+        SagaTransactionalInterfaceImpl service = new 
SagaTransactionalInterfaceImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"sagaTransactionalInterfaceImpl");
+
+        assertRemotingDescWithServiceClass(desc, service, 
SagaTransactionalInterface.class);
+    }
+
+    @Test
+    public void testGetServiceDesc_NoAnnotations_ReturnsNull() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertNull(parser.getServiceDesc(service, "noAnnotationService"));
+    }
+
+    @Test
+    public void testGetServiceDesc_NoAnnotationsOnInterface_ReturnsNull() {
+        NoAnnotationInterfaceImpl service = new NoAnnotationInterfaceImpl();
+        assertNull(parser.getServiceDesc(service, 
"noAnnotationInterfaceImpl"));
+    }
+
+    @Test
+    public void testGetProtocol() {
+        assertEquals(Protocols.IN_JVM, parser.getProtocol());
+    }
+
+    @Test
+    public void testInheritedSagaTransactional() throws FrameworkException {
+        InheritedSagaService service = new InheritedSagaService();
+
+        assertTrue(parser.isService(service, "inheritedSagaService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"inheritedSagaService");
+        assertRemotingDescWithServiceClass(desc, service, 
InheritedSagaService.class);
+    }
+
+    @Test
+    public void testAnnotationPrecedence_ImplementationOverInterface() throws 
FrameworkException {
+        // When both implementation and interface have @SagaTransactional,
+        // implementation should take precedence
+
+        @SagaTransactional
+        class TestImpl implements SagaTransactionalInterface {
+            @Override
+            public String doSomething() {
+                return "test-impl";
+            }
+        }
+
+        TestImpl service = new TestImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, "testImpl");
+
+        assertNotNull(desc);
+        // Implementation class should be used, not the interface
+        assertEquals(TestImpl.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void testThrowsFrameworkExceptionWhenNoAnnotationFound() {
+        NoAnnotationService service = new NoAnnotationService();
+
+        // When a bean has no @SagaTransactional annotation, getServiceDesc 
should return null
+        // because isRemoting() check will fail before reaching the exception 
throwing code
+        RemotingDesc desc = parser.getServiceDesc(service, 
"noAnnotationService");
+        assertNull(desc);
+    }
+
+    // Test complex inheritance scenario
+    @Test
+    public void testComplexInheritanceHierarchy() throws FrameworkException {
+        @SagaTransactional
+        class Level1 {
+            public boolean level1Method() {
+                return true;
+            }
+        }
+
+        class Level2 extends Level1 {
+            public boolean level2Method() {
+                return true;
+            }
+        }
+
+        @SagaTransactional
+        class Level3 extends Level2 {
+            public boolean level3Method() {
+                return true;
+            }
+        }
+
+        Level3 service = new Level3();
+
+        // Should be recognized (has its own @SagaTransactional)
+        assertTrue(parser.isService(service, "level3Service"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, "level3Service");
+        assertNotNull(desc);
+        assertEquals(Level3.class, desc.getServiceClass());
+    }
+
+    private void assertValidRemotingDesc(RemotingDesc desc, Object 
expectedTargetBean) {
+        assertNotNull(desc);
+        assertTrue(desc.isReference());
+        assertTrue(desc.isService());
+        assertEquals(Protocols.IN_JVM, desc.getProtocol());
+        assertEquals(expectedTargetBean, desc.getTargetBean());
+        assertNotNull(desc.getServiceClass());
+        assertNotNull(desc.getServiceClassName());
+    }
+
+    private void assertRemotingDescWithServiceClass(
+            RemotingDesc desc, Object targetBean, Class<?> 
expectedServiceClass) {
+        assertValidRemotingDesc(desc, targetBean);
+        assertEquals(expectedServiceClass, desc.getServiceClass());
+        assertEquals(expectedServiceClass.getName(), 
desc.getServiceClassName());
+    }
+}
diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/api/LocalTCC.java 
b/tcc/src/main/java/org/apache/seata/rm/tcc/api/LocalTCC.java
index ba288e3bc6..f3bb601b49 100644
--- a/tcc/src/main/java/org/apache/seata/rm/tcc/api/LocalTCC.java
+++ b/tcc/src/main/java/org/apache/seata/rm/tcc/api/LocalTCC.java
@@ -27,8 +27,42 @@ import java.lang.annotation.Target;
 /**
  * Local TCC bean annotation, add on the TCC interface
  *
- * @see 
org.apache.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object,
 String, Object) // the scanner for TM, GlobalLock, and TCC mode
- * @see LocalTCCRemotingParser // the RemotingParser impl for LocalTCC
+ * This annotation is specifically designed for TCC (Try-Confirm-Cancel) 
transaction mode.
+ * For Saga scenarios, consider using @SagaTransactional annotation instead to 
avoid confusion.
+ *
+ * When to Use @LocalTCC:
+ * - Pure TCC scenarios with Try-Confirm-Cancel semantics
+ * - Existing stable implementations (backward compatibility)
+ * - Services explicitly designed for TCC mode
+ * - When you want to explicitly indicate TCC transaction mode
+ *
+ * When to Consider @SagaTransactional Instead:
+ * - Saga scenarios with compensation actions
+ * - Generic transaction participants (non-TCC specific)
+ * - Services that might be used in multiple transaction modes
+ *
+ * Example Usage:
+ *
+ * @LocalTCC
+ * public interface PaymentTccService {
+ *     @TwoPhaseBusinessAction(name = "payment", commitMethod = 
"confirmPayment", rollbackMethod = "cancelPayment")
+ *     boolean tryPayment(BusinessActionContext context, String orderId, 
double amount);
+ *
+ *     boolean confirmPayment(BusinessActionContext context);
+ *
+ *     boolean cancelPayment(BusinessActionContext context);
+ * }
+ *
+ * Annotation Separation:
+ * Starting from version 2.5.0, @LocalTCC and @SagaTransactional have 
dedicated parsers:
+ * - LocalTCCRemotingParser handles @LocalTCC (TCC mode)
+ * - SagaTransactionalRemotingParser handles @SagaTransactional (Saga mode)
+ * This ensures clear separation of concerns and better maintainability.
+ *
+ * @see 
org.apache.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object,
 String, Object) the scanner for TM, GlobalLock, and TCC mode
+ * @see LocalTCCRemotingParser the RemotingParser impl for LocalTCC
+ * @see org.apache.seata.saga.rm.api.SagaTransactional the dedicated 
annotation for Saga transaction participants
+ * @see org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction commonly used with 
this annotation
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
diff --git 
a/tcc/src/main/java/org/apache/seata/rm/tcc/api/TwoPhaseBusinessAction.java 
b/tcc/src/main/java/org/apache/seata/rm/tcc/api/TwoPhaseBusinessAction.java
index 7172f1a9c3..257dfbc73c 100644
--- a/tcc/src/main/java/org/apache/seata/rm/tcc/api/TwoPhaseBusinessAction.java
+++ b/tcc/src/main/java/org/apache/seata/rm/tcc/api/TwoPhaseBusinessAction.java
@@ -27,9 +27,14 @@ import java.lang.annotation.Target;
 /**
  * TCC annotation.
  * Define a TCC interface, which added on the try method.
- * Must be used with `@LocalTCC`.
+ * Must be used with `@LocalTCC` annotation on the interface or implementation 
class.
+ * This annotation is specifically designed for TCC (Try-Confirm-Cancel) 
transaction mode.
  *
- * @see org.apache.seata.rm.tcc.api.LocalTCC // TCC annotation, which added on 
the TCC interface. It can't be left out.
+ * For Saga scenarios with compensation patterns, use 
`@CompensationBusinessAction` on methods within classes or interfaces marked 
with `@SagaTransactional`.
+ *
+ * @see org.apache.seata.rm.tcc.api.LocalTCC TCC annotation, which added on 
the TCC interface. It can't be left out.
+ * @see org.apache.seata.saga.rm.api.SagaTransactional Generic transaction 
participant annotation for Saga scenarios
+ * @see org.apache.seata.saga.rm.api.CompensationBusinessAction Saga-specific 
business action annotation
  * @see 
org.apache.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object,
 String, Object) // the scanner for TM, GlobalLock, and TCC mode
  * @see TccActionInterceptorHandler // the interceptor of TCC mode
  * @see BusinessActionContext
diff --git 
a/tcc/src/main/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
 
b/tcc/src/main/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
index d1906e3936..8bbed44efc 100644
--- 
a/tcc/src/main/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
+++ 
b/tcc/src/main/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java
@@ -27,8 +27,38 @@ import org.springframework.aop.framework.AopProxyUtils;
 import java.util.Set;
 
 /**
- * local tcc bean parsing
+ * Remoting parser for TCC transaction participant beans with @LocalTCC 
annotation
  *
+ * This parser is specifically designed for TCC (Try-Confirm-Cancel) 
transaction mode
+ * and handles beans annotated with @LocalTCC annotation. It provides proper 
service
+ * detection and proxy enhancement for TCC scenarios.
+ *
+ * Key Features:
+ * - Dedicated support for @LocalTCC annotation
+ * - Optimized for TCC Try-Confirm-Cancel patterns
+ * - Proper integration with TwoPhaseBusinessAction
+ * - High-performance annotation detection
+ *
+ * Usage Pattern:
+ * @LocalTCC
+ * public interface PaymentTccService {
+ *     @TwoPhaseBusinessAction(name = "payment", commitMethod = 
"confirmPayment", rollbackMethod = "cancelPayment")
+ *     boolean tryPayment(BusinessActionContext context, String orderId, 
double amount);
+ *
+ *     boolean confirmPayment(BusinessActionContext context);
+ *     boolean cancelPayment(BusinessActionContext context);
+ * }
+ *
+ * Detection Priority:
+ * 1. Implementation class annotations (higher priority)
+ * 2. Interface annotations (fallback)
+ *
+ * Note: For Saga scenarios, use @SagaTransactional with 
SagaTransactionalRemotingParser instead.
+ *
+ * @see LocalTCC The TCC-specific annotation this parser handles
+ * @see org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction Commonly used with 
@LocalTCC
+ * @see 
org.apache.seata.integration.tx.api.remoting.parser.AbstractedRemotingParser 
Base class
+ * @since 1.0.0
  */
 public class LocalTCCRemotingParser extends AbstractedRemotingParser {
 
@@ -57,24 +87,28 @@ public class LocalTCCRemotingParser extends 
AbstractedRemotingParser {
         remotingDesc.setService(this.isService(bean, beanName));
         remotingDesc.setProtocol(Protocols.IN_JVM);
         Class<?> classType = bean.getClass();
-        // check if LocalTCC annotation is marked on the implementation class
-        if (classType.isAnnotationPresent(LocalTCC.class)) {
+
+        // First priority: check if @LocalTCC is present on the implementation 
class itself
+        // Implementation class annotations take precedence over interface 
annotations
+        if (hasLocalTCCAnnotation(classType)) {
             
remotingDesc.setServiceClass(AopProxyUtils.ultimateTargetClass(bean));
             
remotingDesc.setServiceClassName(remotingDesc.getServiceClass().getName());
             remotingDesc.setTargetBean(bean);
             return remotingDesc;
         }
-        // check if LocalTCC annotation is marked on the interface
+
+        // Second priority: check if @LocalTCC is present on any implemented 
interfaces
+        // Fall back to interface annotations if no implementation class 
annotations found
         Set<Class<?>> interfaceClasses = 
ReflectionUtil.getInterfaces(classType);
         for (Class<?> interClass : interfaceClasses) {
-            if (interClass.isAnnotationPresent(LocalTCC.class)) {
+            if (hasLocalTCCAnnotation(interClass)) {
                 remotingDesc.setServiceClassName(interClass.getName());
                 remotingDesc.setServiceClass(interClass);
                 remotingDesc.setTargetBean(bean);
                 return remotingDesc;
             }
         }
-        throw new FrameworkException("Couldn't parser any Remoting info");
+        throw new FrameworkException("Couldn't parse any Remoting info for 
LocalTCC bean");
     }
 
     @Override
@@ -83,22 +117,39 @@ public class LocalTCCRemotingParser extends 
AbstractedRemotingParser {
     }
 
     /**
-     * Determine whether there is an annotation on interface or impl {@link 
LocalTCC}
-     * @param bean the bean
-     * @return boolean
+     * Check if the given bean is annotated with @LocalTCC annotation
+     *
+     * @param bean the bean to check
+     * @return true if the bean or its interfaces have @LocalTCC annotation
      */
     private boolean isLocalTCC(Object bean) {
-        Class<?> classType = bean.getClass();
-        return isLocalTCC(classType);
+        return isLocalTCC(bean.getClass());
     }
 
+    /**
+     * Check if the given class or its interfaces are annotated with @LocalTCC 
annotation
+     *
+     * @param classType the class type to check
+     * @return true if the class has @LocalTCC annotation
+     */
     private boolean isLocalTCC(Class<?> classType) {
-        Set<Class<?>> interfaceClasses = 
ReflectionUtil.getInterfaces(classType);
-        for (Class<?> interClass : interfaceClasses) {
-            if (interClass.isAnnotationPresent(LocalTCC.class)) {
-                return true;
-            }
+        // Check the class itself first for better performance
+        if (hasLocalTCCAnnotation(classType)) {
+            return true;
         }
-        return classType.isAnnotationPresent(LocalTCC.class);
+
+        // Check all interfaces
+        Set<Class<?>> interfaceClasses = 
ReflectionUtil.getInterfaces(classType);
+        return interfaceClasses.stream().anyMatch(this::hasLocalTCCAnnotation);
+    }
+
+    /**
+     * Check if a class has @LocalTCC annotation
+     *
+     * @param clazz the class to check
+     * @return true if the class has @LocalTCC annotation
+     */
+    private boolean hasLocalTCCAnnotation(Class<?> clazz) {
+        return clazz.isAnnotationPresent(LocalTCC.class);
     }
 }
diff --git 
a/tcc/src/test/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java
 
b/tcc/src/test/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java
index b25ad556fb..084408a5ce 100644
--- 
a/tcc/src/test/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java
+++ 
b/tcc/src/test/java/org/apache/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java
@@ -16,57 +16,208 @@
  */
 package org.apache.seata.rm.tcc.remoting.parser;
 
+import org.apache.seata.common.exception.FrameworkException;
+import org.apache.seata.integration.tx.api.remoting.Protocols;
 import org.apache.seata.integration.tx.api.remoting.RemotingDesc;
-import org.apache.seata.rm.tcc.TccAction;
-import org.apache.seata.rm.tcc.TccActionImpl;
-import org.junit.jupiter.api.Assertions;
+import org.apache.seata.rm.tcc.api.LocalTCC;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 /**
- * The type Local tcc remoting parser test.
+ * Unit tests for LocalTCCRemotingParser
+ * Tests the @LocalTCC annotation support for TCC transaction mode
+ *
+ * Test scenarios covered:
+ * 1. @LocalTCC annotation on implementation class
+ * 2. @LocalTCC annotation on interface with implementation
+ * 3. No annotations (negative test cases)
+ * 4. Multiple interfaces with different annotation patterns
  *
+ * The tests verify:
+ * - Annotation detection accuracy for @LocalTCC
+ * - Service/reference identification
+ * - RemotingDesc generation correctness
+ * - TCC-specific functionality
  */
 public class LocalTCCRemotingParserTest {
 
-    /**
-     * The Local tcc remoting parser.
-     */
-    LocalTCCRemotingParser localTCCRemotingParser = new 
LocalTCCRemotingParser();
+    private LocalTCCRemotingParser parser;
+
+    // Test classes with different annotation patterns
+    @LocalTCC
+    public static class LocalTCCService {
+        public String doSomething() {
+            return "local-tcc";
+        }
+    }
+
+    public static class NoAnnotationService {
+        public String doSomething() {
+            return "none";
+        }
+    }
+
+    @LocalTCC
+    public interface LocalTCCInterface {
+        String doSomething();
+    }
+
+    public interface NoAnnotationInterface {
+        String doSomething();
+    }
+
+    public static class LocalTCCInterfaceImpl implements LocalTCCInterface {
+        @Override
+        public String doSomething() {
+            return "impl-local-tcc";
+        }
+    }
+
+    public static class NoAnnotationInterfaceImpl implements 
NoAnnotationInterface {
+        @Override
+        public String doSomething() {
+            return "impl-none";
+        }
+    }
+
+    // Test class with multiple interfaces
+    @LocalTCC
+    public interface AnotherLocalTCCInterface {
+        String doAnotherThing();
+    }
+
+    public static class MultipleInterfaceImpl implements LocalTCCInterface, 
NoAnnotationInterface {
+        @Override
+        public String doSomething() {
+            return "multiple-interfaces";
+        }
+    }
+
+    @BeforeEach
+    public void setUp() {
+        parser = new LocalTCCRemotingParser();
+    }
 
-    /**
-     * Test service parser.
-     */
     @Test
-    public void testServiceParser() {
-        TccActionImpl tccAction = new TccActionImpl();
+    public void testIsReference_LocalTCC() {
+        LocalTCCService service = new LocalTCCService();
+        assertTrue(parser.isReference(service, "localTCCService"));
+    }
 
-        boolean result = localTCCRemotingParser.isService(tccAction, "a");
-        Assertions.assertTrue(result);
+    @Test
+    public void testIsReference_NoAnnotations() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertFalse(parser.isReference(service, "noAnnotationService"));
     }
 
-    /**
-     * Test reference parser.
-     */
     @Test
-    public void testReferenceParser() {
-        TccActionImpl tccAction = new TccActionImpl();
+    public void testIsService_LocalTCC() {
+        LocalTCCService service = new LocalTCCService();
+        assertTrue(parser.isService(service, "localTCCService"));
+    }
 
-        boolean result = localTCCRemotingParser.isReference(tccAction, "b");
-        Assertions.assertTrue(result);
+    @Test
+    public void testIsService_NoAnnotations() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertFalse(parser.isService(service, "noAnnotationService"));
     }
 
-    /**
-     * Test service desc.
-     */
     @Test
-    public void testServiceDesc() {
-        TccActionImpl tccAction = new TccActionImpl();
+    public void testIsService_Class_LocalTCC() throws FrameworkException {
+        assertTrue(parser.isService(LocalTCCService.class));
+    }
+
+    @Test
+    public void testIsService_Class_NoAnnotations() throws FrameworkException {
+        assertFalse(parser.isService(NoAnnotationService.class));
+    }
 
-        RemotingDesc remotingDesc = 
localTCCRemotingParser.getServiceDesc(tccAction, "c");
-        Assertions.assertNotNull(remotingDesc);
+    @Test
+    public void testGetServiceDesc_LocalTCCOnImplementation() throws 
FrameworkException {
+        LocalTCCService service = new LocalTCCService();
+        RemotingDesc desc = parser.getServiceDesc(service, "localTCCService");
+
+        assertRemotingDescWithServiceClass(desc, service, 
LocalTCCService.class);
+    }
+
+    @Test
+    public void testGetServiceDesc_LocalTCCOnInterface() throws 
FrameworkException {
+        LocalTCCInterfaceImpl service = new LocalTCCInterfaceImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"localTCCInterfaceImpl");
+
+        assertRemotingDescWithServiceClass(desc, service, 
LocalTCCInterface.class);
+    }
+
+    @Test
+    public void testGetServiceDesc_NoAnnotations_ReturnsNull() {
+        NoAnnotationService service = new NoAnnotationService();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"noAnnotationService");
+
+        assertNull(desc);
+    }
+
+    @Test
+    public void testGetServiceDesc_NoAnnotationsOnInterface_ReturnsNull() {
+        NoAnnotationInterfaceImpl service = new NoAnnotationInterfaceImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"noAnnotationInterfaceImpl");
+
+        assertNull(desc);
+    }
+
+    @Test
+    public void testGetProtocol() {
+        assertEquals(Protocols.IN_JVM, parser.getProtocol());
+    }
+
+    @Test
+    public void testMultipleInterfacesWithLocalTCC() throws FrameworkException 
{
+        MultipleInterfaceImpl service = new MultipleInterfaceImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, 
"multipleInterfaceImpl");
+
+        assertNotNull(desc);
+        assertValidRemotingDesc(desc, service);
+
+        // Should detect the @LocalTCC annotation on LocalTCCInterface
+        assertTrue(isAnnotatedInterface(desc.getServiceClass()));
+        assertEquals(LocalTCCInterface.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void testIsRemoting_LocalTCC() {
+        LocalTCCService service = new LocalTCCService();
+        assertTrue(parser.isRemoting(service, "localTCCService"));
+    }
+
+    @Test
+    public void testIsRemoting_NoAnnotations() {
+        NoAnnotationService service = new NoAnnotationService();
+        assertFalse(parser.isRemoting(service, "noAnnotationService"));
+    }
+
+    private boolean isAnnotatedInterface(Class<?> clazz) {
+        return clazz.isAnnotationPresent(LocalTCC.class);
+    }
+
+    private void assertValidRemotingDesc(RemotingDesc desc, Object 
expectedTargetBean) {
+        assertNotNull(desc);
+        assertTrue(desc.isReference());
+        assertTrue(desc.isService());
+        assertEquals(Protocols.IN_JVM, desc.getProtocol());
+        assertEquals(expectedTargetBean, desc.getTargetBean());
+        assertNotNull(desc.getServiceClass());
+        assertNotNull(desc.getServiceClassName());
+    }
 
-        Assertions.assertEquals("org.apache.seata.rm.tcc.TccAction", 
remotingDesc.getServiceClassName());
-        Assertions.assertEquals(remotingDesc.getServiceClass(), 
TccAction.class);
-        Assertions.assertEquals(remotingDesc.getTargetBean(), tccAction);
+    private void assertRemotingDescWithServiceClass(
+            RemotingDesc desc, Object targetBean, Class<?> 
expectedServiceClass) {
+        assertValidRemotingDesc(desc, targetBean);
+        assertEquals(expectedServiceClass, desc.getServiceClass());
+        assertEquals(expectedServiceClass.getName(), 
desc.getServiceClassName());
     }
 }
diff --git 
a/test/src/test/java/org/apache/seata/saga/annotation/AnnotationConflictTest.java
 
b/test/src/test/java/org/apache/seata/saga/annotation/AnnotationConflictTest.java
new file mode 100644
index 0000000000..43737e3a2d
--- /dev/null
+++ 
b/test/src/test/java/org/apache/seata/saga/annotation/AnnotationConflictTest.java
@@ -0,0 +1,364 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.annotation;
+
+import org.apache.seata.integration.tx.api.remoting.RemotingDesc;
+import org.apache.seata.rm.tcc.api.LocalTCC;
+import org.apache.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser;
+import org.apache.seata.saga.rm.api.SagaTransactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for LocalTCCRemotingParser behavior with different annotation 
scenarios
+ *
+ * This test suite validates that LocalTCCRemotingParser:
+ * 1. ONLY recognizes @LocalTCC annotations (its primary responsibility)
+ * 2. IGNORES @SagaTransactional annotations (not its responsibility)
+ * 3. Handles inheritance correctly for @LocalTCC
+ * 4. Handles interface vs implementation scenarios for @LocalTCC
+ * 5. Properly handles edge cases and null scenarios
+ *
+ * Note: @SagaTransactional annotations should be handled by 
SagaTransactionalRemotingParser,
+ * not by LocalTCCRemotingParser. This test verifies proper separation of 
concerns.
+ */
+public class AnnotationConflictTest {
+
+    private LocalTCCRemotingParser parser;
+
+    // Valid @LocalTCC scenarios
+    @LocalTCC
+    public static class LocalTCCService {
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @LocalTCC
+    public interface LocalTCCInterface {
+        boolean doSomething();
+    }
+
+    public static class LocalTCCInterfaceImpl implements LocalTCCInterface {
+        @Override
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    // @SagaTransactional scenarios (should be IGNORED by 
LocalTCCRemotingParser)
+    @SagaTransactional
+    public static class SagaTransactionalService {
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @SagaTransactional
+    public interface SagaTransactionalInterface {
+        boolean doSomething();
+    }
+
+    public static class SagaTransactionalInterfaceImpl implements 
SagaTransactionalInterface {
+        @Override
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    // Mixed scenarios - implementation vs interface
+    @LocalTCC
+    public static class LocalTCCImpl implements SagaTransactionalInterface {
+        @Override
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @SagaTransactional
+    public static class SagaImpl implements LocalTCCInterface {
+        @Override
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    // Inheritance scenarios
+    @LocalTCC
+    public static class BaseLocalTCCService {
+        public boolean baseMethod() {
+            return true;
+        }
+    }
+
+    public static class ExtendedLocalTCCService extends BaseLocalTCCService {
+        public boolean extendedMethod() {
+            return true;
+        }
+    }
+
+    @SagaTransactional
+    public static class BaseSagaService {
+        public boolean baseMethod() {
+            return true;
+        }
+    }
+
+    public static class ExtendedSagaService extends BaseSagaService {
+        public boolean extendedMethod() {
+            return true;
+        }
+    }
+
+    // No annotation scenarios
+    public static class NoAnnotationService {
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    public interface NoAnnotationInterface {
+        boolean doSomething();
+    }
+
+    public static class NoAnnotationInterfaceImpl implements 
NoAnnotationInterface {
+        @Override
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @BeforeEach
+    public void setUp() {
+        parser = new LocalTCCRemotingParser();
+    }
+
+    // Tests for @LocalTCC recognition (should work)
+    @Test
+    public void testLocalTCCOnClass_ShouldBeRecognized() {
+        LocalTCCService service = new LocalTCCService();
+
+        assertTrue(parser.isService(service, "localTCCService"));
+        assertTrue(parser.isReference(service, "localTCCService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, "localTCCService");
+        assertNotNull(desc);
+        assertEquals(LocalTCCService.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void testLocalTCCOnInterface_ShouldBeRecognized() {
+        LocalTCCInterfaceImpl service = new LocalTCCInterfaceImpl();
+
+        assertTrue(parser.isService(service, "localTCCInterfaceImpl"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"localTCCInterfaceImpl");
+        assertNotNull(desc);
+        assertEquals(LocalTCCInterface.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void testLocalTCCInheritance_ShouldBeRecognized() {
+        ExtendedLocalTCCService service = new ExtendedLocalTCCService();
+
+        // Should inherit @LocalTCC from parent
+        assertTrue(parser.isService(service, "extendedLocalTCCService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"extendedLocalTCCService");
+        assertNotNull(desc);
+        assertEquals(ExtendedLocalTCCService.class, desc.getServiceClass());
+    }
+
+    // Tests for @SagaTransactional scenarios (should be IGNORED)
+    @Test
+    public void testSagaTransactionalOnClass_ShouldBeIgnored() {
+        SagaTransactionalService service = new SagaTransactionalService();
+
+        // LocalTCCRemotingParser should NOT recognize @SagaTransactional
+        assertFalse(parser.isService(service, "sagaTransactionalService"));
+        assertFalse(parser.isReference(service, "sagaTransactionalService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"sagaTransactionalService");
+        assertNull(desc);
+    }
+
+    @Test
+    public void testSagaTransactionalOnInterface_ShouldBeIgnored() {
+        SagaTransactionalInterfaceImpl service = new 
SagaTransactionalInterfaceImpl();
+
+        // LocalTCCRemotingParser should NOT recognize @SagaTransactional
+        assertFalse(parser.isService(service, 
"sagaTransactionalInterfaceImpl"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"sagaTransactionalInterfaceImpl");
+        assertNull(desc);
+    }
+
+    @Test
+    public void testSagaTransactionalInheritance_ShouldBeIgnored() {
+        ExtendedSagaService service = new ExtendedSagaService();
+
+        // LocalTCCRemotingParser should NOT recognize inherited 
@SagaTransactional
+        assertFalse(parser.isService(service, "extendedSagaService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"extendedSagaService");
+        assertNull(desc);
+    }
+
+    // Tests for mixed scenarios
+    @Test
+    public void 
testLocalTCCImplWithSagaTransactionalInterface_ShouldRecognizeLocalTCC() {
+        LocalTCCImpl service = new LocalTCCImpl();
+
+        // Should recognize @LocalTCC on implementation, ignore 
@SagaTransactional on interface
+        assertTrue(parser.isService(service, "localTCCImpl"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, "localTCCImpl");
+        assertNotNull(desc);
+        // Implementation class should be used (has @LocalTCC)
+        assertEquals(LocalTCCImpl.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void 
testSagaImplWithLocalTCCInterface_ShouldRecognizeInterfaceLocalTCC() {
+        SagaImpl service = new SagaImpl();
+
+        // Should recognize @LocalTCC on interface, ignore @SagaTransactional 
on implementation
+        assertTrue(parser.isService(service, "sagaImpl"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, "sagaImpl");
+        assertNotNull(desc);
+        // Interface should be used (has @LocalTCC)
+        assertEquals(LocalTCCInterface.class, desc.getServiceClass());
+    }
+
+    // Tests for no annotation scenarios
+    @Test
+    public void testNoAnnotations_ShouldNotBeRecognized() {
+        NoAnnotationService service = new NoAnnotationService();
+
+        assertFalse(parser.isService(service, "noAnnotationService"));
+        assertFalse(parser.isReference(service, "noAnnotationService"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"noAnnotationService");
+        assertNull(desc);
+    }
+
+    @Test
+    public void testNoAnnotationInterface_ShouldNotBeRecognized() {
+        NoAnnotationInterfaceImpl service = new NoAnnotationInterfaceImpl();
+
+        assertFalse(parser.isService(service, "noAnnotationInterfaceImpl"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, 
"noAnnotationInterfaceImpl");
+        assertNull(desc);
+    }
+
+    // Edge case tests
+    @Test
+    public void testNullService_ShouldThrowException() {
+        assertThrows(RuntimeException.class, () -> {
+            parser.isService(null, "nullService");
+        });
+
+        assertThrows(RuntimeException.class, () -> {
+            parser.getServiceDesc(null, "nullService");
+        });
+    }
+
+    @Test
+    public void testNullBeanName_ShouldNotThrowException() {
+        LocalTCCService service = new LocalTCCService();
+
+        assertDoesNotThrow(() -> {
+            boolean result = parser.isService(service, null);
+            assertTrue(result);
+        });
+
+        assertDoesNotThrow(() -> {
+            RemotingDesc desc = parser.getServiceDesc(service, null);
+            assertNotNull(desc);
+        });
+    }
+
+    @Test
+    public void testEmptyBeanName_ShouldNotThrowException() {
+        LocalTCCService service = new LocalTCCService();
+
+        assertDoesNotThrow(() -> {
+            boolean result = parser.isService(service, "");
+            assertTrue(result);
+        });
+
+        assertDoesNotThrow(() -> {
+            RemotingDesc desc = parser.getServiceDesc(service, "");
+            assertNotNull(desc);
+        });
+    }
+
+    @Test
+    public void testAnnotationPrecedence_ImplementationOverInterface() {
+        // When implementation has @LocalTCC and interface has 
@SagaTransactional,
+        // implementation should take precedence
+
+        LocalTCCImpl service = new LocalTCCImpl();
+        RemotingDesc desc = parser.getServiceDesc(service, "testImpl");
+
+        assertNotNull(desc);
+        // Implementation class should be used (has @LocalTCC)
+        assertEquals(LocalTCCImpl.class, desc.getServiceClass());
+    }
+
+    @Test
+    public void testClassHierarchyWithLocalTCC() {
+        // Test inheritance chain with @LocalTCC
+        @LocalTCC
+        class Level1 {
+            public boolean level1Method() {
+                return true;
+            }
+        }
+
+        class Level2 extends Level1 {
+            public boolean level2Method() {
+                return true;
+            }
+        }
+
+        class Level3 extends Level2 {
+            public boolean level3Method() {
+                return true;
+            }
+        }
+
+        Level3 service = new Level3();
+
+        // Should be recognized (inherits @LocalTCC)
+        assertTrue(parser.isService(service, "level3Service"));
+
+        RemotingDesc desc = parser.getServiceDesc(service, "level3Service");
+        assertNotNull(desc);
+        assertEquals(Level3.class, desc.getServiceClass());
+    }
+}
diff --git 
a/test/src/test/java/org/apache/seata/saga/annotation/DualParserIntegrationTest.java
 
b/test/src/test/java/org/apache/seata/saga/annotation/DualParserIntegrationTest.java
new file mode 100644
index 0000000000..b9a02e9831
--- /dev/null
+++ 
b/test/src/test/java/org/apache/seata/saga/annotation/DualParserIntegrationTest.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.annotation;
+
+import org.apache.seata.integration.tx.api.remoting.RemotingDesc;
+import org.apache.seata.rm.tcc.api.LocalTCC;
+import org.apache.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser;
+import org.apache.seata.saga.rm.api.SagaTransactional;
+import 
org.apache.seata.saga.rm.remoting.parser.SagaTransactionalRemotingParser;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Integration tests for dual parser system (LocalTCCRemotingParser + 
SagaTransactionalRemotingParser)
+ *
+ * This test suite validates that both parsers can work together correctly in 
the same application:
+ *
+ * Key scenarios covered:
+ * 1. LocalTCCRemotingParser correctly handles @LocalTCC and ignores 
@SagaTransactional
+ * 2. SagaTransactionalRemotingParser correctly handles @SagaTransactional and 
ignores @LocalTCC
+ * 3. Both parsers can work on the same service beans without conflicts
+ * 4. Parser precedence and service detection work correctly
+ * 5. Mixed annotation scenarios are handled properly by each parser
+ * 6. No cross-contamination between parsers
+ *
+ * This ensures that the new @SagaTransactional annotation system works 
seamlessly
+ * with existing @LocalTCC infrastructure without breaking changes.
+ */
+public class DualParserIntegrationTest {
+
+    private LocalTCCRemotingParser localTCCParser;
+    private SagaTransactionalRemotingParser sagaTransactionalParser;
+
+    // Pure @LocalTCC scenarios
+    @LocalTCC
+    public static class PureLocalTCCService {
+        public boolean doTccOperation() {
+            return true;
+        }
+    }
+
+    @LocalTCC
+    public interface PureLocalTCCInterface {
+        boolean doTccOperation();
+    }
+
+    public static class PureLocalTCCInterfaceImpl implements 
PureLocalTCCInterface {
+        @Override
+        public boolean doTccOperation() {
+            return true;
+        }
+    }
+
+    // Pure @SagaTransactional scenarios
+    @SagaTransactional
+    public static class PureSagaTransactionalService {
+        public boolean doSagaOperation() {
+            return true;
+        }
+    }
+
+    @SagaTransactional
+    public interface PureSagaTransactionalInterface {
+        boolean doSagaOperation();
+    }
+
+    public static class PureSagaTransactionalInterfaceImpl implements 
PureSagaTransactionalInterface {
+        @Override
+        public boolean doSagaOperation() {
+            return true;
+        }
+    }
+
+    // Mixed scenarios - for comprehensive parser separation testing
+    @LocalTCC
+    public static class LocalTCCImplWithSagaInterface implements 
PureSagaTransactionalInterface {
+        @Override
+        public boolean doSagaOperation() {
+            return true;
+        }
+    }
+
+    @SagaTransactional
+    public static class SagaImplWithLocalTCCInterface implements 
PureLocalTCCInterface {
+        @Override
+        public boolean doTccOperation() {
+            return true;
+        }
+    }
+
+    // No annotation control groups
+    public static class NoAnnotationService {
+        public boolean doSomething() {
+            return true;
+        }
+    }
+
+    @BeforeEach
+    public void setUp() {
+        localTCCParser = new LocalTCCRemotingParser();
+        sagaTransactionalParser = new SagaTransactionalRemotingParser();
+    }
+
+    // Test LocalTCCRemotingParser behavior
+    @Test
+    public void testLocalTCCParser_ShouldOnlyRecognizeLocalTCC() {
+        // Should recognize @LocalTCC
+        PureLocalTCCService localTCCService = new PureLocalTCCService();
+        assertTrue(localTCCParser.isService(localTCCService, 
"localTCCService"));
+        assertTrue(localTCCParser.isReference(localTCCService, 
"localTCCService"));
+
+        RemotingDesc localTCCDesc = 
localTCCParser.getServiceDesc(localTCCService, "localTCCService");
+        assertNotNull(localTCCDesc);
+        assertEquals(PureLocalTCCService.class, 
localTCCDesc.getServiceClass());
+
+        // Should NOT recognize @SagaTransactional
+        PureSagaTransactionalService sagaService = new 
PureSagaTransactionalService();
+        assertFalse(localTCCParser.isService(sagaService, "sagaService"));
+        assertFalse(localTCCParser.isReference(sagaService, "sagaService"));
+
+        RemotingDesc sagaDesc = localTCCParser.getServiceDesc(sagaService, 
"sagaService");
+        assertNull(sagaDesc);
+
+        // Should NOT recognize no annotation
+        NoAnnotationService noAnnotationService = new NoAnnotationService();
+        assertFalse(localTCCParser.isService(noAnnotationService, 
"noAnnotationService"));
+        assertNull(localTCCParser.getServiceDesc(noAnnotationService, 
"noAnnotationService"));
+    }
+
+    @Test
+    public void 
testSagaTransactionalParser_ShouldOnlyRecognizeSagaTransactional() {
+        // Should recognize @SagaTransactional
+        PureSagaTransactionalService sagaService = new 
PureSagaTransactionalService();
+        assertTrue(sagaTransactionalParser.isService(sagaService, 
"sagaService"));
+        assertTrue(sagaTransactionalParser.isReference(sagaService, 
"sagaService"));
+
+        RemotingDesc sagaDesc = 
sagaTransactionalParser.getServiceDesc(sagaService, "sagaService");
+        assertNotNull(sagaDesc);
+        assertEquals(PureSagaTransactionalService.class, 
sagaDesc.getServiceClass());
+
+        // Should NOT recognize @LocalTCC
+        PureLocalTCCService localTCCService = new PureLocalTCCService();
+        assertFalse(sagaTransactionalParser.isService(localTCCService, 
"localTCCService"));
+        assertFalse(sagaTransactionalParser.isReference(localTCCService, 
"localTCCService"));
+
+        RemotingDesc localTCCDesc = 
sagaTransactionalParser.getServiceDesc(localTCCService, "localTCCService");
+        assertNull(localTCCDesc);
+
+        // Should NOT recognize no annotation
+        NoAnnotationService noAnnotationService = new NoAnnotationService();
+        assertFalse(sagaTransactionalParser.isService(noAnnotationService, 
"noAnnotationService"));
+        assertNull(sagaTransactionalParser.getServiceDesc(noAnnotationService, 
"noAnnotationService"));
+    }
+
+    @Test
+    public void testInterfaceAnnotationHandling_BothParsers() {
+        // LocalTCC interface implementation
+        PureLocalTCCInterfaceImpl localTCCImpl = new 
PureLocalTCCInterfaceImpl();
+
+        // LocalTCCParser should recognize it
+        assertTrue(localTCCParser.isService(localTCCImpl, "localTCCImpl"));
+        RemotingDesc localTCCDesc = 
localTCCParser.getServiceDesc(localTCCImpl, "localTCCImpl");
+        assertNotNull(localTCCDesc);
+        assertEquals(PureLocalTCCInterface.class, 
localTCCDesc.getServiceClass());
+
+        // SagaTransactionalParser should NOT recognize it
+        assertFalse(sagaTransactionalParser.isService(localTCCImpl, 
"localTCCImpl"));
+        assertNull(sagaTransactionalParser.getServiceDesc(localTCCImpl, 
"localTCCImpl"));
+
+        // SagaTransactional interface implementation
+        PureSagaTransactionalInterfaceImpl sagaImpl = new 
PureSagaTransactionalInterfaceImpl();
+
+        // SagaTransactionalParser should recognize it
+        assertTrue(sagaTransactionalParser.isService(sagaImpl, "sagaImpl"));
+        RemotingDesc sagaDesc = 
sagaTransactionalParser.getServiceDesc(sagaImpl, "sagaImpl");
+        assertNotNull(sagaDesc);
+        assertEquals(PureSagaTransactionalInterface.class, 
sagaDesc.getServiceClass());
+
+        // LocalTCCParser should NOT recognize it
+        assertFalse(localTCCParser.isService(sagaImpl, "sagaImpl"));
+        assertNull(localTCCParser.getServiceDesc(sagaImpl, "sagaImpl"));
+    }
+
+    @Test
+    public void testMixedAnnotationScenarios_ParserPriority() {
+        // @LocalTCC implementation with @SagaTransactional interface
+        LocalTCCImplWithSagaInterface mixedService1 = new 
LocalTCCImplWithSagaInterface();
+
+        // LocalTCCParser should recognize the @LocalTCC on implementation
+        assertTrue(localTCCParser.isService(mixedService1, "mixedService1"));
+        RemotingDesc desc1 = localTCCParser.getServiceDesc(mixedService1, 
"mixedService1");
+        assertNotNull(desc1);
+        assertEquals(LocalTCCImplWithSagaInterface.class, 
desc1.getServiceClass());
+
+        // SagaTransactionalParser should recognize the @SagaTransactional on 
interface
+        assertTrue(sagaTransactionalParser.isService(mixedService1, 
"mixedService1"));
+        RemotingDesc desc2 = 
sagaTransactionalParser.getServiceDesc(mixedService1, "mixedService1");
+        assertNotNull(desc2);
+        assertEquals(PureSagaTransactionalInterface.class, 
desc2.getServiceClass());
+
+        // @SagaTransactional implementation with @LocalTCC interface
+        SagaImplWithLocalTCCInterface mixedService2 = new 
SagaImplWithLocalTCCInterface();
+
+        // LocalTCCParser should recognize the @LocalTCC on interface
+        assertTrue(localTCCParser.isService(mixedService2, "mixedService2"));
+        RemotingDesc desc3 = localTCCParser.getServiceDesc(mixedService2, 
"mixedService2");
+        assertNotNull(desc3);
+        assertEquals(PureLocalTCCInterface.class, desc3.getServiceClass());
+
+        // SagaTransactionalParser should recognize the @SagaTransactional on 
implementation
+        assertTrue(sagaTransactionalParser.isService(mixedService2, 
"mixedService2"));
+        RemotingDesc desc4 = 
sagaTransactionalParser.getServiceDesc(mixedService2, "mixedService2");
+        assertNotNull(desc4);
+        assertEquals(SagaImplWithLocalTCCInterface.class, 
desc4.getServiceClass());
+    }
+
+    @Test
+    public void testBothParsersWorkIndependently() {
+        // Create services for both parsers
+        PureLocalTCCService localTCCService = new PureLocalTCCService();
+        PureSagaTransactionalService sagaService = new 
PureSagaTransactionalService();
+        NoAnnotationService noAnnotationService = new NoAnnotationService();
+
+        // Test all combinations to ensure no cross-contamination
+
+        // LocalTCC service
+        assertTrue(localTCCParser.isService(localTCCService, "test"));
+        assertFalse(sagaTransactionalParser.isService(localTCCService, 
"test"));
+
+        // Saga service
+        assertFalse(localTCCParser.isService(sagaService, "test"));
+        assertTrue(sagaTransactionalParser.isService(sagaService, "test"));
+
+        // No annotation service
+        assertFalse(localTCCParser.isService(noAnnotationService, "test"));
+        assertFalse(sagaTransactionalParser.isService(noAnnotationService, 
"test"));
+    }
+
+    @Test
+    public void testParserProtocolConsistency() {
+        // Both parsers should use the same protocol
+        assertEquals(localTCCParser.getProtocol(), 
sagaTransactionalParser.getProtocol());
+    }
+
+    @Test
+    public void testRemotingDescConsistency_BothParsers() {
+        PureLocalTCCService localTCCService = new PureLocalTCCService();
+        PureSagaTransactionalService sagaService = new 
PureSagaTransactionalService();
+
+        RemotingDesc localTCCDesc = 
localTCCParser.getServiceDesc(localTCCService, "test");
+        RemotingDesc sagaDesc = 
sagaTransactionalParser.getServiceDesc(sagaService, "test");
+
+        // Both should have consistent RemotingDesc structure
+        assertNotNull(localTCCDesc);
+        assertNotNull(sagaDesc);
+
+        // Both should indicate service and reference
+        assertTrue(localTCCDesc.isService());
+        assertTrue(localTCCDesc.isReference());
+        assertTrue(sagaDesc.isService());
+        assertTrue(sagaDesc.isReference());
+
+        // Both should use same protocol
+        assertEquals(localTCCDesc.getProtocol(), sagaDesc.getProtocol());
+
+        // Both should have proper target bean references
+        assertEquals(localTCCService, localTCCDesc.getTargetBean());
+        assertEquals(sagaService, sagaDesc.getTargetBean());
+    }
+
+    @Test
+    public void testNullBeanHandling_BothParsers() {
+        // Both parsers should handle null beans consistently
+        try {
+            localTCCParser.isService(null, "test");
+            assertTrue(false, "Should throw exception for null bean");
+        } catch (Exception e) {
+            // Expected
+        }
+
+        try {
+            sagaTransactionalParser.isService(null, "test");
+            assertTrue(false, "Should throw exception for null bean");
+        } catch (Exception e) {
+            // Expected
+        }
+    }
+}
diff --git 
a/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationAction.java
 
b/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationAction.java
new file mode 100644
index 0000000000..c1761f5894
--- /dev/null
+++ 
b/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationAction.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.annotation;
+
+import org.apache.seata.rm.tcc.api.BusinessActionContext;
+import org.apache.seata.saga.rm.api.SagaTransactional;
+
+import java.util.List;
+
+/**
+ * The interface Saga action using the new @SagaTransactional annotation.
+ * This demonstrates the new way to avoid using @LocalTCC in Saga scenarios.
+ */
+@SagaTransactional
+public interface SagaTransactionalAnnotationAction {
+
+    /**
+     * Commit transaction
+     *
+     * @param actionContext the action context
+     * @param a             the a
+     * @param b             the b
+     * @param sagaParam     the saga param
+     * @return the boolean
+     */
+    boolean commit(BusinessActionContext actionContext, int a, List b, 
SagaParam sagaParam);
+
+    /**
+     * Compensation transaction
+     *
+     * @param actionContext the action context
+     * @param param         the param
+     * @return the boolean
+     */
+    boolean compensation(BusinessActionContext actionContext, SagaParam param);
+}
diff --git 
a/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationActionImpl.java
 
b/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationActionImpl.java
new file mode 100644
index 0000000000..a8ef96419c
--- /dev/null
+++ 
b/test/src/test/java/org/apache/seata/saga/annotation/SagaTransactionalAnnotationActionImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.saga.annotation;
+
+import org.apache.seata.rm.tcc.api.BusinessActionContext;
+import org.apache.seata.rm.tcc.api.BusinessActionContextParameter;
+import org.apache.seata.saga.rm.api.CompensationBusinessAction;
+
+import java.util.List;
+
+/**
+ * Implementation that uses @SagaTransactional instead of @LocalTCC
+ * This demonstrates the recommended approach for Saga scenarios to avoid 
confusion
+ */
+public class SagaTransactionalAnnotationActionImpl implements 
SagaTransactionalAnnotationAction {
+
+    private boolean isCommit;
+
+    @Override
+    @CompensationBusinessAction(
+            name = "sagaActionWithLocalTransactional",
+            compensationMethod = "compensation",
+            compensationArgsClasses = {BusinessActionContext.class, 
SagaParam.class})
+    public boolean commit(
+            BusinessActionContext actionContext,
+            @BusinessActionContextParameter("a") int a,
+            @BusinessActionContextParameter(paramName = "b", index = 0) List b,
+            @BusinessActionContextParameter(isParamInProperty = true) 
SagaParam sagaParam) {
+        isCommit = true;
+        return a > 1;
+    }
+
+    @Override
+    public boolean compensation(
+            BusinessActionContext actionContext, 
@BusinessActionContextParameter("sagaParam") SagaParam param) {
+        isCommit = false;
+        return true;
+    }
+
+    public boolean isCommit() {
+        return isCommit;
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to