Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
库存服务:对给定的商品扣除仓储数量。
订单服务:根据采购需求创建订单。
帐户服务:从用户帐户中扣除余额。
#初始化seata 的nacos配置
cd conf
sh nacos-config.sh 127.0.0.1
#启动seata-server
cd bin
seata-server.bat -p 8091 -h 127.0.0.1 -m db
/**
*
* Mapper 接口
*
*
* @author elite
* @since 2022-09-11
*/
@Mapper
public interface AccountMapper extends BaseMapper<Account> {
/**
* 更新账户余额
* @param user_id
* @param amt
* @return
*/
@Update("UPDATE seata_account.account SET acc_money= acc_money - #{amt} WHERE user_id = #{user_id} and (acc_money - #{amt}) >= 0")
boolean deductAcct(Integer user_id, BigDecimal amt);
}
/**
*
* 库存表 Mapper 接口
*
*
* @author elite
* @since 2022-09-11
*/
@Mapper
public interface StockMapper extends BaseMapper<Stock> {
@Update("UPDATE seata_stock.stock set stock_num = stock_num - #{product_num} WHERE product_id = #{product_id} and (stock_num - #{product_num}) >= 0")
boolean deuctStock(Integer product_id, Integer product_num);
}
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-seataartifactId>
<version>2.2.0.RELEASEversion>
dependency>
<dependency>
server:
port: 8087
spring:
application:
name: springcloud-seata-order
##nacos服务地址注册
cloud:
nacos:
discovery:
server-addr: 192.168.5.130:8848
enabled: true
alibaba:
seata:
tx-service-group: springcloud-seata-order
#配置数据库
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.5.130:3306/seata_order
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
druid:
# 初始化大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间(毫秒)
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置有一个连接在连接池中的最小生存时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
filters: stat, wall, log4j
# 通过connectProperties属性来打开mergeSql功能,慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#mybatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
/**
*
* 订单表 前端控制器
*
*
* @author elite
* @since 2022-09-11
*/
@RestController
@RequestMapping("/springcloud/order")
public class OrderController {
@Autowired
IOrderService orderService;
/**
* 传入用户id 商品id下单
* @param product_id
* @param user_id
* @return
*/
@GetMapping("/createOrder/{product_id}/{user_id}/{product_num}")
public R createOrder(@PathVariable("")Integer product_id,
@PathVariable("user_id") Integer user_id,
@PathVariable("product_num")Integer product_num){
R r = orderService.createOrder(product_id,user_id,product_num);
return r;
}
}
/**
*
* 订单表 服务实现类
*
*
* @author elite
* @since 2022-09-06
*/
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Autowired
OrderMapper orderMapper;
//库存服务
@Autowired
StockService stockService;
//账户服务
@Autowired
AcctService acctService;
/**
* 下单服务
* @param product_id
* @param user_id
* @return
*/
@GlobalTransactional
@Override
public R createOrder(Integer product_id, Integer user_id,Integer product_num) {
//库存服务:对给定的商品扣除仓储数量。
Product product = stockService.getProductById(product_id);
if (product == null){
return R.fail(400,"商品信息为空,商品ID不能为空");
}
boolean deductSuccess = stockService.deductStock(product_id, product_num);
if (!deductSuccess){
return R.fail(400,"扣减库存失败!");
}
//验证用户
User user = acctService.getUserByUseId(user_id);
if (user == null){
return R.fail(400,"传入的用户ID不存在");
}
//订单服务:根据采购需求创建订单。
BigDecimal amt = (product.getProductPrice()).multiply(new BigDecimal(product_num ));
//订单信息
Order order = new Order();
order.setOrderNo(10);
order.setProductId(product_id);
order.setUserId(user.getUserId());
order.setOrderNum(product_num);
order.setOrderAmt(amt); //单价*数量
order.setOrderStatus("下单");
order.setPayStatus("支付成功");
order.setCreateBy("牛奶糖");
order.setUpdateBy("牛奶糖");
orderMapper.insert(order);
//模拟异常
//int i = 1/0;
//帐户服务:从用户帐户中扣除余额。
boolean deductAcctFlag = acctService.deductAcct(user_id, amt);
if (!deductAcctFlag){
return R.fail(400,"扣减账户失败!");
}
return R.ok(200,"下单成功!",order);
}
}
执行的sql
==> Preparing: UPDATE seata_stock.stock set stock_num = stock_num - ? WHERE product_id = ? and (stock_num - ?) >= 0