com.baomidou
diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java
new file mode 100644
index 000000000..22f32b9b3
--- /dev/null
+++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java
@@ -0,0 +1,44 @@
+package com.pig4cloud.pigx.common.core.factory;
+
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.support.EncodedResource;
+import org.springframework.core.io.support.PropertySourceFactory;
+import org.springframework.lang.Nullable;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * @author lengleng
+ * @date 2022/3/29
+ *
+ * 读取自定义 yaml 文件工厂类
+ */
+public class YamlPropertySourceFactory implements PropertySourceFactory {
+
+ @Override
+ public PropertySource> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
+ Properties propertiesFromYaml = loadYamlIntoProperties(resource);
+ String sourceName = name != null ? name : resource.getResource().getFilename();
+ return new PropertiesPropertySource(sourceName, propertiesFromYaml);
+ }
+
+ private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
+ try {
+ YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
+ factory.setResources(resource.getResource());
+ factory.afterPropertiesSet();
+ return factory.getObject();
+ }
+ catch (IllegalStateException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof FileNotFoundException)
+ throw (FileNotFoundException) e.getCause();
+ throw e;
+ }
+ }
+
+}
diff --git a/pigx-common/pigx-common-seata/pom.xml b/pigx-common/pigx-common-seata/pom.xml
new file mode 100644
index 000000000..7e31126dd
--- /dev/null
+++ b/pigx-common/pigx-common-seata/pom.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ 4.0.0
+
+ com.pig4cloud
+ pigx-common
+ 4.3.0
+
+
+ pigx-common-seata
+ jar
+ pigx 分布式事务处理模块
+
+
+
+
+ com.pig4cloud
+ pigx-common-core
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+
+ io.seata
+ seata-serializer-kryo
+
+
+
+
\ No newline at end of file
diff --git a/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java b/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java
new file mode 100644
index 000000000..82acca7aa
--- /dev/null
+++ b/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java
@@ -0,0 +1,19 @@
+package com.pig4cloud.pigx.common.seata.config;
+
+import com.pig4cloud.pigx.common.core.factory.YamlPropertySourceFactory;
+import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+/**
+ * Seata 配置类
+ *
+ * @author lengleng
+ * @date 2022/3/29
+ */
+@PropertySource(value = "classpath:seata-config.yml", factory = YamlPropertySourceFactory.class)
+@EnableAutoDataSourceProxy
+@Configuration(proxyBeanMethods = false)
+public class SeataAutoConfiguration {
+
+}
diff --git a/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java b/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java
new file mode 100644
index 000000000..6b9ded757
--- /dev/null
+++ b/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java
@@ -0,0 +1,177 @@
+/**
+ * @author lengleng
+ * @date 2022/3/29
+ */
+package io.seata.spring.util;
+
+import io.seata.common.util.CollectionUtils;
+import io.seata.rm.tcc.remoting.parser.DubboUtil;
+import org.springframework.aop.TargetSource;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.framework.AdvisedSupport;
+import org.springframework.aop.support.AopUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Proxy tools base on spring 主要解决 v1.4.2 兼容性问题 https://github.com/seata/seata/issues/3709
+ *
+ * @author zhangsen
+ */
+public class SpringProxyUtils {
+
+ private SpringProxyUtils() {
+ }
+
+ /**
+ * Find target class class.
+ * @param proxy the proxy
+ * @return the class
+ * @throws Exception the exception
+ */
+ public static Class> findTargetClass(Object proxy) throws Exception {
+ if (proxy == null) {
+ return null;
+ }
+ if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) {
+ // #issue 3709
+ final TargetSource targetSource = ((Advised) proxy).getTargetSource();
+ if (!targetSource.isStatic()) {
+ return targetSource.getTargetClass();
+ }
+ return findTargetClass(targetSource.getTarget());
+ }
+ return proxy.getClass();
+ }
+
+ public static Class>[] findInterfaces(Object proxy) throws Exception {
+ if (AopUtils.isJdkDynamicProxy(proxy)) {
+ AdvisedSupport advised = getAdvisedSupport(proxy);
+ return getInterfacesByAdvised(advised);
+ }
+ else {
+ return new Class>[] {};
+ }
+ }
+
+ private static Class>[] getInterfacesByAdvised(AdvisedSupport advised) {
+ Class>[] interfaces = advised.getProxiedInterfaces();
+ if (interfaces.length > 0) {
+ return interfaces;
+ }
+ else {
+ throw new IllegalStateException("Find the jdk dynamic proxy class that does not implement the interface");
+ }
+ }
+
+ /**
+ * Gets advised support.
+ * @param proxy the proxy
+ * @return the advised support
+ * @throws Exception the exception
+ */
+ public static AdvisedSupport getAdvisedSupport(Object proxy) throws Exception {
+ Field h;
+ if (AopUtils.isJdkDynamicProxy(proxy)) {
+ h = proxy.getClass().getSuperclass().getDeclaredField("h");
+ }
+ else {
+ h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
+ }
+ h.setAccessible(true);
+ Object dynamicAdvisedInterceptor = h.get(proxy);
+ Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
+ advised.setAccessible(true);
+ return (AdvisedSupport) advised.get(dynamicAdvisedInterceptor);
+ }
+
+ /**
+ * Is proxy boolean.
+ * @param bean the bean
+ * @return the boolean
+ */
+ public static boolean isProxy(Object bean) {
+ if (bean == null) {
+ return false;
+ }
+ // check dubbo proxy ?
+ return DubboUtil.isDubboProxyName(bean.getClass().getName())
+ || (Proxy.class.isAssignableFrom(bean.getClass()) || AopUtils.isAopProxy(bean));
+ }
+
+ /**
+ * Get the target class , get the interface of its agent if it is a Proxy
+ * @param proxy the proxy
+ * @return target interface
+ * @throws Exception the exception
+ */
+ public static Class> getTargetInterface(Object proxy) throws Exception {
+ if (proxy == null) {
+ throw new IllegalArgumentException("proxy can not be null");
+ }
+
+ // jdk proxy
+ if (Proxy.class.isAssignableFrom(proxy.getClass())) {
+ Proxy p = (Proxy) proxy;
+ return p.getClass().getInterfaces()[0];
+ }
+
+ return getTargetClass(proxy);
+ }
+
+ /**
+ * Get the class type of the proxy target object, if hadn't a target object, return
+ * the interface of the proxy
+ * @param proxy the proxy
+ * @return target interface
+ * @throws Exception the exception
+ */
+ protected static Class> getTargetClass(Object proxy) throws Exception {
+ if (proxy == null) {
+ throw new IllegalArgumentException("proxy can not be null");
+ }
+ // not proxy
+ if (!AopUtils.isAopProxy(proxy)) {
+ return proxy.getClass();
+ }
+ AdvisedSupport advisedSupport = getAdvisedSupport(proxy);
+ Object target = advisedSupport.getTargetSource().getTarget();
+ /*
+ * the Proxy of sofa:reference has no target
+ */
+ if (target == null) {
+ if (CollectionUtils.isNotEmpty(advisedSupport.getProxiedInterfaces())) {
+ return advisedSupport.getProxiedInterfaces()[0];
+ }
+ else {
+ return proxy.getClass();
+ }
+ }
+ else {
+ return getTargetClass(target);
+ }
+ }
+
+ /**
+ * get the all interfaces of bean, if the bean is null, then return empty array
+ * @param bean the bean
+ * @return target interface
+ */
+ public static Class>[] getAllInterfaces(Object bean) {
+ Set> interfaces = new HashSet<>();
+ if (bean != null) {
+ Class> clazz = bean.getClass();
+ while (!Object.class.getName().equalsIgnoreCase(clazz.getName())) {
+ Class>[] clazzInterfaces = clazz.getInterfaces();
+ interfaces.addAll(Arrays.asList(clazzInterfaces));
+ clazz = clazz.getSuperclass();
+ }
+ }
+ return interfaces.toArray(new Class[0]);
+ }
+
+}
diff --git a/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories b/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..68bbc5866
--- /dev/null
+++ b/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.pig4cloud.pigx.common.seata.config.SeataAutoConfiguration
+
+
+
diff --git a/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml b/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml
new file mode 100644
index 000000000..183ff4911
--- /dev/null
+++ b/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml
@@ -0,0 +1,56 @@
+seata:
+ enabled: true
+ tx-service-group: pigx_tx_group # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
+ client:
+ rm-report-success-enable: true
+ rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false)
+ rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5)
+ rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000)
+ rm:
+ lock:
+ lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms)
+ lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30)
+ lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功)
+ tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
+ tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
+ undo:
+ data-validation: true # 二阶段回滚镜像校验(默认true开启)
+ log-serialization: kryo # undo序列化方式(默认jackson 不支持 LocalDateTime)
+ log-table: undo_log # 自定义undo表名(默认undo_log)
+ log:
+ exceptionRate: 100 # 日志异常输出概率(默认100)
+ support:
+ spring:
+ datasource-autoproxy: true
+ service:
+ vgroup-mapping:
+ pigx_tx_group: default # TC 集群(必须与seata-server保持一致)
+ enable-degrade: false # 降级开关
+ disable-global-transaction: false # 禁用全局事务(默认false)
+ grouplist:
+ default: pig-seata:8091
+ transport:
+ shutdown:
+ wait: 3
+ thread-factory:
+ boss-thread-prefix: NettyBoss
+ worker-thread-prefix: NettyServerNIOWorker
+ server-executor-thread-prefix: NettyServerBizHandler
+ share-boss-worker: false
+ client-selector-thread-prefix: NettyClientSelector
+ client-selector-thread-size: 1
+ client-worker-thread-prefix: NettyClientWorkerThread
+ type: TCP
+ server: NIO
+ heartbeat: true
+ serialization: seata
+ compressor: none
+ enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true)
+ registry:
+ file:
+ name: file.conf
+ type: file
+ config:
+ file:
+ name: file.conf
+ type: file
diff --git a/pigx-common/pom.xml b/pigx-common/pom.xml
index 988088ed2..6c376a0e2 100644
--- a/pigx-common/pom.xml
+++ b/pigx-common/pom.xml
@@ -45,6 +45,7 @@
pigx-common-job
pigx-common-log
pigx-common-oss
+ pigx-common-seata
pigx-common-security
pigx-common-sentinel
pigx-common-sequence