diff --git a/lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java b/lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java index 40e67811..1d0e633b 100644 --- a/lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java +++ b/lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java @@ -27,9 +27,9 @@ public class OrderServiceImpl implements OrderService { @Autowired private ProductService productService; - @GlobalTransactional @Override @DS(value = "order-ds") + @GlobalTransactional public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { Integer amount = 1; // 购买数量,暂时设置为 1。 diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/pom.xml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/pom.xml new file mode 100644 index 00000000..b803d0e2 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/pom.xml @@ -0,0 +1,61 @@ + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.2.RELEASE + + + 4.0.0 + + lab-52-seata-at-httpclient-demo-account-service + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + mysql + mysql-connector-java + 5.1.48 + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.2 + + + + + io.seata + seata-spring-boot-starter + 1.1.0 + + + + io.seata + seata-http + 1.1.0 + + + + + org.apache.httpcomponents + httpclient + 4.5.8 + + + + diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/AccountServiceApplication.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/AccountServiceApplication.java new file mode 100644 index 00000000..713a4c23 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/AccountServiceApplication.java @@ -0,0 +1,13 @@ +package cn.iocoder.springboot.lab52.accountservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AccountServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(AccountServiceApplication.class, args); + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/controller/AccountController.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/controller/AccountController.java new file mode 100644 index 00000000..dd0eed2b --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/controller/AccountController.java @@ -0,0 +1,36 @@ +package cn.iocoder.springboot.lab52.accountservice.controller; + +import cn.iocoder.springboot.lab52.accountservice.dto.AccountReduceBalanceDTO; +import cn.iocoder.springboot.lab52.accountservice.service.AccountService; +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; + +@RestController +@RequestMapping("/account") +public class AccountController { + + private Logger logger = LoggerFactory.getLogger(AccountController.class); + + @Autowired + private AccountService accountService; + + @PostMapping("/reduce-balance") + public Boolean reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO) { + logger.info("[reduceBalance] 收到减少余额请求, 用户:{}, 金额:{}", accountReduceBalanceDTO.getUserId(), + accountReduceBalanceDTO.getPrice()); + try { + accountService.reduceBalance(accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice()); + // 正常扣除余额,返回 true + return true; + } catch (Exception e) { + // 失败扣除余额,返回 false + return false; + } + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dao/AccountDao.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dao/AccountDao.java new file mode 100644 index 00000000..17ac5957 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dao/AccountDao.java @@ -0,0 +1,32 @@ +package cn.iocoder.springboot.lab52.accountservice.dao; + + +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.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface AccountDao { + + /** + * 获取账户余额 + * + * @param userId 用户 ID + * @return 账户余额 + */ + @Select("SELECT balance FROM account WHERE id = #{userId}") + Integer getBalance(@Param("userId") Long userId); + + /** + * 扣减余额 + * + * @param price 需要扣减的数目 + * @return 影响记录行数 + */ + @Update("UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}") + int reduceBalance(@Param("price") Integer price); + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dto/AccountReduceBalanceDTO.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dto/AccountReduceBalanceDTO.java new file mode 100644 index 00000000..be3ecdf5 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dto/AccountReduceBalanceDTO.java @@ -0,0 +1,36 @@ +package cn.iocoder.springboot.lab52.accountservice.dto; + +/** + * 账户减少余额 DTO + */ +public class AccountReduceBalanceDTO { + + /** + * 用户编号 + */ + private Long userId; + + /** + * 扣减金额 + */ + private Integer price; + + public Long getUserId() { + return userId; + } + + public AccountReduceBalanceDTO setUserId(Long userId) { + this.userId = userId; + return this; + } + + public Integer getPrice() { + return price; + } + + public AccountReduceBalanceDTO setPrice(Integer price) { + this.price = price; + return this; + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountService.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountService.java new file mode 100644 index 00000000..c5eb6668 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountService.java @@ -0,0 +1,17 @@ +package cn.iocoder.springboot.lab52.accountservice.service; + +/** + * 账户 Service + */ +public interface AccountService { + + /** + * 扣除余额 + * + * @param userId 用户编号 + * @param price 扣减金额 + * @throws Exception 失败时抛出异常 + */ + void reduceBalance(Long userId, Integer price) throws Exception; + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountServiceImpl.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountServiceImpl.java new file mode 100644 index 00000000..d63abb38 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountServiceImpl.java @@ -0,0 +1,47 @@ +package cn.iocoder.springboot.lab52.accountservice.service; + +import cn.iocoder.springboot.lab52.accountservice.dao.AccountDao; +import io.seata.core.context.RootContext; +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; + +@Service +public class AccountServiceImpl implements AccountService { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private AccountDao accountDao; + + @Override + @Transactional // 开启新事物 + public void reduceBalance(Long userId, Integer price) throws Exception { + logger.info("[reduceBalance] 当前 XID: {}", RootContext.getXID()); + + // 检查余额 + checkBalance(userId, price); + + logger.info("[reduceBalance] 开始扣减用户 {} 余额", userId); + // 扣除余额 + int updateCount = accountDao.reduceBalance(price); + // 扣除成功 + if (updateCount == 0) { + logger.warn("[reduceBalance] 扣除用户 {} 余额失败", userId); + throw new Exception("余额不足"); + } + logger.info("[reduceBalance] 扣除用户 {} 余额成功", userId); + } + + private void checkBalance(Long userId, Integer price) throws Exception { + logger.info("[checkBalance] 检查用户 {} 余额", userId); + Integer balance = accountDao.getBalance(userId); + if (balance < price) { + logger.warn("[checkBalance] 用户 {} 余额不足,当前余额:{}", userId, balance); + throw new Exception("余额不足"); + } + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/resources/application.yaml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/resources/application.yaml new file mode 100644 index 00000000..ea7faeae --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +server: + port: 8083 + +spring: + application: + name: account-service + + datasource: + url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + driver-class-name: com.mysql.jdbc.Driver + username: root + password: + +# Seata 配置项,对应 SeataProperties 类 +seata: + application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} + tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 + # 服务配置项,对应 ServiceProperties 类 + service: + # 虚拟组和分组的映射 + vgroup-mapping: + account-service-group: default + # 分组和 Seata 服务的映射 + grouplist: + default: 127.0.0.1:8091 diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/pom.xml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/pom.xml new file mode 100644 index 00000000..43a785c6 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/pom.xml @@ -0,0 +1,61 @@ + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.2.RELEASE + + + 4.0.0 + + lab-52-seata-at-httpclient-demo-order-service + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + mysql + mysql-connector-java + 5.1.48 + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.2 + + + + + io.seata + seata-spring-boot-starter + 1.1.0 + + + + io.seata + seata-http + 1.1.0 + + + + + org.apache.httpcomponents + httpclient + 4.5.8 + + + + diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/OrderServiceApplication.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/OrderServiceApplication.java new file mode 100644 index 00000000..7f9ae1b5 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/OrderServiceApplication.java @@ -0,0 +1,13 @@ +package cn.iocoder.springboot.lab52.orderservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrderServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(OrderServiceApplication.class, args); + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/controller/OrderController.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/controller/OrderController.java new file mode 100644 index 00000000..51f4154e --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/controller/OrderController.java @@ -0,0 +1,29 @@ +package cn.iocoder.springboot.lab52.orderservice.controller; + +import cn.iocoder.springboot.lab52.orderservice.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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/order") +public class OrderController { + + private Logger logger = LoggerFactory.getLogger(OrderController.class); + + @Autowired + private OrderService orderService; + + @PostMapping("/create") + public Integer createOrder(@RequestParam("userId") Long userId, + @RequestParam("productId") Long productId, + @RequestParam("price") Integer price) throws Exception { + logger.info("[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}", userId, productId, price); + return orderService.createOrder(userId, productId, price); + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/dao/OrderDao.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/dao/OrderDao.java new file mode 100644 index 00000000..6001ee4f --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/dao/OrderDao.java @@ -0,0 +1,23 @@ +package cn.iocoder.springboot.lab52.orderservice.dao; + +import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Options; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface OrderDao { + + /** + * 插入订单记录 + * + * @param order 订单 + * @return 影响记录数量 + */ + @Insert("INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})") + @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") + int saveOrder(OrderDO order); + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/entity/OrderDO.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/entity/OrderDO.java new file mode 100644 index 00000000..4ff509e4 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/entity/OrderDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.springboot.lab53.orderservice.entity; + +/** + * 订单实体 + */ +public class OrderDO { + + /** 订单编号 **/ + private Integer id; + + /** 用户编号 **/ + private Long userId; + + /** 产品编号 **/ + private Long productId; + + /** 支付金额 **/ + private Integer payAmount; + + public Integer getId() { + return id; + } + + public OrderDO setId(Integer id) { + this.id = id; + return this; + } + + public Long getUserId() { + return userId; + } + + public OrderDO setUserId(Long userId) { + this.userId = userId; + return this; + } + + public Long getProductId() { + return productId; + } + + public OrderDO setProductId(Long productId) { + this.productId = productId; + return this; + } + + public Integer getPayAmount() { + return payAmount; + } + + public OrderDO setPayAmount(Integer payAmount) { + this.payAmount = payAmount; + return this; + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderService.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderService.java new file mode 100644 index 00000000..1ca8d010 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderService.java @@ -0,0 +1,19 @@ +package cn.iocoder.springboot.lab52.orderservice.service; + +/** + * 订单 Service + */ +public interface OrderService { + + /** + * 创建订单 + * + * @param userId 用户编号 + * @param productId 产品编号 + * @param price 价格 + * @return 订单编号 + * @throws Exception 创建订单失败,抛出异常 + */ + Integer createOrder(Long userId, Long productId, Integer price) throws Exception; + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderServiceImpl.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderServiceImpl.java new file mode 100644 index 00000000..dd1d8c13 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderServiceImpl.java @@ -0,0 +1,76 @@ +package cn.iocoder.springboot.lab52.orderservice.service; + +import cn.iocoder.springboot.lab52.orderservice.dao.OrderDao; +import cn.iocoder.springboot.lab53.orderservice.entity.OrderDO; +import com.alibaba.fastjson.JSONObject; +import io.seata.core.context.RootContext; +import io.seata.integration.http.DefaultHttpExecutor; +import io.seata.spring.annotation.GlobalTransactional; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class OrderServiceImpl implements OrderService { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private OrderDao orderDao; + + @Override + @GlobalTransactional + public Integer createOrder(Long userId, Long productId, Integer price) throws Exception { + Integer amount = 1; // 购买数量,暂时设置为 1。 + + logger.info("[createOrder] 当前 XID: {}", RootContext.getXID()); + + // 扣减库存 + this.reduceStock(productId, amount); + + // 扣减余额 + this.reduceBalance(userId, price); + + // 保存订单 + OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price); + orderDao.saveOrder(order); + logger.info("[createOrder] 保存订单: {}", order.getId()); + + // 返回订单编号 + return order.getId(); + } + + private void reduceStock(Long productId, Integer amount) throws IOException { + // 参数拼接 + JSONObject params = new JSONObject().fluentPut("productId", String.valueOf(productId)) + .fluentPut("amount", String.valueOf(amount)); + // 执行调用 + HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:8082", "/product/reduce-stock", + params, HttpResponse.class); + // 解析结果 + Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity())); + if (!success) { + throw new RuntimeException("扣除库存失败"); + } + } + + private void reduceBalance(Long userId, Integer price) throws IOException { + // 参数拼接 + JSONObject params = new JSONObject().fluentPut("userId", String.valueOf(userId)) + .fluentPut("price", String.valueOf(price)); + // 执行调用 + HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:8083", "/account/reduce-balance", + params, HttpResponse.class); + // 解析结果 + Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity())); + if (!success) { + throw new RuntimeException("扣除余额失败"); + } + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/resources/application.yaml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/resources/application.yaml new file mode 100644 index 00000000..204bf93c --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +server: + port: 8081 # 端口 + +spring: + application: + name: order-service + + datasource: + url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + driver-class-name: com.mysql.jdbc.Driver + username: root + password: + +# Seata 配置项,对应 SeataProperties 类 +seata: + application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} + tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 + # 服务配置项,对应 ServiceProperties 类 + service: + # 虚拟组和分组的映射 + vgroup-mapping: + order-service-group: default + # 分组和 Seata 服务的映射 + grouplist: + default: 127.0.0.1:8091 diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/pom.xml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/pom.xml new file mode 100644 index 00000000..e9260443 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/pom.xml @@ -0,0 +1,55 @@ + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.2.RELEASE + + + 4.0.0 + + lab-52-seata-at-httpclient-demo-product-service + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + mysql + mysql-connector-java + 5.1.48 + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.2 + + + + + io.seata + seata-spring-boot-starter + 1.1.0 + + + + io.seata + seata-http + 1.1.0 + + + + + diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/ProductServiceApplication.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/ProductServiceApplication.java new file mode 100644 index 00000000..4666d4bf --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/ProductServiceApplication.java @@ -0,0 +1,13 @@ +package cn.iocoder.springboot.lab52.productservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ProductServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(ProductServiceApplication.class, args); + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/controller/ProductController.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/controller/ProductController.java new file mode 100644 index 00000000..9935006a --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/controller/ProductController.java @@ -0,0 +1,33 @@ +package cn.iocoder.springboot.lab52.productservice.controller; + +import cn.iocoder.springboot.lab52.productservice.dto.ProductReduceStockDTO; +import cn.iocoder.springboot.lab52.productservice.service.ProductService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/product") +public class ProductController { + + private Logger logger = LoggerFactory.getLogger(ProductController.class); + + @Autowired + private ProductService productService; + + @PostMapping("/reduce-stock") + public Boolean reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO) { + logger.info("[reduceStock] 收到减少库存请求, 商品:{}, 价格:{}", productReduceStockDTO.getProductId(), + productReduceStockDTO.getAmount()); + try { + productService.reduceStock(productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount()); + // 正常扣除库存,返回 true + return true; + } catch (Exception e) { + // 失败扣除库存,返回 false + return false; + } + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dao/ProductDao.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dao/ProductDao.java new file mode 100644 index 00000000..2b8ad712 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dao/ProductDao.java @@ -0,0 +1,33 @@ +package cn.iocoder.springboot.lab52.productservice.dao; + + +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.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ProductDao { + + /** + * 获取库存 + * + * @param productId 商品编号 + * @return 库存 + */ + @Select("SELECT stock FROM product WHERE id = #{productId}") + Integer getStock(@Param("productId") Long productId); + + /** + * 扣减库存 + * + * @param productId 商品编号 + * @param amount 扣减数量 + * @return 影响记录行数 + */ + @Update("UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}") + int reduceStock(@Param("productId") Long productId, @Param("amount") Integer amount); + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dto/ProductReduceStockDTO.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dto/ProductReduceStockDTO.java new file mode 100644 index 00000000..4d195de1 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dto/ProductReduceStockDTO.java @@ -0,0 +1,35 @@ +package cn.iocoder.springboot.lab52.productservice.dto; + +/** + * 商品减少库存 DTO + */ +public class ProductReduceStockDTO { + + /** + * 商品编号 + */ + private Long productId; + /** + * 数量 + */ + private Integer amount; + + public Long getProductId() { + return productId; + } + + public ProductReduceStockDTO setProductId(Long productId) { + this.productId = productId; + return this; + } + + public Integer getAmount() { + return amount; + } + + public ProductReduceStockDTO setAmount(Integer amount) { + this.amount = amount; + return this; + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductService.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductService.java new file mode 100644 index 00000000..388d408a --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductService.java @@ -0,0 +1,17 @@ +package cn.iocoder.springboot.lab52.productservice.service; + +/** + * 商品 Service + */ +public interface ProductService { + + /** + * 扣减库存 + * + * @param productId 商品 ID + * @param amount 扣减数量 + * @throws Exception 扣减失败时抛出异常 + */ + void reduceStock(Long productId, Integer amount) throws Exception; + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductServiceImpl.java b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductServiceImpl.java new file mode 100644 index 00000000..16899ed1 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductServiceImpl.java @@ -0,0 +1,48 @@ +package cn.iocoder.springboot.lab52.productservice.service; + +import cn.iocoder.springboot.lab52.productservice.dao.ProductDao; +import io.seata.core.context.RootContext; +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; + +@Service +public class ProductServiceImpl implements ProductService { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ProductDao productDao; + + @Override + @Transactional // 开启事物 + public void reduceStock(Long productId, Integer amount) throws Exception { + logger.info("[reduceStock] 当前 XID: {}", RootContext.getXID()); + + // 检查库存 + checkStock(productId, amount); + + logger.info("[reduceStock] 开始扣减 {} 库存", productId); + // 扣减库存 + int updateCount = productDao.reduceStock(productId, amount); + // 扣除成功 + if (updateCount == 0) { + logger.warn("[reduceStock] 扣除 {} 库存失败", productId); + throw new Exception("库存不足"); + } + // 扣除失败 + logger.info("[reduceStock] 扣除 {} 库存成功", productId); + } + + private void checkStock(Long productId, Integer requiredAmount) throws Exception { + logger.info("[checkStock] 检查 {} 库存", productId); + Integer stock = productDao.getStock(productId); + if (stock < requiredAmount) { + logger.warn("[checkStock] {} 库存不足,当前库存: {}", productId, stock); + throw new Exception("库存不足"); + } + } + +} diff --git a/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/resources/application.yaml b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/resources/application.yaml new file mode 100644 index 00000000..ac63d302 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +server: + port: 8082 # 端口 + +spring: + application: + name: product-service + + datasource: + url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + driver-class-name: com.mysql.jdbc.Driver + username: root + password: + +# Seata 配置项,对应 SeataProperties 类 +seata: + application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name} + tx-service-group: ${spring.application.name}-group # Seata 事务组编号,用于 TC 集群名 + # 服务配置项,对应 ServiceProperties 类 + service: + # 虚拟组和分组的映射 + vgroup-mapping: + product-service-group: default + # 分组和 Seata 服务的映射 + grouplist: + default: 127.0.0.1:8091 diff --git a/lab-52/lab-52-seata-at-httpclient-demo/pom.xml b/lab-52/lab-52-seata-at-httpclient-demo/pom.xml new file mode 100644 index 00000000..01b2c5d8 --- /dev/null +++ b/lab-52/lab-52-seata-at-httpclient-demo/pom.xml @@ -0,0 +1,20 @@ + + + + lab-52 + cn.iocoder.springboot.labs + 1.0-SNAPSHOT + + 4.0.0 + + lab-52-seata-at-httpclient-demo + + + lab-52-seata-at-httpclient-demo-order-service + lab-52-seata-at-httpclient-demo-product-service + lab-52-seata-at-httpclient-demo-account-service + + + diff --git a/lab-52/pom.xml b/lab-52/pom.xml index 65e4992d..e3525458 100644 --- a/lab-52/pom.xml +++ b/lab-52/pom.xml @@ -11,9 +11,10 @@ lab-52 pom + lab-52-multiple-datasource + lab-52-seata-at-httpclient-demo - diff --git a/lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml b/lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml index 81773e6c..b8616381 100644 --- a/lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml +++ b/lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml @@ -3,7 +3,7 @@ server: spring: application: - name: user-service + name: order-service datasource: url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8 @@ -35,7 +35,7 @@ seata: service: # 虚拟组和分组的映射 vgroup-mapping: - user-service-group: default + order-service-group: default # 分组和 Seata 服务的映射 grouplist: default: 127.0.0.1:8091