diff --git a/aware_demo/pom.xml b/aware_demo/pom.xml
new file mode 100644
index 0000000..14f2c3b
--- /dev/null
+++ b/aware_demo/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.0
+
+
+ org.javaboy
+ aware_demo
+ 0.0.1-SNAPSHOT
+ aware_demo
+ Demo project for Spring Boot
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/aware_demo/src/main/java/org/javaboy/aware_demo/AwareDemoApplication.java b/aware_demo/src/main/java/org/javaboy/aware_demo/AwareDemoApplication.java
new file mode 100644
index 0000000..857b88d
--- /dev/null
+++ b/aware_demo/src/main/java/org/javaboy/aware_demo/AwareDemoApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.aware_demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AwareDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AwareDemoApplication.class, args);
+ }
+
+}
diff --git a/aware_demo/src/main/java/org/javaboy/aware_demo/BeanUtils.java b/aware_demo/src/main/java/org/javaboy/aware_demo/BeanUtils.java
new file mode 100644
index 0000000..a41ba3b
--- /dev/null
+++ b/aware_demo/src/main/java/org/javaboy/aware_demo/BeanUtils.java
@@ -0,0 +1,29 @@
+package org.javaboy.aware_demo;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Component
+public class BeanUtils implements BeanFactoryAware {
+ private static BeanFactory beanFactory = null;
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ BeanUtils.beanFactory = beanFactory;
+ }
+
+ public static T getBean(String beanName) {
+ return (T) beanFactory.getBean(beanName);
+ }
+}
diff --git a/aware_demo/src/main/java/org/javaboy/aware_demo/UserService.java b/aware_demo/src/main/java/org/javaboy/aware_demo/UserService.java
new file mode 100644
index 0000000..3b2bc51
--- /dev/null
+++ b/aware_demo/src/main/java/org/javaboy/aware_demo/UserService.java
@@ -0,0 +1,20 @@
+package org.javaboy.aware_demo;
+
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class UserService {
+
+ public void hello() {
+ System.out.println("hello javaboy!");
+ }
+}
diff --git a/aware_demo/src/main/resources/application.properties b/aware_demo/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/aware_demo/src/test/java/org/javaboy/aware_demo/AwareDemoApplicationTests.java b/aware_demo/src/test/java/org/javaboy/aware_demo/AwareDemoApplicationTests.java
new file mode 100644
index 0000000..fffd533
--- /dev/null
+++ b/aware_demo/src/test/java/org/javaboy/aware_demo/AwareDemoApplicationTests.java
@@ -0,0 +1,15 @@
+package org.javaboy.aware_demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class AwareDemoApplicationTests {
+
+ @Test
+ void contextLoads() {
+ UserService userService = BeanUtils.getBean("userService");
+ userService.hello();
+ }
+
+}
diff --git a/dd_demo/pom.xml b/dd_demo/pom.xml
new file mode 100644
index 0000000..605a005
--- /dev/null
+++ b/dd_demo/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.0
+
+
+ org.javaboy
+ dd_demo
+ 0.0.1-SNAPSHOT
+ dd_demo
+ Demo project for Spring Boot
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.2.2
+
+
+ com.baomidou
+ dynamic-datasource-spring-boot-starter
+ 3.5.1
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/dd_demo/src/main/java/org/javaboy/dd_demo/DdDemoApplication.java b/dd_demo/src/main/java/org/javaboy/dd_demo/DdDemoApplication.java
new file mode 100644
index 0000000..20bf86b
--- /dev/null
+++ b/dd_demo/src/main/java/org/javaboy/dd_demo/DdDemoApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.dd_demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DdDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DdDemoApplication.class, args);
+ }
+
+}
diff --git a/dd_demo/src/main/java/org/javaboy/dd_demo/Test08Service.java b/dd_demo/src/main/java/org/javaboy/dd_demo/Test08Service.java
new file mode 100644
index 0000000..973ed32
--- /dev/null
+++ b/dd_demo/src/main/java/org/javaboy/dd_demo/Test08Service.java
@@ -0,0 +1,25 @@
+package org.javaboy.dd_demo;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class Test08Service {
+
+ @Autowired
+ UserMapper userMapper;
+// @DS("master")
+ public void addUser() {
+ userMapper.addUser("maba22", 9999);
+ }
+}
diff --git a/dd_demo/src/main/java/org/javaboy/dd_demo/Test09Service.java b/dd_demo/src/main/java/org/javaboy/dd_demo/Test09Service.java
new file mode 100644
index 0000000..926cf25
--- /dev/null
+++ b/dd_demo/src/main/java/org/javaboy/dd_demo/Test09Service.java
@@ -0,0 +1,25 @@
+package org.javaboy.dd_demo;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class Test09Service {
+
+ @Autowired
+ UserMapper userMapper;
+// @DS("slave")
+ public void addUser() {
+ userMapper.addUser("qianshi22", 9999);
+ }
+}
diff --git a/dd_demo/src/main/java/org/javaboy/dd_demo/UserMapper.java b/dd_demo/src/main/java/org/javaboy/dd_demo/UserMapper.java
new file mode 100644
index 0000000..bf85ae3
--- /dev/null
+++ b/dd_demo/src/main/java/org/javaboy/dd_demo/UserMapper.java
@@ -0,0 +1,23 @@
+package org.javaboy.dd_demo;
+
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface UserMapper {
+ @Select("select count(*) from user")
+ Integer getCount();
+
+ @Insert("insert into user(username,age) values(#{username},#{age})")
+ Integer addUser(String username, int age);
+}
diff --git a/dd_demo/src/main/java/org/javaboy/dd_demo/UserService.java b/dd_demo/src/main/java/org/javaboy/dd_demo/UserService.java
new file mode 100644
index 0000000..a8e6dfb
--- /dev/null
+++ b/dd_demo/src/main/java/org/javaboy/dd_demo/UserService.java
@@ -0,0 +1,48 @@
+package org.javaboy.dd_demo;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class UserService {
+
+ @Autowired
+ UserMapper userMapper;
+
+ @Autowired
+ Test08Service test08Service;
+
+ @Autowired
+ Test09Service test09Service;
+
+ @Transactional
+ @DS("master")
+ public Integer count() {
+ return userMapper.getCount();
+ }
+
+ @Transactional
+ @DS("slave")
+ public void addUser2() {
+ test08Service.addUser();
+ int i = 1 / 0;
+ test09Service.addUser();
+ }
+
+ @Transactional
+ @DS("master")
+ public Integer addUser() {
+ return userMapper.addUser("fengqi", 101);
+ }
+}
diff --git a/dd_demo/src/main/resources/application.yaml b/dd_demo/src/main/resources/application.yaml
new file mode 100644
index 0000000..df37932
--- /dev/null
+++ b/dd_demo/src/main/resources/application.yaml
@@ -0,0 +1,14 @@
+spring:
+ datasource:
+ dynamic:
+ primary: master #设置默认的数据源或者数据源组,默认值即为master
+ strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
+ datasource:
+ master:
+ url: jdbc:mysql://localhost:3306/test08?serverTimezone=Asia/Shanghai&useSSL=false
+ username: root
+ password: 123
+ slave_1:
+ url: jdbc:mysql://localhost:3306/test09?serverTimezone=Asia/Shanghai&useSSL=false
+ username: root
+ password: 123
\ No newline at end of file
diff --git a/dd_demo/src/test/java/org/javaboy/dd_demo/DdDemoApplicationTests.java b/dd_demo/src/test/java/org/javaboy/dd_demo/DdDemoApplicationTests.java
new file mode 100644
index 0000000..a198ebf
--- /dev/null
+++ b/dd_demo/src/test/java/org/javaboy/dd_demo/DdDemoApplicationTests.java
@@ -0,0 +1,22 @@
+package org.javaboy.dd_demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class DdDemoApplicationTests {
+
+ @Autowired
+ UserService userService;
+
+ @Test
+ void contextLoads() {
+// System.out.println("userService.count() = " + userService.count());
+// System.out.println("userService.addUser() = " + userService.addUser());
+ userService.addUser2();
+ }
+
+
+
+}
diff --git a/permiss_demo/pom.xml b/permiss_demo/pom.xml
new file mode 100644
index 0000000..9c19fb9
--- /dev/null
+++ b/permiss_demo/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.1
+
+
+ org.javaboy
+ permiss_demo
+ 0.0.1-SNAPSHOT
+ permiss_demo
+ Demo project for Spring Boot
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/permiss_demo/src/main/java/org/javaboy/permiss_demo/CustomPermissionEvaluator.java b/permiss_demo/src/main/java/org/javaboy/permiss_demo/CustomPermissionEvaluator.java
new file mode 100644
index 0000000..7d8c5a3
--- /dev/null
+++ b/permiss_demo/src/main/java/org/javaboy/permiss_demo/CustomPermissionEvaluator.java
@@ -0,0 +1,41 @@
+package org.javaboy.permiss_demo;
+
+import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Component
+public class CustomPermissionEvaluator implements PermissionEvaluator {
+
+ AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+ @Override
+ public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
+ Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
+ for (GrantedAuthority authority : authorities) {
+ if (antPathMatcher.match(authority.getAuthority(), (String) permission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
+ return false;
+ }
+}
diff --git a/permiss_demo/src/main/java/org/javaboy/permiss_demo/PermissDemoApplication.java b/permiss_demo/src/main/java/org/javaboy/permiss_demo/PermissDemoApplication.java
new file mode 100644
index 0000000..5cb840c
--- /dev/null
+++ b/permiss_demo/src/main/java/org/javaboy/permiss_demo/PermissDemoApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.permiss_demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class PermissDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(PermissDemoApplication.class, args);
+ }
+
+}
diff --git a/permiss_demo/src/main/java/org/javaboy/permiss_demo/SecurityConfig.java b/permiss_demo/src/main/java/org/javaboy/permiss_demo/SecurityConfig.java
new file mode 100644
index 0000000..66f151b
--- /dev/null
+++ b/permiss_demo/src/main/java/org/javaboy/permiss_demo/SecurityConfig.java
@@ -0,0 +1,45 @@
+package org.javaboy.permiss_demo;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig {
+
+ @Bean
+ UserDetailsService userDetailsService() {
+ InMemoryUserDetailsManager m = new InMemoryUserDetailsManager();
+ m.createUser(User.withUsername("javaboy").password("{noop}123").authorities("system:user:*").build());
+ return m;
+ }
+
+ @Bean
+ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http.csrf().disable();
+ http.authorizeRequests().anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .permitAll();
+ return http.build();
+ }
+
+}
diff --git a/permiss_demo/src/main/java/org/javaboy/permiss_demo/UserController.java b/permiss_demo/src/main/java/org/javaboy/permiss_demo/UserController.java
new file mode 100644
index 0000000..d15c975
--- /dev/null
+++ b/permiss_demo/src/main/java/org/javaboy/permiss_demo/UserController.java
@@ -0,0 +1,39 @@
+package org.javaboy.permiss_demo;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class UserController {
+
+ @GetMapping("/add")
+ @PreAuthorize("hasPermission('/add','system:user:add')")
+ public String addUser() {
+ return "add";
+ }
+ @GetMapping("/delete")
+ @PreAuthorize("hasPermission('/delete','system:user:delete')")
+ public String deleteUser() {
+ return "delete";
+ }
+ @GetMapping("/update")
+ @PreAuthorize("hasPermission('/update','system:user:update')")
+ public String updateUser() {
+ return "update";
+ }
+ @GetMapping("/select")
+ @PreAuthorize("hasPermission('/select','system:user:select')")
+ public String selectUser() {
+ return "select";
+ }
+}
diff --git a/permiss_demo/src/main/resources/application.properties b/permiss_demo/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/permiss_demo/src/test/java/org/javaboy/permiss_demo/PermissDemoApplicationTests.java b/permiss_demo/src/test/java/org/javaboy/permiss_demo/PermissDemoApplicationTests.java
new file mode 100644
index 0000000..1f5d782
--- /dev/null
+++ b/permiss_demo/src/test/java/org/javaboy/permiss_demo/PermissDemoApplicationTests.java
@@ -0,0 +1,24 @@
+package org.javaboy.permiss_demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.AntPathMatcher;
+
+@SpringBootTest
+class PermissDemoApplicationTests {
+
+ AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+ @Test
+ @PreAuthorize("hasPermission('system:user:add')")
+ void contextLoads() {
+ System.out.println("antPathMatcher.match(\"user:*\",\"user:aaa\") = " + antPathMatcher.match("user:*", "user:aaa"));
+ System.out.println("antPathMatcher.match(\"user:list:*\", \"user:list:add\") = " + antPathMatcher.match("user:list:*", "user:list:add"));
+ System.out.println("antPathMatcher.match(\"*:*:*\",\"aa:bb:cc\") = " + antPathMatcher.match("*:*:*", "aa:bb:cc"));
+ System.out.println("antPathMatcher.match(\"a:*:*\",\"c:bbb:ccc\") = " + antPathMatcher.match("a:*:*", "c:bbb:ccc"));
+ System.out.println("antPathMatcher.match(\"user:*:add\",\"user:aaa:add\") = " + antPathMatcher.match("user:*:add", "user:aaa:add"));
+ System.out.println("antPathMatcher.match(\"user:*:add\",\"user1:aa:add\") = " + antPathMatcher.match("user:*:add", "user1:aa:add"));
+ }
+
+}
\ No newline at end of file
diff --git a/seata-tcc.zip b/seata-tcc.zip
new file mode 100644
index 0000000..1a43dd3
Binary files /dev/null and b/seata-tcc.zip differ
diff --git a/seata-tcc/account/pom.xml b/seata-tcc/account/pom.xml
new file mode 100644
index 0000000..1d7ef11
--- /dev/null
+++ b/seata-tcc/account/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+ org.javaboy
+ account
+ 0.0.1-SNAPSHOT
+ account
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.javaboy
+ 1.0-SNAPSHOT
+ common
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+ org.javaboy.account.AccountApplication
+
+
+
+ repackage
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/seata-tcc/account/src/main/java/org/javaboy/account/AccountApplication.java b/seata-tcc/account/src/main/java/org/javaboy/account/AccountApplication.java
new file mode 100644
index 0000000..d65698e
--- /dev/null
+++ b/seata-tcc/account/src/main/java/org/javaboy/account/AccountApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.account;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(scanBasePackages = "org.javaboy")
+public class AccountApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AccountApplication.class, args);
+ }
+
+}
diff --git a/seata-tcc/account/src/main/java/org/javaboy/account/controller/AccountController.java b/seata-tcc/account/src/main/java/org/javaboy/account/controller/AccountController.java
new file mode 100644
index 0000000..08ec2b3
--- /dev/null
+++ b/seata-tcc/account/src/main/java/org/javaboy/account/controller/AccountController.java
@@ -0,0 +1,41 @@
+package org.javaboy.account.controller;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import org.javaboy.account.service.AccountService;
+import org.javaboy.common.RespBean;
+import org.javaboy.common.feign.AccountServiceApi;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class AccountController implements AccountServiceApi {
+ @Autowired
+ AccountService accountService;
+
+ @Override
+ public boolean prepare(BusinessActionContext actionContext, String userId, Double money) {
+ return accountService.prepareDeduct(userId, money);
+ }
+
+ @Override
+ public boolean commit(BusinessActionContext actionContext) {
+ return accountService.commitDeduct(actionContext);
+ }
+
+ @Override
+ public boolean rollback(BusinessActionContext actionContext) {
+ return accountService.rollbackDeduct(actionContext);
+ }
+}
diff --git a/seata-tcc/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java b/seata-tcc/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java
new file mode 100644
index 0000000..344b0cc
--- /dev/null
+++ b/seata-tcc/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java
@@ -0,0 +1,25 @@
+package org.javaboy.account.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import org.javaboy.account.model.Account;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface AccountMapper {
+
+ @Select("select * from account_tbl where userId=#{userId}")
+ Account getAccountByUserId(String userId);
+
+ @Update("update account_tbl set freezeMoney=#{freezeMoney},money=#{money} where userId=#{userId}")
+ Integer updateAccount(Account account);
+}
diff --git a/seata-tcc/account/src/main/java/org/javaboy/account/model/Account.java b/seata-tcc/account/src/main/java/org/javaboy/account/model/Account.java
new file mode 100644
index 0000000..88c26b0
--- /dev/null
+++ b/seata-tcc/account/src/main/java/org/javaboy/account/model/Account.java
@@ -0,0 +1,59 @@
+package org.javaboy.account.model;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+public class Account {
+ private Integer id;
+ private String userId;
+ private Double money;
+ private Double freezeMoney;
+
+ @Override
+ public String toString() {
+ return "Account{" +
+ "id=" + id +
+ ", userId='" + userId + '\'' +
+ ", money=" + money +
+ ", freezeMoney=" + freezeMoney +
+ '}';
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public Double getMoney() {
+ return money;
+ }
+
+ public void setMoney(Double money) {
+ this.money = money;
+ }
+
+ public Double getFreezeMoney() {
+ return freezeMoney;
+ }
+
+ public void setFreezeMoney(Double freezeMoney) {
+ this.freezeMoney = freezeMoney;
+ }
+}
diff --git a/seata-tcc/account/src/main/java/org/javaboy/account/service/AccountService.java b/seata-tcc/account/src/main/java/org/javaboy/account/service/AccountService.java
new file mode 100644
index 0000000..beb02bb
--- /dev/null
+++ b/seata-tcc/account/src/main/java/org/javaboy/account/service/AccountService.java
@@ -0,0 +1,97 @@
+package org.javaboy.account.service;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import org.javaboy.account.model.Account;
+import org.javaboy.account.mapper.AccountMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class AccountService {
+
+ private static final Logger logger = LoggerFactory.getLogger(AccountService.class);
+
+ @Autowired
+ AccountMapper accountMapper;
+
+ /**
+ * 预扣款阶段
+ * 检查账户余额
+ *
+ * @param userId
+ * @param money
+ * @return
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean prepareDeduct(String userId, Double money) {
+ Account account = accountMapper.getAccountByUserId(userId);
+ if (account == null) {
+ throw new RuntimeException("账户不存在");
+ }
+ if (account.getMoney() < money) {
+ throw new RuntimeException("余额不足,预扣款失败");
+ }
+ account.setFreezeMoney(account.getFreezeMoney() + money);
+ account.setMoney(account.getMoney() - money);
+ Integer i = accountMapper.updateAccount(account);
+ logger.info("{} 账户预扣款 {} 元", userId, money);
+ return i == 1;
+ }
+
+ /**
+ * 实际扣款阶段
+ *
+ * @param actionContext
+ * @return
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean commitDeduct(BusinessActionContext actionContext) {
+ String userId = (String) actionContext.getActionContext("userId");
+ Double money = ((BigDecimal) actionContext.getActionContext("money")).doubleValue();
+ Account account = accountMapper.getAccountByUserId(userId);
+ if (account.getFreezeMoney() < money) {
+ throw new RuntimeException("余额不足,扣款失败");
+ }
+ account.setFreezeMoney(account.getFreezeMoney() - money);
+ Integer i = accountMapper.updateAccount(account);
+ logger.info("{} 账户扣款 {} 元", userId, money);
+ return i == 1;
+ }
+
+ /**
+ * 账户回滚阶段
+ *
+ * @param actionContext
+ * @return
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean rollbackDeduct(BusinessActionContext actionContext) {
+ String userId = (String) actionContext.getActionContext("userId");
+ Double money = ((BigDecimal) actionContext.getActionContext("money")).doubleValue();
+ Account account = accountMapper.getAccountByUserId(userId);
+ if (account.getFreezeMoney() >= money) {
+ account.setMoney(account.getMoney() + money);
+ account.setFreezeMoney(account.getFreezeMoney() - money);
+ Integer i = accountMapper.updateAccount(account);
+ logger.info("{} 账户释放冻结金额 {} 元", userId, money);
+ return i == 1;
+ }
+ logger.info("{} 账户资金已释放",userId);
+ //说明prepare中抛出异常,未冻结资金
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/account/src/main/resources/application.properties b/seata-tcc/account/src/main/resources/application.properties
new file mode 100644
index 0000000..426d3db
--- /dev/null
+++ b/seata-tcc/account/src/main/resources/application.properties
@@ -0,0 +1,21 @@
+# 应用名称
+spring.application.name=account
+# 应用服务 WEB 访问端口
+server.port=1111
+#下面这些内容是为了让MyBatis映射
+#指定Mybatis的Mapper文件
+mybatis.mapper-locations=classpath:mappers/*xml
+#指定Mybatis的实体目录
+mybatis.type-aliases-package=org.javaboy.account.mybatis.entity
+# 数据库驱动:
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# 数据源名称
+spring.datasource.name=defaultDataSource
+# 数据库连接地址
+spring.datasource.url=jdbc:mysql://localhost:3306/tcc_account?serverTimezone=UTC
+# 数据库用户名&密码:
+spring.datasource.username=root
+spring.datasource.password=123
+
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
\ No newline at end of file
diff --git a/seata-tcc/account/src/main/resources/file.conf b/seata-tcc/account/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-tcc/account/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/account/src/main/resources/registry.conf b/seata-tcc/account/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-tcc/account/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-tcc/account/src/test/java/org/javaboy/account/AccountApplicationTests.java b/seata-tcc/account/src/test/java/org/javaboy/account/AccountApplicationTests.java
new file mode 100644
index 0000000..fca712c
--- /dev/null
+++ b/seata-tcc/account/src/test/java/org/javaboy/account/AccountApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.account;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class AccountApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-tcc/business/pom.xml b/seata-tcc/business/pom.xml
new file mode 100644
index 0000000..af1f8ae
--- /dev/null
+++ b/seata-tcc/business/pom.xml
@@ -0,0 +1,104 @@
+
+
+ 4.0.0
+ org.javaboy
+ business
+ 0.0.1-SNAPSHOT
+ business
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.javaboy
+ 1.0-SNAPSHOT
+ common
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+ org.javaboy.business.BusinessApplication
+
+
+
+ repackage
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/seata-tcc/business/src/main/java/org/javaboy/business/BusinessApplication.java b/seata-tcc/business/src/main/java/org/javaboy/business/BusinessApplication.java
new file mode 100644
index 0000000..5b02212
--- /dev/null
+++ b/seata-tcc/business/src/main/java/org/javaboy/business/BusinessApplication.java
@@ -0,0 +1,17 @@
+package org.javaboy.business;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication(scanBasePackages = "org.javaboy")
+@EnableFeignClients
+@EnableDiscoveryClient
+public class BusinessApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(BusinessApplication.class, args);
+ }
+
+}
diff --git a/seata-tcc/business/src/main/java/org/javaboy/business/controller/BusinessController.java b/seata-tcc/business/src/main/java/org/javaboy/business/controller/BusinessController.java
new file mode 100644
index 0000000..0c4fd0b
--- /dev/null
+++ b/seata-tcc/business/src/main/java/org/javaboy/business/controller/BusinessController.java
@@ -0,0 +1,33 @@
+package org.javaboy.business.controller;
+
+import org.javaboy.business.service.BusinessService;
+import org.javaboy.common.RespBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class BusinessController {
+ @Autowired
+ BusinessService businessService;
+
+ @PostMapping("/order")
+ public RespBean order(String account, String productId, Integer count) {
+ try {
+ businessService.purchase(account, productId, count);
+ return RespBean.ok("下单成功");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return RespBean.error("下单失败", e.getMessage());
+ }
+ }
+}
diff --git a/seata-tcc/business/src/main/java/org/javaboy/business/feign/OrderServiceApiImpl.java b/seata-tcc/business/src/main/java/org/javaboy/business/feign/OrderServiceApiImpl.java
new file mode 100644
index 0000000..bc42e4f
--- /dev/null
+++ b/seata-tcc/business/src/main/java/org/javaboy/business/feign/OrderServiceApiImpl.java
@@ -0,0 +1,17 @@
+package org.javaboy.business.feign;
+
+import org.javaboy.common.feign.OrderServiceApi;
+import org.springframework.cloud.openfeign.FeignClient;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("order")
+public interface OrderServiceApiImpl extends OrderServiceApi {
+}
diff --git a/seata-tcc/business/src/main/java/org/javaboy/business/feign/StorageServiceApiImpl.java b/seata-tcc/business/src/main/java/org/javaboy/business/feign/StorageServiceApiImpl.java
new file mode 100644
index 0000000..e477b23
--- /dev/null
+++ b/seata-tcc/business/src/main/java/org/javaboy/business/feign/StorageServiceApiImpl.java
@@ -0,0 +1,17 @@
+package org.javaboy.business.feign;
+
+import org.javaboy.common.feign.StorageServiceApi;
+import org.springframework.cloud.openfeign.FeignClient;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("storage")
+public interface StorageServiceApiImpl extends StorageServiceApi {
+}
diff --git a/seata-tcc/business/src/main/java/org/javaboy/business/service/BusinessService.java b/seata-tcc/business/src/main/java/org/javaboy/business/service/BusinessService.java
new file mode 100644
index 0000000..1d84db5
--- /dev/null
+++ b/seata-tcc/business/src/main/java/org/javaboy/business/service/BusinessService.java
@@ -0,0 +1,35 @@
+package org.javaboy.business.service;
+
+import io.seata.core.context.RootContext;
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.spring.annotation.GlobalTransactional;
+import org.javaboy.common.feign.OrderServiceApi;
+import org.javaboy.common.feign.StorageServiceApi;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class BusinessService {
+ @Autowired
+ StorageServiceApi storageServiceApi;
+ @Autowired
+ OrderServiceApi orderServiceApi;
+
+ @GlobalTransactional
+ public void purchase(String account, String productId, Integer count) {
+ String xid = RootContext.getXID();
+ BusinessActionContext actionContext = new BusinessActionContext();
+ actionContext.setXid(xid);
+ storageServiceApi.deduct(actionContext, productId, count);
+ orderServiceApi.prepare(actionContext, account, productId, count);
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/business/src/main/resources/application.properties b/seata-tcc/business/src/main/resources/application.properties
new file mode 100644
index 0000000..3bd5731
--- /dev/null
+++ b/seata-tcc/business/src/main/resources/application.properties
@@ -0,0 +1,7 @@
+# 应用名称
+spring.application.name=business
+# 应用服务 WEB 访问端口
+server.port=1112
+
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
\ No newline at end of file
diff --git a/seata-tcc/business/src/main/resources/file.conf b/seata-tcc/business/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-tcc/business/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/business/src/main/resources/registry.conf b/seata-tcc/business/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-tcc/business/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-tcc/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java b/seata-tcc/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java
new file mode 100644
index 0000000..02e5fc2
--- /dev/null
+++ b/seata-tcc/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.business;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class BusinessApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-tcc/common/pom.xml b/seata-tcc/common/pom.xml
new file mode 100644
index 0000000..62836a5
--- /dev/null
+++ b/seata-tcc/common/pom.xml
@@ -0,0 +1,32 @@
+
+
+
+ seata-tcc
+ org.javaboy
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ common
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.3.7.RELEASE
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+ 2.2.2.RELEASE
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ 2.2.6.RELEASE
+
+
+
+
\ No newline at end of file
diff --git a/seata-tcc/common/src/main/java/org/javaboy/common/RespBean.java b/seata-tcc/common/src/main/java/org/javaboy/common/RespBean.java
new file mode 100644
index 0000000..10c4913
--- /dev/null
+++ b/seata-tcc/common/src/main/java/org/javaboy/common/RespBean.java
@@ -0,0 +1,65 @@
+package org.javaboy.common;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+public class RespBean {
+ private Integer status;
+ private String msg;
+ private Object data;
+
+ public static RespBean ok(String msg, Object data) {
+ return new RespBean(200, msg, data);
+ }
+
+ public static RespBean ok(String msg) {
+ return new RespBean(200, msg, null);
+ }
+
+ public static RespBean error(String msg, Object data) {
+ return new RespBean(500, msg, data);
+ }
+
+ public static RespBean error(String msg) {
+ return new RespBean(500, msg, null);
+ }
+
+ private RespBean() {
+ }
+
+ private RespBean(Integer status, String msg, Object data) {
+ this.status = status;
+ this.msg = msg;
+ this.data = data;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+}
diff --git a/seata-tcc/common/src/main/java/org/javaboy/common/exception/GlobalException.java b/seata-tcc/common/src/main/java/org/javaboy/common/exception/GlobalException.java
new file mode 100644
index 0000000..2302b49
--- /dev/null
+++ b/seata-tcc/common/src/main/java/org/javaboy/common/exception/GlobalException.java
@@ -0,0 +1,26 @@
+package org.javaboy.common.exception;
+
+import org.javaboy.common.RespBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestControllerAdvice
+public class GlobalException {
+
+ @ExceptionHandler(RuntimeException.class)
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public RespBean runtimeException(RuntimeException e) {
+ return RespBean.error(e.getMessage());
+ }
+}
diff --git a/seata-tcc/common/src/main/java/org/javaboy/common/feign/AccountServiceApi.java b/seata-tcc/common/src/main/java/org/javaboy/common/feign/AccountServiceApi.java
new file mode 100644
index 0000000..341b980
--- /dev/null
+++ b/seata-tcc/common/src/main/java/org/javaboy/common/feign/AccountServiceApi.java
@@ -0,0 +1,34 @@
+package org.javaboy.common.feign;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextParameter;
+import io.seata.rm.tcc.api.LocalTCC;
+import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
+import org.javaboy.common.RespBean;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@LocalTCC
+public interface AccountServiceApi {
+ @PostMapping("/account/deduct/prepare")
+ @TwoPhaseBusinessAction(name = "accountServiceApi", commitMethod = "commit", rollbackMethod = "rollback")
+ boolean prepare(@RequestBody BusinessActionContext actionContext, @RequestParam("userId") @BusinessActionContextParameter(paramName = "userId") String userId, @RequestParam("money") @BusinessActionContextParameter(paramName = "money") Double money);
+
+ @RequestMapping("/account/deduct/commit")
+ boolean commit(@RequestBody BusinessActionContext actionContext);
+
+ @RequestMapping("/account/deduct/rollback")
+ boolean rollback(@RequestBody BusinessActionContext actionContext);
+}
diff --git a/seata-tcc/common/src/main/java/org/javaboy/common/feign/OrderServiceApi.java b/seata-tcc/common/src/main/java/org/javaboy/common/feign/OrderServiceApi.java
new file mode 100644
index 0000000..7f91424
--- /dev/null
+++ b/seata-tcc/common/src/main/java/org/javaboy/common/feign/OrderServiceApi.java
@@ -0,0 +1,34 @@
+package org.javaboy.common.feign;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextParameter;
+import io.seata.rm.tcc.api.LocalTCC;
+import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
+import org.javaboy.common.RespBean;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@LocalTCC
+public interface OrderServiceApi {
+ @PostMapping("/order/create/prepare")
+ @TwoPhaseBusinessAction(name = "orderServiceApi", commitMethod = "commit", rollbackMethod = "rollback")
+ boolean prepare(@RequestBody BusinessActionContext actionContext, @RequestParam("userId") @BusinessActionContextParameter(paramName = "userId") String userId, @RequestParam("productId") @BusinessActionContextParameter(paramName = "productId") String productId, @RequestParam("count") @BusinessActionContextParameter(paramName = "count") Integer count);
+
+ @RequestMapping("/order/create/commit")
+ boolean commit(@RequestBody BusinessActionContext actionContext);
+
+ @RequestMapping("/order/create/rollback")
+ boolean rollback(@RequestBody BusinessActionContext actionContext);
+}
diff --git a/seata-tcc/common/src/main/java/org/javaboy/common/feign/StorageServiceApi.java b/seata-tcc/common/src/main/java/org/javaboy/common/feign/StorageServiceApi.java
new file mode 100644
index 0000000..f4907cc
--- /dev/null
+++ b/seata-tcc/common/src/main/java/org/javaboy/common/feign/StorageServiceApi.java
@@ -0,0 +1,32 @@
+package org.javaboy.common.feign;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextParameter;
+import io.seata.rm.tcc.api.LocalTCC;
+import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@LocalTCC
+public interface StorageServiceApi {
+ @PostMapping("/storage/deduct/prepare")
+ @TwoPhaseBusinessAction(name = "storageServiceApi",commitMethod = "commit",rollbackMethod = "rollback")
+ boolean deduct(@RequestBody BusinessActionContext actionContext, @RequestParam("productId")@BusinessActionContextParameter(paramName = "productId") String productId, @RequestParam("count") @BusinessActionContextParameter(paramName = "count") Integer count);
+
+ @RequestMapping("/storage/deduct/commit")
+ boolean commit(@RequestBody BusinessActionContext actionContext);
+
+ @RequestMapping("/storage/deduct/rollback")
+ boolean rollback(@RequestBody BusinessActionContext actionContext);
+}
diff --git a/seata-tcc/eureka/pom.xml b/seata-tcc/eureka/pom.xml
new file mode 100644
index 0000000..f156104
--- /dev/null
+++ b/seata-tcc/eureka/pom.xml
@@ -0,0 +1,92 @@
+
+
+ 4.0.0
+ org.javaboy
+ eureka
+ 0.0.1-SNAPSHOT
+ eureka
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+ org.javaboy.eureka.EurekaApplication
+
+
+
+ repackage
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/seata-tcc/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java b/seata-tcc/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java
new file mode 100644
index 0000000..5d70e8d
--- /dev/null
+++ b/seata-tcc/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java
@@ -0,0 +1,15 @@
+package org.javaboy.eureka;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
+
+@SpringBootApplication
+@EnableEurekaServer
+public class EurekaApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(EurekaApplication.class, args);
+ }
+
+}
diff --git a/seata-tcc/eureka/src/main/resources/application.properties b/seata-tcc/eureka/src/main/resources/application.properties
new file mode 100644
index 0000000..6e491bc
--- /dev/null
+++ b/seata-tcc/eureka/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+# 应用名称
+spring.application.name=eureka
+# 应用服务 WEB 访问端口
+server.port=8761
+
+eureka.client.fetch-registry=false
+eureka.client.register-with-eureka=false
+
+
diff --git a/seata-tcc/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java b/seata-tcc/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java
new file mode 100644
index 0000000..0f0b07c
--- /dev/null
+++ b/seata-tcc/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.eureka;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class EurekaApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-tcc/order/pom.xml b/seata-tcc/order/pom.xml
new file mode 100644
index 0000000..e9e634a
--- /dev/null
+++ b/seata-tcc/order/pom.xml
@@ -0,0 +1,122 @@
+
+
+ 4.0.0
+ org.javaboy
+ order
+ 0.0.1-SNAPSHOT
+ order
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ org.javaboy
+ 1.0-SNAPSHOT
+ common
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+ org.javaboy.order.OrderApplication
+
+
+
+ repackage
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/seata-tcc/order/src/main/java/org/javaboy/order/OrderApplication.java b/seata-tcc/order/src/main/java/org/javaboy/order/OrderApplication.java
new file mode 100644
index 0000000..16f2db6
--- /dev/null
+++ b/seata-tcc/order/src/main/java/org/javaboy/order/OrderApplication.java
@@ -0,0 +1,17 @@
+package org.javaboy.order;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication(scanBasePackages = "org.javaboy")
+@EnableEurekaClient
+@EnableFeignClients
+public class OrderApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OrderApplication.class, args);
+ }
+
+}
diff --git a/seata-tcc/order/src/main/java/org/javaboy/order/controller/OrderController.java b/seata-tcc/order/src/main/java/org/javaboy/order/controller/OrderController.java
new file mode 100644
index 0000000..ee2d4a0
--- /dev/null
+++ b/seata-tcc/order/src/main/java/org/javaboy/order/controller/OrderController.java
@@ -0,0 +1,44 @@
+package org.javaboy.order.controller;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import org.javaboy.common.RespBean;
+import org.javaboy.common.feign.OrderServiceApi;
+import org.javaboy.order.service.OrderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class OrderController implements OrderServiceApi {
+
+ @Autowired
+ OrderService orderService;
+
+ @Override
+ public boolean prepare(BusinessActionContext actionContext, String userId, String productId, Integer count) {
+ return orderService.prepareCreateOrder(actionContext,userId, productId, count);
+ }
+
+ @Override
+ public boolean commit(BusinessActionContext actionContext) {
+ return orderService.commitOrder(actionContext);
+ }
+
+ @Override
+ public boolean rollback(BusinessActionContext actionContext) {
+ return orderService.rollbackOrder(actionContext);
+ }
+}
diff --git a/seata-tcc/order/src/main/java/org/javaboy/order/feign/AccountServiceApiImpl.java b/seata-tcc/order/src/main/java/org/javaboy/order/feign/AccountServiceApiImpl.java
new file mode 100644
index 0000000..7164a1b
--- /dev/null
+++ b/seata-tcc/order/src/main/java/org/javaboy/order/feign/AccountServiceApiImpl.java
@@ -0,0 +1,17 @@
+package org.javaboy.order.feign;
+
+import org.javaboy.common.feign.AccountServiceApi;
+import org.springframework.cloud.openfeign.FeignClient;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("account")
+public interface AccountServiceApiImpl extends AccountServiceApi {
+}
diff --git a/seata-tcc/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java b/seata-tcc/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java
new file mode 100644
index 0000000..70dc1bc
--- /dev/null
+++ b/seata-tcc/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java
@@ -0,0 +1,25 @@
+package org.javaboy.order.mapper;
+
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface OrderMapper {
+ @Insert("insert into order_tbl(userId,productId,count,money) values(#{userId},#{productId},#{count},#{money})")
+ int addOrder(@Param("userId") String userId, @Param("productId") String productId, @Param("count") Integer count, @Param("money") Double money);
+
+ @Delete("delete from order_tbl where userId=#{userId} and productId=#{productId} and count=#{count} order by id desc limit 1")
+ int deleteLatestOrder(@Param("userId") String userId, @Param("productId") String productId, @Param("count") Integer count);
+}
+
diff --git a/seata-tcc/order/src/main/java/org/javaboy/order/service/OrderService.java b/seata-tcc/order/src/main/java/org/javaboy/order/service/OrderService.java
new file mode 100644
index 0000000..a430d3a
--- /dev/null
+++ b/seata-tcc/order/src/main/java/org/javaboy/order/service/OrderService.java
@@ -0,0 +1,61 @@
+package org.javaboy.order.service;
+
+import io.seata.core.context.RootContext;
+import io.seata.rm.tcc.api.BusinessActionContext;
+import org.javaboy.common.feign.AccountServiceApi;
+import org.javaboy.order.mapper.OrderMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class OrderService {
+
+ private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
+
+ @Autowired
+ AccountServiceApi accountServiceApi;
+
+ @Autowired
+ OrderMapper orderMapper;
+
+ @Transactional(rollbackFor = Exception.class)
+ public boolean prepareCreateOrder(BusinessActionContext actionContext, String userId, String productId, Integer count) {
+ //先去扣款,假设每个产品100块钱
+ boolean resp = accountServiceApi.prepare(actionContext, userId, count * 100.0);
+ logger.info("{} 用户购买的 {} 商品共计 {} 件,预下单成功", userId, productId, count);
+ return resp;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public boolean commitOrder(BusinessActionContext actionContext) {
+ String userId = (String) actionContext.getActionContext("userId");
+ String productId = (String) actionContext.getActionContext("productId");
+ Integer count = (Integer) actionContext.getActionContext("count");
+ int i = orderMapper.addOrder(userId, productId, count, count * 100.0);
+ logger.info("{} 用户购买的 {} 商品共计 {} 件,下单成功", userId, productId, count);
+ return i==1;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public boolean rollbackOrder(BusinessActionContext actionContext) {
+ String userId = (String) actionContext.getActionContext("userId");
+ String productId = (String) actionContext.getActionContext("productId");
+ Integer count = (Integer) actionContext.getActionContext("count");
+ logger.info("{} 用户购买的 {} 商品共计 {} 件,订单回滚成功", userId, productId, count);
+ return true;
+ }
+}
diff --git a/seata-tcc/order/src/main/resources/application.properties b/seata-tcc/order/src/main/resources/application.properties
new file mode 100644
index 0000000..3fc2f6f
--- /dev/null
+++ b/seata-tcc/order/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+# 应用名称
+spring.application.name=order
+# 应用服务 WEB 访问端口
+server.port=1113
+#下面这些内容是为了让MyBatis映射
+#指定Mybatis的Mapper文件
+mybatis.mapper-locations=classpath:mappers/*xml
+#指定Mybatis的实体目录
+mybatis.type-aliases-package=org.javaboy.order.mybatis.entity
+# 数据库驱动:
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# 数据源名称
+spring.datasource.name=defaultDataSource
+# 数据库连接地址
+spring.datasource.url=jdbc:mysql://localhost:3306/tcc_order?serverTimezone=UTC
+# 数据库用户名&密码:
+spring.datasource.username=root
+spring.datasource.password=123
+
+
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
\ No newline at end of file
diff --git a/seata-tcc/order/src/main/resources/file.conf b/seata-tcc/order/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-tcc/order/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/order/src/main/resources/registry.conf b/seata-tcc/order/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-tcc/order/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-tcc/order/src/test/java/org/javaboy/order/OrderApplicationTests.java b/seata-tcc/order/src/test/java/org/javaboy/order/OrderApplicationTests.java
new file mode 100644
index 0000000..e4b4b00
--- /dev/null
+++ b/seata-tcc/order/src/test/java/org/javaboy/order/OrderApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.order;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class OrderApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-tcc/pom.xml b/seata-tcc/pom.xml
new file mode 100644
index 0000000..b081830
--- /dev/null
+++ b/seata-tcc/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+ org.javaboy
+ seata-tcc
+ pom
+ 1.0-SNAPSHOT
+
+ common
+
+
+
+
\ No newline at end of file
diff --git a/seata-tcc/storage/pom.xml b/seata-tcc/storage/pom.xml
new file mode 100644
index 0000000..be083b4
--- /dev/null
+++ b/seata-tcc/storage/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+ org.javaboy
+ storage
+ 0.0.1-SNAPSHOT
+ storage
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.javaboy
+ 1.0-SNAPSHOT
+ common
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+ org.javaboy.storage.StorageApplication
+
+
+
+ repackage
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/seata-tcc/storage/src/main/java/org/javaboy/storage/StorageApplication.java b/seata-tcc/storage/src/main/java/org/javaboy/storage/StorageApplication.java
new file mode 100644
index 0000000..6461571
--- /dev/null
+++ b/seata-tcc/storage/src/main/java/org/javaboy/storage/StorageApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.storage;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(scanBasePackages = "org.javaboy")
+public class StorageApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(StorageApplication.class, args);
+ }
+
+}
diff --git a/seata-tcc/storage/src/main/java/org/javaboy/storage/controller/StorageController.java b/seata-tcc/storage/src/main/java/org/javaboy/storage/controller/StorageController.java
new file mode 100644
index 0000000..dd1ae07
--- /dev/null
+++ b/seata-tcc/storage/src/main/java/org/javaboy/storage/controller/StorageController.java
@@ -0,0 +1,42 @@
+package org.javaboy.storage.controller;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.LocalTCC;
+import org.javaboy.common.RespBean;
+import org.javaboy.common.feign.StorageServiceApi;
+import org.javaboy.storage.service.StorageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class StorageController implements StorageServiceApi {
+
+ @Autowired
+ StorageService storageService;
+
+ @Override
+ public boolean deduct(BusinessActionContext actionContext, String productId, Integer count) {
+ return storageService.prepareDeduct(productId, count);
+ }
+
+ @Override
+ public boolean commit(BusinessActionContext actionContext) {
+ return storageService.commitDeduct(actionContext);
+ }
+
+ @Override
+ public boolean rollback(BusinessActionContext actionContext) {
+ return storageService.rollbackDeduct(actionContext);
+ }
+}
diff --git a/seata-tcc/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java b/seata-tcc/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java
new file mode 100644
index 0000000..3bf0228
--- /dev/null
+++ b/seata-tcc/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java
@@ -0,0 +1,26 @@
+package org.javaboy.storage.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import org.javaboy.storage.model.Storage;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface StorageMapper {
+
+ @Update("update storage_tbl set freezeCount=#{freezeCount},count=#{count} where productId=#{productId}")
+ int updateStorage(Storage storage);
+
+ @Select("select * from storage_tbl where productId=#{productId}")
+ Storage getStorageByProductId(String productId);
+}
diff --git a/seata-tcc/storage/src/main/java/org/javaboy/storage/model/Storage.java b/seata-tcc/storage/src/main/java/org/javaboy/storage/model/Storage.java
new file mode 100644
index 0000000..fb9b94d
--- /dev/null
+++ b/seata-tcc/storage/src/main/java/org/javaboy/storage/model/Storage.java
@@ -0,0 +1,49 @@
+package org.javaboy.storage.model;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+public class Storage {
+ private Integer id;
+ private String productId;
+ private Integer count;
+ private Integer freezeCount;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getProductId() {
+ return productId;
+ }
+
+ public void setProductId(String productId) {
+ this.productId = productId;
+ }
+
+ public Integer getCount() {
+ return count;
+ }
+
+ public void setCount(Integer count) {
+ this.count = count;
+ }
+
+ public Integer getFreezeCount() {
+ return freezeCount;
+ }
+
+ public void setFreezeCount(Integer freezeCount) {
+ this.freezeCount = freezeCount;
+ }
+}
diff --git a/seata-tcc/storage/src/main/java/org/javaboy/storage/service/StorageService.java b/seata-tcc/storage/src/main/java/org/javaboy/storage/service/StorageService.java
new file mode 100644
index 0000000..f18ac7d
--- /dev/null
+++ b/seata-tcc/storage/src/main/java/org/javaboy/storage/service/StorageService.java
@@ -0,0 +1,92 @@
+package org.javaboy.storage.service;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.LocalTCC;
+import io.seata.spring.annotation.GlobalTransactional;
+import org.javaboy.storage.mapper.StorageMapper;
+import org.javaboy.storage.model.Storage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import reactor.core.publisher.Mono;
+
+import java.net.Inet4Address;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class StorageService {
+
+ private static final Logger logger = LoggerFactory.getLogger(StorageService.class);
+
+ @Autowired
+ StorageMapper storageMapper;
+
+ /**
+ * 预扣库存
+ *
+ * @param productId
+ * @param count
+ * @return
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean prepareDeduct(String productId, Integer count) {
+ Storage storage = storageMapper.getStorageByProductId(productId);
+ if (storage == null) {
+ throw new RuntimeException("商品不存在");
+ }
+ if (storage.getCount() < count) {
+ throw new RuntimeException("库存不足,预扣库存失败");
+ }
+ storage.setFreezeCount(storage.getFreezeCount() + count);
+ storage.setCount(storage.getCount() - count);
+ int i = storageMapper.updateStorage(storage);
+ logger.info("{} 商品库存冻结 {} 个", productId, count);
+ return i == 1;
+ }
+
+ /**
+ * 扣库存
+ *
+ * @param actionContext
+ * @return
+ */
+ @Transactional(rollbackFor = Exception.class)
+ public boolean commitDeduct(BusinessActionContext actionContext) {
+ String productId = (String) actionContext.getActionContext("productId");
+ Integer count = (Integer) actionContext.getActionContext("count");
+ Storage storage = storageMapper.getStorageByProductId(productId);
+ if (storage.getFreezeCount() < count) {
+ throw new RuntimeException("库存不足,扣库存失败");
+ }
+ storage.setFreezeCount(storage.getFreezeCount() - count);
+ int i = storageMapper.updateStorage(storage);
+ logger.info("{} 商品库存扣除 {} 个", productId, count);
+ return i == 1;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public boolean rollbackDeduct(BusinessActionContext actionContext) {
+ String productId = (String) actionContext.getActionContext("productId");
+ Integer count = (Integer) actionContext.getActionContext("count");
+ Storage storage = storageMapper.getStorageByProductId(productId);
+ if (storage.getFreezeCount() >= count) {
+ storage.setFreezeCount(storage.getFreezeCount() - count);
+ storage.setCount(storage.getCount() + count);
+ int i = storageMapper.updateStorage(storage);
+ logger.info("{} 商品释放库存 {} 个", productId, count);
+ return i == 1;
+ }
+ //说明 prepare 阶段就没有冻结
+ return true;
+ }
+}
diff --git a/seata-tcc/storage/src/main/resources/application.properties b/seata-tcc/storage/src/main/resources/application.properties
new file mode 100644
index 0000000..4d8418c
--- /dev/null
+++ b/seata-tcc/storage/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+# 应用名称
+spring.application.name=storage
+# 应用服务 WEB 访问端口
+server.port=1114
+#下面这些内容是为了让MyBatis映射
+#指定Mybatis的Mapper文件
+mybatis.mapper-locations=classpath:mappers/*xml
+#指定Mybatis的实体目录
+mybatis.type-aliases-package=org.javaboy.storage.mybatis.entity
+# 数据库驱动:
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+# 数据源名称
+spring.datasource.name=defaultDataSource
+# 数据库连接地址
+spring.datasource.url=jdbc:mysql://localhost:3306/tcc_storage?serverTimezone=UTC
+# 数据库用户名&密码:
+spring.datasource.username=root
+spring.datasource.password=123
+
+
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
\ No newline at end of file
diff --git a/seata-tcc/storage/src/main/resources/file.conf b/seata-tcc/storage/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-tcc/storage/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-tcc/storage/src/main/resources/registry.conf b/seata-tcc/storage/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-tcc/storage/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-tcc/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java b/seata-tcc/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java
new file mode 100644
index 0000000..0a32d95
--- /dev/null
+++ b/seata-tcc/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.storage;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class StorageApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-xa/account/pom.xml b/seata-xa/account/pom.xml
new file mode 100644
index 0000000..d94f764
--- /dev/null
+++ b/seata-xa/account/pom.xml
@@ -0,0 +1,113 @@
+
+
+ 4.0.0
+ org.javaboy
+ account
+ 0.0.1-SNAPSHOT
+
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ common
+ org.javaboy
+ 1.0-SNAPSHOT
+
+
+ mysql
+ mysql-connector-java
+ runtime
+ 8.0.11
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+
+
+
+
+
diff --git a/seata-xa/account/src/main/java/org/javaboy/AccountApplication.java b/seata-xa/account/src/main/java/org/javaboy/AccountApplication.java
new file mode 100644
index 0000000..35e183f
--- /dev/null
+++ b/seata-xa/account/src/main/java/org/javaboy/AccountApplication.java
@@ -0,0 +1,18 @@
+package org.javaboy;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+
+@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
+@EnableEurekaClient
+@MapperScan(basePackages = "org.javaboy.account.mapper")
+public class AccountApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AccountApplication.class, args);
+ }
+
+}
diff --git a/seata-xa/account/src/main/java/org/javaboy/account/config/DataSourceConfiguration.java b/seata-xa/account/src/main/java/org/javaboy/account/config/DataSourceConfiguration.java
new file mode 100644
index 0000000..0726528
--- /dev/null
+++ b/seata-xa/account/src/main/java/org/javaboy/account/config/DataSourceConfiguration.java
@@ -0,0 +1,55 @@
+package org.javaboy.account.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.zaxxer.hikari.HikariDataSource;
+import io.seata.rm.datasource.xa.DataSourceProxyXA;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import javax.sql.DataSource;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Configuration
+public class DataSourceConfiguration {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DruidDataSource druidDataSource() {
+ return new DruidDataSource();
+ }
+
+ @Bean("dataSourceProxy")
+ @Primary
+ public DataSource dataSource(DruidDataSource druidDataSource) {
+ return new DataSourceProxyXA(druidDataSource);
+ }
+
+
+ @Bean
+ public SqlSessionFactory sqlSessionFactory(DataSource dataSourceProxy)throws Exception{
+ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
+ sqlSessionFactoryBean.setDataSource(dataSourceProxy);
+ sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
+ return sqlSessionFactoryBean.getObject();
+ }
+
+}
\ No newline at end of file
diff --git a/seata-xa/account/src/main/java/org/javaboy/account/controller/AccountController.java b/seata-xa/account/src/main/java/org/javaboy/account/controller/AccountController.java
new file mode 100644
index 0000000..3dfd7ae
--- /dev/null
+++ b/seata-xa/account/src/main/java/org/javaboy/account/controller/AccountController.java
@@ -0,0 +1,38 @@
+package org.javaboy.account.controller;
+
+import org.javaboy.account.service.AccountService;
+import org.javaboy.common.RespBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.sql.DataSource;
+import java.util.Map;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class AccountController {
+
+ @Autowired
+ AccountService accountService;
+
+ @Autowired
+ DataSource dataSource;
+
+ @PostMapping("/deductAccount")
+ public RespBean deductAccount(String account, Double money) {
+ System.out.println("dataSource.getClass() = " + dataSource.getClass());
+ if (accountService.deductAccount(account, money)) {
+ return RespBean.ok("扣款成功");
+ }
+ return RespBean.error("扣款失败");
+ }
+}
diff --git a/seata-xa/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java b/seata-xa/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java
new file mode 100644
index 0000000..6fe3b38
--- /dev/null
+++ b/seata-xa/account/src/main/java/org/javaboy/account/mapper/AccountMapper.java
@@ -0,0 +1,24 @@
+package org.javaboy.account.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface AccountMapper {
+ @Update("update account_tbl set money=money-#{money} where user_id=#{account}")
+ int updateAccount(@Param("account") String account, @Param("money") Double money);
+
+ @Select("select money from account_tbl where user_id=#{account}")
+ Double getMoneyByAccount(String account);
+}
diff --git a/seata-xa/account/src/main/java/org/javaboy/account/service/AccountService.java b/seata-xa/account/src/main/java/org/javaboy/account/service/AccountService.java
new file mode 100644
index 0000000..8c676d0
--- /dev/null
+++ b/seata-xa/account/src/main/java/org/javaboy/account/service/AccountService.java
@@ -0,0 +1,30 @@
+package org.javaboy.account.service;
+
+import org.javaboy.account.mapper.AccountMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class AccountService {
+
+ @Autowired
+ AccountMapper accountMapper;
+
+ public boolean deductAccount(String account, Double money) {
+ Double m = accountMapper.getMoneyByAccount(account);
+ if (m < money) {
+ throw new RuntimeException("账户余额不足");
+ }
+ int i = accountMapper.updateAccount(account, money);
+ return i == 1;
+ }
+}
diff --git a/seata-xa/account/src/main/resources/application.properties b/seata-xa/account/src/main/resources/application.properties
new file mode 100644
index 0000000..172c2c4
--- /dev/null
+++ b/seata-xa/account/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+server.port=1111
+spring.application.name=account
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+
+spring.datasource.username=root
+spring.datasource.password=123
+spring.datasource.url=jdbc:mysql:///xa_account?serverTimezone=Asia/Shanghai&useSSL=false
+
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
+
+seata.enable-auto-data-source-proxy=false
\ No newline at end of file
diff --git a/seata-xa/account/src/main/resources/file.conf b/seata-xa/account/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-xa/account/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-xa/account/src/main/resources/registry.conf b/seata-xa/account/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-xa/account/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-xa/account/src/test/java/org/javaboy/account/AccountApplicationTests.java b/seata-xa/account/src/test/java/org/javaboy/account/AccountApplicationTests.java
new file mode 100644
index 0000000..fca712c
--- /dev/null
+++ b/seata-xa/account/src/test/java/org/javaboy/account/AccountApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.account;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class AccountApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-xa/business/pom.xml b/seata-xa/business/pom.xml
new file mode 100644
index 0000000..eba6af4
--- /dev/null
+++ b/seata-xa/business/pom.xml
@@ -0,0 +1,101 @@
+
+
+ 4.0.0
+ org.javaboy
+ business
+ 0.0.1-SNAPSHOT
+
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ common
+ org.javaboy
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+
+
+
+
diff --git a/seata-xa/business/src/main/java/org/javaboy/BusinessApplication.java b/seata-xa/business/src/main/java/org/javaboy/BusinessApplication.java
new file mode 100644
index 0000000..7ab3255
--- /dev/null
+++ b/seata-xa/business/src/main/java/org/javaboy/BusinessApplication.java
@@ -0,0 +1,15 @@
+package org.javaboy;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication
+@EnableFeignClients
+public class BusinessApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(BusinessApplication.class, args);
+ }
+
+}
diff --git a/seata-xa/business/src/main/java/org/javaboy/business/controller/BusinessController.java b/seata-xa/business/src/main/java/org/javaboy/business/controller/BusinessController.java
new file mode 100644
index 0000000..b4c2974
--- /dev/null
+++ b/seata-xa/business/src/main/java/org/javaboy/business/controller/BusinessController.java
@@ -0,0 +1,34 @@
+package org.javaboy.business.controller;
+
+import org.javaboy.business.service.BusinessService;
+import org.javaboy.common.RespBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class BusinessController {
+
+ @Autowired
+ BusinessService businessService;
+
+ @GetMapping("/order")
+ public RespBean order(String account, Integer count, String productId) {
+ try {
+ businessService.purchase(account, count, productId);
+ return RespBean.ok("下单成功");
+ } catch (Exception e) {
+ return RespBean.error(e.getMessage());
+ }
+
+ }
+}
diff --git a/seata-xa/business/src/main/java/org/javaboy/business/feign/OrderFeignClient.java b/seata-xa/business/src/main/java/org/javaboy/business/feign/OrderFeignClient.java
new file mode 100644
index 0000000..7d9cea5
--- /dev/null
+++ b/seata-xa/business/src/main/java/org/javaboy/business/feign/OrderFeignClient.java
@@ -0,0 +1,24 @@
+package org.javaboy.business.feign;
+
+import org.javaboy.common.RespBean;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.ws.rs.POST;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("order")
+public interface OrderFeignClient {
+ @PostMapping("/createOrder")
+ RespBean createOrder(@RequestParam("acount") String account, @RequestParam("count") Integer count, @RequestParam("productId") String productId);
+}
diff --git a/seata-xa/business/src/main/java/org/javaboy/business/feign/StorageFeignClient.java b/seata-xa/business/src/main/java/org/javaboy/business/feign/StorageFeignClient.java
new file mode 100644
index 0000000..e6468a5
--- /dev/null
+++ b/seata-xa/business/src/main/java/org/javaboy/business/feign/StorageFeignClient.java
@@ -0,0 +1,21 @@
+package org.javaboy.business.feign;
+
+import org.javaboy.common.RespBean;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("storage")
+public interface StorageFeignClient {
+ @PostMapping("/deduct")
+ RespBean deduce(@RequestParam("productId") String productId, @RequestParam("count") Integer count);
+}
diff --git a/seata-xa/business/src/main/java/org/javaboy/business/service/BusinessService.java b/seata-xa/business/src/main/java/org/javaboy/business/service/BusinessService.java
new file mode 100644
index 0000000..dc14d50
--- /dev/null
+++ b/seata-xa/business/src/main/java/org/javaboy/business/service/BusinessService.java
@@ -0,0 +1,33 @@
+package org.javaboy.business.service;
+
+import io.seata.spring.annotation.GlobalTransactional;
+import org.javaboy.business.feign.OrderFeignClient;
+import org.javaboy.business.feign.StorageFeignClient;
+import org.javaboy.common.RespBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class BusinessService {
+
+ @Autowired
+ StorageFeignClient storageFeignClient;
+ @Autowired
+ OrderFeignClient orderFeignClient;
+
+
+ @GlobalTransactional(rollbackFor = Exception.class)
+ public void purchase(String account, Integer count, String productId) {
+ storageFeignClient.deduce(productId, count);
+ orderFeignClient.createOrder(account, count, productId);
+ }
+}
diff --git a/seata-xa/business/src/main/resources/application.properties b/seata-xa/business/src/main/resources/application.properties
new file mode 100644
index 0000000..c017008
--- /dev/null
+++ b/seata-xa/business/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+server.port=1112
+spring.application.name=business
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
\ No newline at end of file
diff --git a/seata-xa/business/src/main/resources/file.conf b/seata-xa/business/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-xa/business/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-xa/business/src/main/resources/registry.conf b/seata-xa/business/src/main/resources/registry.conf
new file mode 100755
index 0000000..b76b193
--- /dev/null
+++ b/seata-xa/business/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "file"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "eureka"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-xa/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java b/seata-xa/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java
new file mode 100644
index 0000000..02e5fc2
--- /dev/null
+++ b/seata-xa/business/src/test/java/org/javaboy/business/BusinessApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.business;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class BusinessApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-xa/common/pom.xml b/seata-xa/common/pom.xml
new file mode 100644
index 0000000..ef251de
--- /dev/null
+++ b/seata-xa/common/pom.xml
@@ -0,0 +1,65 @@
+
+
+
+ seata-xa
+ org.javaboy
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ common
+
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+
+
+
+
+
\ No newline at end of file
diff --git a/seata-xa/common/src/main/java/org/javaboy/common/RespBean.java b/seata-xa/common/src/main/java/org/javaboy/common/RespBean.java
new file mode 100644
index 0000000..c6ff68f
--- /dev/null
+++ b/seata-xa/common/src/main/java/org/javaboy/common/RespBean.java
@@ -0,0 +1,65 @@
+package org.javaboy.common;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+public class RespBean {
+ private Integer status;
+ private String message;
+ private Object data;
+
+ public static RespBean ok(String message, Object data) {
+ return new RespBean(200, message, data);
+ }
+
+ public static RespBean ok(String message) {
+ return new RespBean(200, message, null);
+ }
+
+ public static RespBean error(String message, Object data) {
+ return new RespBean(500, message, data);
+ }
+
+ public static RespBean error(String message) {
+ return new RespBean(500, message, null);
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ private RespBean() {
+ }
+
+ private RespBean(Integer status, String message, Object data) {
+ this.status = status;
+ this.message = message;
+ this.data = data;
+ }
+}
diff --git a/seata-xa/common/src/main/java/org/javaboy/common/exception/GlobalException.java b/seata-xa/common/src/main/java/org/javaboy/common/exception/GlobalException.java
new file mode 100644
index 0000000..768b192
--- /dev/null
+++ b/seata-xa/common/src/main/java/org/javaboy/common/exception/GlobalException.java
@@ -0,0 +1,25 @@
+package org.javaboy.common.exception;
+
+import org.javaboy.common.RespBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestControllerAdvice
+public class GlobalException {
+ @ExceptionHandler(RuntimeException.class)
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public RespBean runtimeException(RuntimeException e) {
+ return RespBean.error(e.getMessage());
+ }
+}
diff --git a/seata-xa/eureka/pom.xml b/seata-xa/eureka/pom.xml
new file mode 100644
index 0000000..ddaf0f4
--- /dev/null
+++ b/seata-xa/eureka/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.8
+
+
+ org.javaboy
+ eureka
+ 0.0.1-SNAPSHOT
+ eureka
+ Demo project for Spring Boot
+
+ 17
+ 2021.0.2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/seata-xa/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java b/seata-xa/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java
new file mode 100644
index 0000000..5d70e8d
--- /dev/null
+++ b/seata-xa/eureka/src/main/java/org/javaboy/eureka/EurekaApplication.java
@@ -0,0 +1,15 @@
+package org.javaboy.eureka;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
+
+@SpringBootApplication
+@EnableEurekaServer
+public class EurekaApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(EurekaApplication.class, args);
+ }
+
+}
diff --git a/seata-xa/eureka/src/main/resources/application.properties b/seata-xa/eureka/src/main/resources/application.properties
new file mode 100644
index 0000000..156cbda
--- /dev/null
+++ b/seata-xa/eureka/src/main/resources/application.properties
@@ -0,0 +1,4 @@
+eureka.client.fetch-registry=false
+eureka.client.register-with-eureka=false
+
+server.port=8761
\ No newline at end of file
diff --git a/seata-xa/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java b/seata-xa/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java
new file mode 100644
index 0000000..0f0b07c
--- /dev/null
+++ b/seata-xa/eureka/src/test/java/org/javaboy/eureka/EurekaApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.eureka;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class EurekaApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-xa/order/pom.xml b/seata-xa/order/pom.xml
new file mode 100644
index 0000000..204f65f
--- /dev/null
+++ b/seata-xa/order/pom.xml
@@ -0,0 +1,117 @@
+
+
+ 4.0.0
+ org.javaboy
+ order
+ 0.0.1-SNAPSHOT
+
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ common
+ org.javaboy
+ 1.0-SNAPSHOT
+
+
+ mysql
+ mysql-connector-java
+ runtime
+ 8.0.11
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+
+
+
+
diff --git a/seata-xa/order/src/main/java/org/javaboy/OrderApplication.java b/seata-xa/order/src/main/java/org/javaboy/OrderApplication.java
new file mode 100644
index 0000000..c1cab27
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/OrderApplication.java
@@ -0,0 +1,18 @@
+package org.javaboy;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
+@EnableEurekaClient
+@EnableFeignClients
+public class OrderApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OrderApplication.class, args);
+ }
+
+}
diff --git a/seata-xa/order/src/main/java/org/javaboy/order/config/DataSourceConfiguration.java b/seata-xa/order/src/main/java/org/javaboy/order/config/DataSourceConfiguration.java
new file mode 100644
index 0000000..6053c7c
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/order/config/DataSourceConfiguration.java
@@ -0,0 +1,51 @@
+package org.javaboy.order.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.seata.rm.datasource.xa.DataSourceProxyXA;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.sql.DataSource;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Configuration
+public class DataSourceConfiguration {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DruidDataSource druidDataSource() {
+ return new DruidDataSource();
+ }
+
+ @Bean("dataSourceProxy")
+ @Primary
+ public DataSource dataSource(DruidDataSource druidDataSource) {
+ return new DataSourceProxyXA(druidDataSource);
+ }
+
+
+ @Bean
+ public SqlSessionFactory sqlSessionFactory(DataSource dataSourceProxy)throws Exception{
+ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
+ sqlSessionFactoryBean.setDataSource(dataSourceProxy);
+// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
+// .getResources("classpath*:/mapper/*.xml"));
+// sqlSessionFactoryBean.setTypeAliasesPackage("io.seata.sample.entity");
+ sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
+ return sqlSessionFactoryBean.getObject();
+ }
+
+}
\ No newline at end of file
diff --git a/seata-xa/order/src/main/java/org/javaboy/order/controller/OrderController.java b/seata-xa/order/src/main/java/org/javaboy/order/controller/OrderController.java
new file mode 100644
index 0000000..157d03e
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/order/controller/OrderController.java
@@ -0,0 +1,32 @@
+package org.javaboy.order.controller;
+
+import org.javaboy.common.RespBean;
+import org.javaboy.order.service.OrderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class OrderController {
+ @Autowired
+ OrderService orderService;
+
+ @PostMapping("/createOrder")
+ public RespBean createOrder(@RequestParam("acount") String account, @RequestParam("count") Integer count, @RequestParam("productId") String productId) {
+ if (orderService.createOrder(account, productId, count)) {
+ return RespBean.ok("下单成功");
+ }
+ return RespBean.error("下单失败");
+ }
+}
diff --git a/seata-xa/order/src/main/java/org/javaboy/order/feign/AccountFeign.java b/seata-xa/order/src/main/java/org/javaboy/order/feign/AccountFeign.java
new file mode 100644
index 0000000..70c0710
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/order/feign/AccountFeign.java
@@ -0,0 +1,22 @@
+package org.javaboy.order.feign;
+
+import org.apache.ibatis.annotations.Param;
+import org.javaboy.common.RespBean;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@FeignClient("account")
+public interface AccountFeign {
+ @PostMapping("/deductAccount")
+ RespBean deductAccount(@RequestParam("account") String account, @RequestParam("money") Double money);
+}
diff --git a/seata-xa/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java b/seata-xa/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java
new file mode 100644
index 0000000..2aaee20
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/order/mapper/OrderMapper.java
@@ -0,0 +1,20 @@
+package org.javaboy.order.mapper;
+
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Mapper
+public interface OrderMapper {
+ @Insert("insert into order_tbl(user_id,commodity_code,count) values(#{account},#{productId},#{count})")
+ int createOrder(@Param("account") String account, @Param("productId") String productId, @Param("count") Integer count);
+}
diff --git a/seata-xa/order/src/main/java/org/javaboy/order/service/OrderService.java b/seata-xa/order/src/main/java/org/javaboy/order/service/OrderService.java
new file mode 100644
index 0000000..ac5e25d
--- /dev/null
+++ b/seata-xa/order/src/main/java/org/javaboy/order/service/OrderService.java
@@ -0,0 +1,32 @@
+package org.javaboy.order.service;
+
+import org.javaboy.common.RespBean;
+import org.javaboy.order.feign.AccountFeign;
+import org.javaboy.order.mapper.OrderMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class OrderService {
+
+ @Autowired
+ OrderMapper orderMapper;
+ @Autowired
+ AccountFeign accountFeign;
+
+ public boolean createOrder(String account, String productId, Integer count) {
+ //扣款,每件商品 100 块钱
+ RespBean respBean = accountFeign.deductAccount(account, count * 100.0);
+ int order = orderMapper.createOrder(account, productId, count);
+ return order == 1 && respBean.getStatus() == 200;
+ }
+}
diff --git a/seata-xa/order/src/main/resources/application.properties b/seata-xa/order/src/main/resources/application.properties
new file mode 100644
index 0000000..f542522
--- /dev/null
+++ b/seata-xa/order/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+server.port=1113
+spring.application.name=order
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+
+spring.datasource.username=root
+spring.datasource.password=123
+spring.datasource.url=jdbc:mysql:///xa_order?serverTimezone=Asia/Shanghai&useSSL=false
+#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
+#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
+seata.enable-auto-data-source-proxy=false
\ No newline at end of file
diff --git a/seata-xa/order/src/main/resources/file.conf b/seata-xa/order/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-xa/order/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-xa/order/src/main/resources/registry.conf b/seata-xa/order/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-xa/order/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-xa/order/src/test/java/org/javaboy/order/OrderApplicationTests.java b/seata-xa/order/src/test/java/org/javaboy/order/OrderApplicationTests.java
new file mode 100644
index 0000000..e4b4b00
--- /dev/null
+++ b/seata-xa/order/src/test/java/org/javaboy/order/OrderApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.order;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class OrderApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/seata-xa/pom.xml b/seata-xa/pom.xml
new file mode 100644
index 0000000..12cf1bb
--- /dev/null
+++ b/seata-xa/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+ org.javaboy
+ seata-xa
+ pom
+ 1.0-SNAPSHOT
+
+ common
+
+
+
+
\ No newline at end of file
diff --git a/seata-xa/storage/pom.xml b/seata-xa/storage/pom.xml
new file mode 100644
index 0000000..d3dc71e
--- /dev/null
+++ b/seata-xa/storage/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+ org.javaboy
+ storage
+ 0.0.1-SNAPSHOT
+
+ Demo project for Spring Boot
+
+
+ 1.8
+ UTF-8
+ UTF-8
+ 2.3.7.RELEASE
+ 2.2.2.RELEASE
+ Hoxton.SR9
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.4
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+ common
+ org.javaboy
+ 1.0-SNAPSHOT
+
+
+ mysql
+ mysql-connector-java
+ runtime
+ 8.0.11
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.3.7.RELEASE
+
+
+
+
+
diff --git a/seata-xa/storage/src/main/java/org/javaboy/StorageApplication.java b/seata-xa/storage/src/main/java/org/javaboy/StorageApplication.java
new file mode 100644
index 0000000..2a943d4
--- /dev/null
+++ b/seata-xa/storage/src/main/java/org/javaboy/StorageApplication.java
@@ -0,0 +1,16 @@
+package org.javaboy;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
+@MapperScan(basePackages = "org.javaboy.storage.mapper")
+public class StorageApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(StorageApplication.class, args);
+ }
+
+}
diff --git a/seata-xa/storage/src/main/java/org/javaboy/storage/config/DataSourceConfiguration.java b/seata-xa/storage/src/main/java/org/javaboy/storage/config/DataSourceConfiguration.java
new file mode 100644
index 0000000..69bc74d
--- /dev/null
+++ b/seata-xa/storage/src/main/java/org/javaboy/storage/config/DataSourceConfiguration.java
@@ -0,0 +1,51 @@
+package org.javaboy.storage.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.seata.rm.datasource.xa.DataSourceProxyXA;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.sql.DataSource;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Configuration
+public class DataSourceConfiguration {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DruidDataSource druidDataSource() {
+ return new DruidDataSource();
+ }
+
+ @Bean("dataSourceProxy")
+ @Primary
+ public DataSource dataSource(DruidDataSource druidDataSource) {
+ return new DataSourceProxyXA(druidDataSource);
+ }
+
+
+ @Bean
+ public SqlSessionFactory sqlSessionFactory(DataSource dataSourceProxy)throws Exception{
+ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
+ sqlSessionFactoryBean.setDataSource(dataSourceProxy);
+// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
+// .getResources("classpath*:/mapper/*.xml"));
+// sqlSessionFactoryBean.setTypeAliasesPackage("io.seata.sample.entity");
+ sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
+ return sqlSessionFactoryBean.getObject();
+ }
+
+}
\ No newline at end of file
diff --git a/seata-xa/storage/src/main/java/org/javaboy/storage/controller/StorageController.java b/seata-xa/storage/src/main/java/org/javaboy/storage/controller/StorageController.java
new file mode 100644
index 0000000..2361f14
--- /dev/null
+++ b/seata-xa/storage/src/main/java/org/javaboy/storage/controller/StorageController.java
@@ -0,0 +1,32 @@
+package org.javaboy.storage.controller;
+
+import org.javaboy.common.RespBean;
+import org.javaboy.storage.service.StorageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class StorageController {
+
+ @Autowired
+ StorageService storageService;
+
+ @PostMapping("/deduct")
+ public RespBean deduct(@RequestParam("productId") String productId, @RequestParam("count") Integer count) {
+ if (storageService.deduct(productId, count)) {
+ return RespBean.ok("扣库存成功");
+ }
+ return RespBean.error("扣库存失败");
+ }
+}
diff --git a/seata-xa/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java b/seata-xa/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java
new file mode 100644
index 0000000..65f2668
--- /dev/null
+++ b/seata-xa/storage/src/main/java/org/javaboy/storage/mapper/StorageMapper.java
@@ -0,0 +1,24 @@
+package org.javaboy.storage.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+//@Mapper
+public interface StorageMapper {
+ @Update("update storage_tbl set count=count-#{count} where commodity_code=#{productId}")
+ int deduct(@Param("productId") String productId, @Param("count") Integer count);
+
+ @Select("select count from storage_tbl where commodity_code=#{productId}")
+ int getCountByProductId(String productId);
+}
diff --git a/seata-xa/storage/src/main/java/org/javaboy/storage/service/StorageService.java b/seata-xa/storage/src/main/java/org/javaboy/storage/service/StorageService.java
new file mode 100644
index 0000000..5613d01
--- /dev/null
+++ b/seata-xa/storage/src/main/java/org/javaboy/storage/service/StorageService.java
@@ -0,0 +1,31 @@
+package org.javaboy.storage.service;
+
+import org.javaboy.storage.mapper.StorageMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Service
+public class StorageService {
+ @Autowired
+ StorageMapper storageMapper;
+
+ public boolean deduct(String productId, Integer count) {
+ System.out.println("productId = " + productId);
+ System.out.println("count = " + count);
+ int c = storageMapper.getCountByProductId(productId);
+ if (c < count) {
+ throw new RuntimeException("库存不足,扣库存失败");
+ }
+ int deduct = storageMapper.deduct(productId, count);
+ return deduct == 1;
+ }
+}
diff --git a/seata-xa/storage/src/main/resources/application.properties b/seata-xa/storage/src/main/resources/application.properties
new file mode 100644
index 0000000..9527981
--- /dev/null
+++ b/seata-xa/storage/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+server.port=1114
+spring.application.name=storage
+eureka.client.service-url.defaultZone=http://localhost:8761/eureka
+
+spring.datasource.username=root
+spring.datasource.password=123
+spring.datasource.url=jdbc:mysql:///xa_storage?serverTimezone=Asia/Shanghai&useSSL=false
+#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
+#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+
+spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
+seata.enable-auto-data-source-proxy=false
\ No newline at end of file
diff --git a/seata-xa/storage/src/main/resources/file.conf b/seata-xa/storage/src/main/resources/file.conf
new file mode 100755
index 0000000..e38ee82
--- /dev/null
+++ b/seata-xa/storage/src/main/resources/file.conf
@@ -0,0 +1,66 @@
+transport {
+ # tcp udt unix-domain-socket
+ type = "TCP"
+ #NIO NATIVE
+ server = "NIO"
+ #enable heartbeat
+ heartbeat = true
+ # the client batch send request enable
+ enableClientBatchSendRequest = true
+ #thread factory for netty
+ threadFactory {
+ bossThreadPrefix = "NettyBoss"
+ workerThreadPrefix = "NettyServerNIOWorker"
+ serverExecutorThread-prefix = "NettyServerBizHandler"
+ shareBossWorker = false
+ clientSelectorThreadPrefix = "NettyClientSelector"
+ clientSelectorThreadSize = 1
+ clientWorkerThreadPrefix = "NettyClientWorkerThread"
+ # netty boss thread size,will not be used for UDT
+ bossThreadSize = 1
+ #auto default pin or 8
+ workerThreadSize = "default"
+ }
+ shutdown {
+ # when destroy server, wait seconds
+ wait = 3
+ }
+ serialization = "seata"
+ compressor = "none"
+}
+service {
+ #transaction service group mapping
+ vgroupMapping.my_test_tx_group = "default"
+ #only support when registry.type=file, please don't set multiple addresses
+ default.grouplist = "127.0.0.1:8091"
+ #degrade, current not support
+ enableDegrade = false
+ #disable seata
+ disableGlobalTransaction = false
+}
+
+client {
+ rm {
+ asyncCommitBufferLimit = 10000
+ lock {
+ retryInterval = 10
+ retryTimes = 30
+ retryPolicyBranchRollbackOnConflict = true
+ }
+ reportRetryCount = 5
+ tableMetaCheckEnable = false
+ reportSuccessEnable = false
+ }
+ tm {
+ commitRetryCount = 5
+ rollbackRetryCount = 5
+ }
+ undo {
+ dataValidation = true
+ logSerialization = "jackson"
+ logTable = "undo_log"
+ }
+ log {
+ exceptionRate = 100
+ }
+}
\ No newline at end of file
diff --git a/seata-xa/storage/src/main/resources/registry.conf b/seata-xa/storage/src/main/resources/registry.conf
new file mode 100755
index 0000000..7f2236f
--- /dev/null
+++ b/seata-xa/storage/src/main/resources/registry.conf
@@ -0,0 +1,79 @@
+registry {
+ # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
+ type = "eureka"
+
+ nacos {
+ application = "seata-server"
+ serverAddr = "localhost"
+ namespace = ""
+ username = ""
+ password = ""
+ }
+ eureka {
+ serviceUrl = "http://localhost:8761/eureka"
+ weight = "1"
+ }
+ redis {
+ serverAddr = "localhost:6379"
+ db = "0"
+ password = ""
+ timeout = "0"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ sofa {
+ serverAddr = "127.0.0.1:9603"
+ region = "DEFAULT_ZONE"
+ datacenter = "DefaultDataCenter"
+ group = "SEATA_GROUP"
+ addressWaitTime = "3000"
+ }
+ file {
+ name = "file.conf"
+ }
+}
+
+config {
+ # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
+ type = "file"
+
+ nacos {
+ serverAddr = "localhost"
+ namespace = ""
+ group = "SEATA_GROUP"
+ username = ""
+ password = ""
+ }
+ consul {
+ serverAddr = "127.0.0.1:8500"
+ }
+ apollo {
+ appId = "seata-server"
+ apolloMeta = "http://192.168.1.204:8801"
+ namespace = "application"
+ }
+ zk {
+ serverAddr = "127.0.0.1:2181"
+ sessionTimeout = 6000
+ connectTimeout = 2000
+ username = ""
+ password = ""
+ }
+ etcd3 {
+ serverAddr = "http://localhost:2379"
+ }
+ file {
+ name = "file.conf"
+ }
+}
diff --git a/seata-xa/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java b/seata-xa/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java
new file mode 100644
index 0000000..0a32d95
--- /dev/null
+++ b/seata-xa/storage/src/test/java/org/javaboy/storage/StorageApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.storage;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class StorageApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/security_demo_new/pom.xml b/security_demo_new/pom.xml
new file mode 100644
index 0000000..b575c0c
--- /dev/null
+++ b/security_demo_new/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.0
+
+
+ org.javaboy
+ security_demo_new
+ 0.0.1-SNAPSHOT
+ security_demo_new
+ Demo project for Spring Boot
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/security_demo_new/src/main/java/org/javaboy/security_demo_new/SecurityDemoNewApplication.java b/security_demo_new/src/main/java/org/javaboy/security_demo_new/SecurityDemoNewApplication.java
new file mode 100644
index 0000000..13076b8
--- /dev/null
+++ b/security_demo_new/src/main/java/org/javaboy/security_demo_new/SecurityDemoNewApplication.java
@@ -0,0 +1,13 @@
+package org.javaboy.security_demo_new;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SecurityDemoNewApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SecurityDemoNewApplication.class, args);
+ }
+
+}
diff --git a/security_demo_new/src/main/java/org/javaboy/security_demo_new/config/SecurityConfig.java b/security_demo_new/src/main/java/org/javaboy/security_demo_new/config/SecurityConfig.java
new file mode 100644
index 0000000..5fb8de1
--- /dev/null
+++ b/security_demo_new/src/main/java/org/javaboy/security_demo_new/config/SecurityConfig.java
@@ -0,0 +1,56 @@
+package org.javaboy.security_demo_new.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.servlet.Filter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@Configuration
+public class SecurityConfig {
+
+ @Bean
+ UserDetailsService userDetailsService() {
+ InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
+ users.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
+ users.createUser(User.withUsername("江南一点雨").password("{noop}123").roles("admin").build());
+ return users;
+ }
+
+// @Bean
+ WebSecurityCustomizer webSecurityCustomizer() {
+ return web -> web.ignoring().antMatchers("/hello");
+ }
+
+ @Bean
+ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .permitAll()
+ .and()
+ .csrf().disable();
+ return http.build();
+ }
+
+}
diff --git a/security_demo_new/src/main/java/org/javaboy/security_demo_new/controller/HelloController.java b/security_demo_new/src/main/java/org/javaboy/security_demo_new/controller/HelloController.java
new file mode 100644
index 0000000..b9bbdb9
--- /dev/null
+++ b/security_demo_new/src/main/java/org/javaboy/security_demo_new/controller/HelloController.java
@@ -0,0 +1,22 @@
+package org.javaboy.security_demo_new.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author 江南一点雨
+ * @微信公众号 江南一点雨
+ * @网站 http://www.itboyhub.com
+ * @国际站 http://www.javaboy.org
+ * @微信 a_java_boy
+ * @GitHub https://github.com/lenve
+ * @Gitee https://gitee.com/lenve
+ */
+@RestController
+public class HelloController {
+
+ @GetMapping("/hello")
+ public String hello() {
+ return "hello 江南一点雨!";
+ }
+}
diff --git a/security_demo_new/src/main/resources/application.properties b/security_demo_new/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/security_demo_new/src/test/java/org/javaboy/security_demo_new/SecurityDemoNewApplicationTests.java b/security_demo_new/src/test/java/org/javaboy/security_demo_new/SecurityDemoNewApplicationTests.java
new file mode 100644
index 0000000..c734a6c
--- /dev/null
+++ b/security_demo_new/src/test/java/org/javaboy/security_demo_new/SecurityDemoNewApplicationTests.java
@@ -0,0 +1,13 @@
+package org.javaboy.security_demo_new;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SecurityDemoNewApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}