mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-04-23 02:10:32 +00:00
fix(seata): 修复seata示例项目余额不足时没有正确回滚库存的问题 fix #9287
This commit is contained in:
parent
cf7eeac6ab
commit
03ee0616a5
@ -1,6 +1,7 @@
|
|||||||
package org.jeecg.modules.test.seata.account.controller;
|
package org.jeecg.modules.test.seata.account.controller;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.modules.test.seata.account.service.SeataAccountService;
|
import org.jeecg.modules.test.seata.account.service.SeataAccountService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -20,7 +21,7 @@ public class SeataAccountController {
|
|||||||
private SeataAccountService accountService;
|
private SeataAccountService accountService;
|
||||||
|
|
||||||
@PostMapping("/reduceBalance")
|
@PostMapping("/reduceBalance")
|
||||||
public void reduceBalance(Long userId, BigDecimal amount) {
|
public Result<?> reduceBalance(Long userId, BigDecimal amount) {
|
||||||
accountService.reduceBalance(userId, amount);
|
return accountService.reduceBalance(userId, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package org.jeecg.modules.test.seata.account.service;
|
package org.jeecg.modules.test.seata.account.service;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,5 +16,5 @@ public interface SeataAccountService {
|
|||||||
* @param userId 用户 ID
|
* @param userId 用户 ID
|
||||||
* @param amount 扣减金额
|
* @param amount 扣减金额
|
||||||
*/
|
*/
|
||||||
void reduceBalance(Long userId, BigDecimal amount);
|
Result<?> reduceBalance(Long userId, BigDecimal amount);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.baomidou.dynamic.datasource.annotation.DS;
|
|||||||
import io.seata.core.context.RootContext;
|
import io.seata.core.context.RootContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.modules.test.seata.account.entity.SeataAccount;
|
import org.jeecg.modules.test.seata.account.entity.SeataAccount;
|
||||||
import org.jeecg.modules.test.seata.account.mapper.SeataAccountMapper;
|
import org.jeecg.modules.test.seata.account.mapper.SeataAccountMapper;
|
||||||
import org.jeecg.modules.test.seata.account.service.SeataAccountService;
|
import org.jeecg.modules.test.seata.account.service.SeataAccountService;
|
||||||
@ -34,7 +35,7 @@ public class SeataAccountServiceImpl implements SeataAccountService {
|
|||||||
@DS("account")
|
@DS("account")
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
|
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
|
||||||
public void reduceBalance(Long userId, BigDecimal amount) {
|
public Result<?> reduceBalance(Long userId, BigDecimal amount) {
|
||||||
log.info("xid:"+ RootContext.getXID());
|
log.info("xid:"+ RootContext.getXID());
|
||||||
log.info("=============ACCOUNT START=================");
|
log.info("=============ACCOUNT START=================");
|
||||||
SeataAccount account = accountMapper.selectById(userId);
|
SeataAccount account = accountMapper.selectById(userId);
|
||||||
@ -44,7 +45,7 @@ public class SeataAccountServiceImpl implements SeataAccountService {
|
|||||||
|
|
||||||
if (balance.compareTo(amount)==-1) {
|
if (balance.compareTo(amount)==-1) {
|
||||||
log.warn("用户 {} 余额不足,当前余额:{}", userId, balance);
|
log.warn("用户 {} 余额不足,当前余额:{}", userId, balance);
|
||||||
throw new RuntimeException("余额不足");
|
return Result.error("余额不足");
|
||||||
}
|
}
|
||||||
log.info("开始扣减用户 {} 余额", userId);
|
log.info("开始扣减用户 {} 余额", userId);
|
||||||
BigDecimal currentBalance = account.getBalance().subtract(amount);
|
BigDecimal currentBalance = account.getBalance().subtract(amount);
|
||||||
@ -52,5 +53,6 @@ public class SeataAccountServiceImpl implements SeataAccountService {
|
|||||||
accountMapper.updateById(account);
|
accountMapper.updateById(account);
|
||||||
log.info("扣减用户 {} 余额成功,扣减后用户账户余额为{}", userId, currentBalance);
|
log.info("扣减用户 {} 余额成功,扣减后用户账户余额为{}", userId, currentBalance);
|
||||||
log.info("=============ACCOUNT END=================");
|
log.info("=============ACCOUNT END=================");
|
||||||
|
return Result.OK();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ spring:
|
|||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
##redis 单机环境配置
|
##redis 单机环境配置
|
||||||
host: localhost
|
host: jeecg-boot-redis
|
||||||
port: 6379
|
port: 6379
|
||||||
database: 0
|
database: 0
|
||||||
password:
|
password:
|
||||||
@ -22,7 +22,7 @@ spring:
|
|||||||
autoconfigure:
|
autoconfigure:
|
||||||
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://127.0.0.1:3306/jeecg_account?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
|
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg_account?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
@ -30,7 +30,7 @@ spring:
|
|||||||
init:
|
init:
|
||||||
schema-locations: classpath:sql/schema-account.sql
|
schema-locations: classpath:sql/schema-account.sql
|
||||||
seata:
|
seata:
|
||||||
enable-auto-data-source-proxy: false
|
enable-auto-data-source-proxy: true
|
||||||
service:
|
service:
|
||||||
grouplist:
|
grouplist:
|
||||||
default: 127.0.0.1:8091
|
default: 127.0.0.1:8091
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jeecg.modules.test.seata.order.feign;
|
package org.jeecg.modules.test.seata.order.feign;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -19,5 +20,5 @@ public interface AccountClient {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/test/seata/account/reduceBalance")
|
@PostMapping("/test/seata/account/reduceBalance")
|
||||||
String reduceBalance(@RequestParam("userId") Long userId, @RequestParam("amount") BigDecimal amount);
|
Result<?> reduceBalance(@RequestParam("userId") Long userId, @RequestParam("amount") BigDecimal amount);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jeecg.modules.test.seata.order.feign;
|
package org.jeecg.modules.test.seata.order.feign;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -21,5 +22,5 @@ public interface ProductClient {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/test/seata/product/reduceStock")
|
@PostMapping("/test/seata/product/reduceStock")
|
||||||
BigDecimal reduceStock(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
|
Result<BigDecimal> reduceStock(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import com.baomidou.dynamic.datasource.annotation.DS;
|
|||||||
import io.seata.core.context.RootContext;
|
import io.seata.core.context.RootContext;
|
||||||
import io.seata.spring.annotation.GlobalTransactional;
|
import io.seata.spring.annotation.GlobalTransactional;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.exception.JeecgBootBizTipException;
|
||||||
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
import org.jeecg.modules.test.seata.order.dto.PlaceOrderRequest;
|
import org.jeecg.modules.test.seata.order.dto.PlaceOrderRequest;
|
||||||
import org.jeecg.modules.test.seata.order.entity.SeataOrder;
|
import org.jeecg.modules.test.seata.order.entity.SeataOrder;
|
||||||
import org.jeecg.modules.test.seata.order.enums.OrderStatus;
|
import org.jeecg.modules.test.seata.order.enums.OrderStatus;
|
||||||
@ -59,13 +62,20 @@ public class SeataOrderServiceImpl implements SeataOrderService {
|
|||||||
orderMapper.insert(order);
|
orderMapper.insert(order);
|
||||||
log.info("订单一阶段生成,等待扣库存付款中");
|
log.info("订单一阶段生成,等待扣库存付款中");
|
||||||
// 扣减库存并计算总价
|
// 扣减库存并计算总价
|
||||||
BigDecimal amount = productClient.reduceStock(productId, count);
|
Result<BigDecimal> productRes = productClient.reduceStock(productId, count);
|
||||||
|
if (!productRes.isSuccess()) {
|
||||||
|
String message = productRes.getMessage();
|
||||||
|
message = oConvertUtils.isEmpty(message) ? "操作失败" : message;
|
||||||
|
throw new JeecgBootBizTipException(message);
|
||||||
|
}
|
||||||
|
BigDecimal amount = productRes.getResult();
|
||||||
// 扣减余额
|
// 扣减余额
|
||||||
String str = accountClient.reduceBalance(userId, amount);
|
Result<?> accountRes = accountClient.reduceBalance(userId, amount);
|
||||||
// feign响应被二次封装,判断使主事务回滚
|
// feign响应被二次封装,判断使主事务回滚
|
||||||
JSONObject jsonObject = JSONObject.parseObject(str);
|
if (!accountRes.isSuccess()) {
|
||||||
if (jsonObject.getInteger("code") != 200) {
|
String message = accountRes.getMessage();
|
||||||
throw new RuntimeException();
|
message = oConvertUtils.isEmpty(message) ? "操作失败" : message;
|
||||||
|
throw new JeecgBootBizTipException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
order.setStatus(OrderStatus.SUCCESS);
|
order.setStatus(OrderStatus.SUCCESS);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ spring:
|
|||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
##redis 单机环境配置
|
##redis 单机环境配置
|
||||||
host: localhost
|
host: jeecg-boot-redis
|
||||||
port: 6379
|
port: 6379
|
||||||
database: 0
|
database: 0
|
||||||
password:
|
password:
|
||||||
@ -23,14 +23,14 @@ spring:
|
|||||||
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://127.0.0.1:3306/jeecg_order?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
|
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg_order?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
sql:
|
sql:
|
||||||
init:
|
init:
|
||||||
schema-locations: classpath:sql/schema-order.sql
|
schema-locations: classpath:sql/schema-order.sql
|
||||||
seata:
|
seata:
|
||||||
enable-auto-data-source-proxy: false
|
enable-auto-data-source-proxy: true
|
||||||
service:
|
service:
|
||||||
grouplist:
|
grouplist:
|
||||||
default: 127.0.0.1:8091
|
default: 127.0.0.1:8091
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.jeecg.modules.test.seata.product.controller;
|
package org.jeecg.modules.test.seata.product.controller;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.modules.test.seata.product.service.SeataProductService;
|
import org.jeecg.modules.test.seata.product.service.SeataProductService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -20,7 +21,7 @@ public class SeataProductController {
|
|||||||
private SeataProductService seataProductService;
|
private SeataProductService seataProductService;
|
||||||
|
|
||||||
@PostMapping("/reduceStock")
|
@PostMapping("/reduceStock")
|
||||||
public BigDecimal reduceStock(Long productId, Integer count, HttpServletRequest request) {
|
public Result<BigDecimal> reduceStock(Long productId, Integer count, HttpServletRequest request) {
|
||||||
return seataProductService.reduceStock(productId, count);
|
return seataProductService.reduceStock(productId, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package org.jeecg.modules.test.seata.product.service;
|
package org.jeecg.modules.test.seata.product.service;
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,5 +18,5 @@ public interface SeataProductService {
|
|||||||
* @param count 扣减数量
|
* @param count 扣减数量
|
||||||
* @return 商品总价
|
* @return 商品总价
|
||||||
*/
|
*/
|
||||||
BigDecimal reduceStock(Long productId, Integer count);
|
Result<BigDecimal> reduceStock(Long productId, Integer count);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import io.seata.core.context.RootContext;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.modules.test.seata.product.entity.SeataProduct;
|
import org.jeecg.modules.test.seata.product.entity.SeataProduct;
|
||||||
import org.jeecg.modules.test.seata.product.mapper.SeataProductMapper;
|
import org.jeecg.modules.test.seata.product.mapper.SeataProductMapper;
|
||||||
import org.jeecg.modules.test.seata.product.service.SeataProductService;
|
import org.jeecg.modules.test.seata.product.service.SeataProductService;
|
||||||
@ -35,7 +36,7 @@ public class SeataProductServiceImpl implements SeataProductService {
|
|||||||
@DS("product")
|
@DS("product")
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
|
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal reduceStock(Long productId, Integer count) {
|
public Result<BigDecimal> reduceStock(Long productId, Integer count) {
|
||||||
log.info("xid:"+ RootContext.getXID());
|
log.info("xid:"+ RootContext.getXID());
|
||||||
log.info("=============PRODUCT START=================");
|
log.info("=============PRODUCT START=================");
|
||||||
// 检查库存
|
// 检查库存
|
||||||
@ -46,7 +47,7 @@ public class SeataProductServiceImpl implements SeataProductService {
|
|||||||
|
|
||||||
if (stock < count) {
|
if (stock < count) {
|
||||||
log.warn("商品编号为{} 库存不足,当前库存:{}", productId, stock);
|
log.warn("商品编号为{} 库存不足,当前库存:{}", productId, stock);
|
||||||
throw new RuntimeException("库存不足");
|
return Result.error("库存不足");
|
||||||
}
|
}
|
||||||
log.info("开始扣减商品编号为 {} 库存,单价商品价格为{}", productId, product.getPrice());
|
log.info("开始扣减商品编号为 {} 库存,单价商品价格为{}", productId, product.getPrice());
|
||||||
// 扣减库存
|
// 扣减库存
|
||||||
@ -56,6 +57,6 @@ public class SeataProductServiceImpl implements SeataProductService {
|
|||||||
BigDecimal totalPrice = product.getPrice().multiply(new BigDecimal(count));
|
BigDecimal totalPrice = product.getPrice().multiply(new BigDecimal(count));
|
||||||
log.info("扣减商品编号为 {} 库存成功,扣减后库存为{}, {} 件商品总价为 {} ", productId, currentStock, count, totalPrice);
|
log.info("扣减商品编号为 {} 库存成功,扣减后库存为{}, {} 件商品总价为 {} ", productId, currentStock, count, totalPrice);
|
||||||
log.info("=============PRODUCT END=================");
|
log.info("=============PRODUCT END=================");
|
||||||
return totalPrice;
|
return Result.OK(totalPrice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,15 @@
|
|||||||
server:
|
server:
|
||||||
port: 5003
|
port: 5003
|
||||||
spring:
|
spring:
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
##redis 单机环境配置
|
||||||
|
host: jeecg-boot-redis
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
password:
|
||||||
|
ssl:
|
||||||
|
enabled: false
|
||||||
application:
|
application:
|
||||||
name: seata-product
|
name: seata-product
|
||||||
cloud:
|
cloud:
|
||||||
@ -14,14 +23,14 @@ spring:
|
|||||||
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
exclude: com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://127.0.0.1:3306/jeecg_product?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
|
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg_product?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
sql:
|
sql:
|
||||||
init:
|
init:
|
||||||
schema-locations: classpath:sql/schema-product.sql
|
schema-locations: classpath:sql/schema-product.sql
|
||||||
seata:
|
seata:
|
||||||
enable-auto-data-source-proxy: false
|
enable-auto-data-source-proxy: true
|
||||||
service:
|
service:
|
||||||
grouplist:
|
grouplist:
|
||||||
default: 127.0.0.1:8091
|
default: 127.0.0.1:8091
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user