业务需求
下订单->减库存->扣余额->改订单状
新建订单 Order- Module
新建库存 Storage -Module
新建账户 Account-Module
- 2001订单服务
1、创建订单接口
package com.laoxu.springcloud.controller;
import com.laoxu.springcloud.domain.Order;
import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/order/create")
public CommonResult create(Order order){
orderService.create(order);
return new CommonResult(200, "订单创建成功!");
}
}
2、服务层
package com.laoxu.springcloud.service.impl;
import com.laoxu.springcloud.dao.OrderDao;
import com.laoxu.springcloud.domain.Order;
import com.laoxu.springcloud.service.AccountService;
import com.laoxu.springcloud.service.OrderService;
import com.laoxu.springcloud.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private StorageService storageService;
@Autowired
private AccountService accountService;
@Override
public void create(Order order) {
log.info("--->开始创建订单");
// 1.创建订单
orderDao.create(order);
log.info("--->订单微服务开始调用库存,做扣减Count");
// 2.扣减库存
storageService.decrease(order.getProductId(), order.getCount());
log.info("--->订单微服务开始调用库存,做扣减end");
log.info("--->订单微服务开始调用账户,做扣减Money");
// 3.扣减账户
accountService.decrease(order.getUserId(), order.getMoney());
log.info("--->订单微服务开始调用账户,做扣减end");
// 4.修改订单的状态,从零到1,代表已经完成
log.info("--->修改订单状态开始");
orderDao.update(order.getUserId(), 0);
log.info("--->修改订单状态结束");
log.info("--->下单结束");
}
}
其中 StorageService 和 AccountService 为feign调用的微服务
- 2002库存服务
扣减库存接口
package com.laoxu.springcloud.controller;
import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StorageController {
@Autowired
private StorageService storageService;
@GetMapping("/storage/decrease")
public CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count){
storageService.decrease(productId, count);
return new CommonResult(200, "扣减库存成功!");
}
}
- 2003账户服务
扣减账户接口
package com.laoxu.springcloud.controller;
import com.laoxu.springcloud.entities.CommonResult;
import com.laoxu.springcloud.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
@RestController
public class AccountController {
@Autowired
private AccountService accountService;
@GetMapping("/account/decrease")
public CommonResult decrease(@RequestParam("userId") Long userId,@RequestParam("money") BigDecimal money){
accountService.decrease(userId, money);
return new CommonResult(200, "扣减账户成功!");
}
}
- 准备多个数据库
订单库
/*
Navicat MySQL Data Transfer
Source Server : 腾讯1G2核
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_order
Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001
Date: 25/10/2020 15:10:54
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id',
`product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id',
`count` int(11) NULL DEFAULT NULL COMMENT '数量',
`money` decimal(11, 0) NULL DEFAULT NULL COMMENT '金额',
`status` int(1) NULL DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
库存库
/*
Navicat MySQL Data Transfer
Source Server : 腾讯1G2核
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_store
Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001
Date: 25/10/2020 15:11:02
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_storage
-- ----------------------------
DROP TABLE IF EXISTS `t_storage`;
CREATE TABLE `t_storage` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id',
`total` int(11) NULL DEFAULT NULL COMMENT '总库存',
`used` int(11) NULL DEFAULT NULL COMMENT '已用库存',
`residue` int(11) NULL DEFAULT NULL COMMENT '剩余库存',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_storage
-- ----------------------------
INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100);
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
账户库
/*
Navicat MySQL Data Transfer
Source Server : 腾讯1G2核
Source Server Type : MySQL
Source Server Version : 50727
Source Host : 106.54.196.44:3306
Source Schema : seata_account
Target Server Type : MySQL
Target Server Version : 50727
File Encoding : 65001
Date: 25/10/2020 15:10:38
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id',
`total` decimal(10, 0) NULL DEFAULT NULL COMMENT '总额度',
`used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用余额',
`residue` decimal(10, 0) NULL DEFAULT NULL COMMENT '剩余可用额度',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, 1, 1000, 0, 1000);
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
创建好后的初始数据
- 测试创建订单接口
访问:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
订单表中生成一条订单记录
用户1购买了10件商品1花了100元,状态为1已完成
库存表中更新库存
商品1的库存减少了10件,还剩90件
账户表中更新账户
用户1的余额扣除100元,还剩900元
订单创建成功!
- 测试接口超时报错
在账户订单中添加延迟函数
由于openfeign默认超时时间是1秒,则调用时会报超时异常
- 查看数据库中情况
在订单表中插入了一条订单记录,此时状态为未支付
但是库存中的库存却已经被减去了
而且账户还被扣去了100元
- 此时在订单创建业务方法上方添加注解@GlobalTransactional 指定遇到哪些运行时异常回滚事务,这里是任何运行时异常Exception
- 此时再次访问:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
- 此时库中数据不变,因为事务被回滚,数据库不做任何操作