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 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() { + } + +}