🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)🔥🔥🔥 有兴趣可以联系我
🔥🔥🔥 文末有往期免费源码,直接领取获取(无删减,无套路)
一、系统架构设计
graph TD
A[API Gateway] --> B[订单服务]
A --> C[库存服务]
B --> D[MySQL 订单库]
C --> E[MySQL 库存库]
F[Nacos] -->|服务注册发现| B
F -->|服务注册发现| C
G[Sentinel] -->|流量控制| B
G -->|流量控制| C
H[Seata] -->|分布式事务| B
H -->|分布式事务| C
二、服务划分与职责
服务名称 | 端口 | 职责描述 | 关键技术栈 |
---|---|---|---|
订单服务 | 8081 | 处理订单创建、状态管理 | Spring Boot, Seata, MyBatis |
库存服务 | 8082 | 管理商品库存、扣减逻辑 | Spring Boot, Sentinel, Redis |
Nacos 配置中心 | 8848 | 统一配置管理 | Spring Cloud Alibaba |
三、数据库设计
1. 订单表 (t_order)
CREATE TABLE t_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32) NOT NULL COMMENT '订单号',
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL COMMENT '购买数量',
status TINYINT DEFAULT 0 COMMENT '0-创建中 1-已支付 2-已取消',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. 库存表 (t_stock)
CREATE TABLE t_stock (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT UNIQUE NOT NULL,
total_stock INT NOT NULL COMMENT '总库存',
locked_stock INT DEFAULT 0 COMMENT '预扣库存',
available_stock INT NOT NULL COMMENT '可用库存',
version INT DEFAULT 0 COMMENT '乐观锁版本号'
);
3. 库存流水表 (t_stock_flow)
CREATE TABLE t_stock_flow (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL,
order_no VARCHAR(32) NOT NULL,
quantity INT NOT NULL,
status TINYINT NOT NULL COMMENT '1-预扣 2-确认 3-释放',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
数据库操作SQL
库存确认SQL
UPDATE t_stock
SET
total_stock = total_stock - #{quantity},
locked_stock = locked_stock - #{quantity},
version = version + 1
WHERE
product_id = #{productId}
AND locked_stock >= #{quantity}
AND version = #{version}
库存释放SQL(预扣释放)
UPDATE t_stock
SET
locked_stock = locked_stock - #{quantity},
available_stock = available_stock + #{quantity},
version = version + 1
WHERE
product_id = #{productId}
AND locked_stock >= #{quantity}
库存释放SQL(已确认释放)
UPDATE t_stock
SET
total_stock = total_stock + #{quantity},
available_stock = available_stock + #{quantity},
version = version + 1
WHERE
product_id = #{productId}
状态机设计(库存流水)
stateDiagram-v2
[*] --> LOCKED: 预扣成功
LOCKED --> CONFIRMED: 确认扣减
LOCKED --> RELEASED: 释放库存
CONFIRMED --> RELEASED: 异常释放
RELEASED --> [*]: 终态
四、核心业务流程
下单扣减库存流程:
sequenceDiagram
participant Client
participant OrderService
participant StockService
participant MySQL
Client->>OrderService: 提交订单请求
OrderService->>StockService: 预扣库存(订单号,商品ID,数量)
StockService->>MySQL: 查询当前库存
alt 库存充足
StockService->>MySQL: 预扣库存(设置锁定库存)
StockService-->>OrderService: 返回成功
OrderService->>MySQL: 创建订单
OrderService-->>Client: 返回订单号
else 库存不足
StockService-->>OrderService: 返回错误
OrderService-->>Client: 返回库存不足
end
五、接口规范
1. 订单服务接口
-
POST /orders 创建订单
请求体:
{
"userId": 1001,
"productId": 2001,
"quantity": 2
}
响应:
{
"code": 200,
"data": "ORDER_202311280001"
}
2. 库存服务接口
-
POST /stocks/lock 预扣库存
请求体:
{
"orderNo": "ORDER_202311280001",
"productId": 2001,
"quantity": 2
}
响应:
{
"code": 200,
"message": "库存锁定成功"
}
3. 库存确认接口 (POST /stocks/confirm)
业务场景:订单支付成功后,将预扣库存正式扣减(TCC模式的Confirm阶段)
请求体:
{
"orderNo": "ORDER_202311280001",
"productId": 2001,
"quantity": 2
}
响应:
{
"code": 200,
"message": "库存确认成功",
"data": {
"productId": 2001,
"confirmedQuantity": 2,
"remainingStock": 98
}
}
4. 库存释放接口 (POST /stocks/release)
业务场景:订单取消/支付超时后,释放预扣库存(TCC模式的Cancel阶段)
请求体:
{
"orderNo": "ORDER_202311280001",
"productId": 2001,
"quantity": 2,
"reason": "PAYMENT_TIMEOUT" // 释放原因:PAYMENT_TIMEOUT/ORDER_CANCEL/MANUAL_OPERATION
}
响应:
{
"code": 200,
"message": "库存释放成功",
"data": {
"productId": 2001,
"releasedQuantity": 2,
"currentStock": 100
}
}
六、关键代码实现
1. 库存服务核心扣减逻辑
@Service
public class StockServiceImpl implements StockService {
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFlowMapper stockFlowMapper;
@Override
@Transactional
public boolean lockStock(StockLockDTO lockDTO) {
// 使用Redis分布式锁
String lockKey = "lock_stock:" + lockDTO.getProductId();
try {
// 尝试获取锁 (实现略)
if (!redisLock.tryLock(lockKey, 3, TimeUnit.SECONDS)) {
throw new RuntimeException("系统繁忙");
}
Stock stock = stockMapper.selectByProductId(lockDTO.getProductId());
if (stock.getAvailableStock() < lockDTO.getQuantity()) {
throw new RuntimeException("库存不足");
}
// 乐观锁更新
int rows = stockMapper.lockStock(
lockDTO.getProductId(),
lockDTO.getQuantity(),
stock.getVersion()
);
if (rows == 0) {
throw new RuntimeException("库存更新冲突");
}
// 记录库存流水
StockFlow flow = new StockFlow();
flow.setOrderNo(lockDTO.getOrderNo());
flow.setProductId(lockDTO.getProductId());
flow.setQuantity(lockDTO.getQuantity());
flow.setStatus(StockFlowStatus.LOCKED);
stockFlowMapper.insert(flow);
return true;
} finally {
redisLock.unlock(lockKey);
}
}
}
2. 订单服务分布式事务控制
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StockFeignClient stockFeignClient;
@GlobalTransactional // Seata全局事务注解
@Override
public String createOrder(OrderCreateDTO createDTO) {
// 1. 生成订单号
String orderNo = generateOrderNo();
// 2. 预扣库存(RPC调用)
ResponseResult stockResult = stockFeignClient.lockStock(
new StockLockDTO(orderNo, createDTO.getProductId(), createDTO.getQuantity())
);
if (!stockResult.isSuccess()) {
throw new RuntimeException("库存操作失败: " + stockResult.getMessage());
}
// 3. 创建订单
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(createDTO.getUserId());
order.setProductId(createDTO.getProductId());
order.setQuantity(createDTO.getQuantity());
orderMapper.insert(order);
// 4. 其他业务操作(如扣减积分等)
// ...
return orderNo;
}
}
3. 库存释放服务实现
@Service
public class StockReleaseService {
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFlowMapper stockFlowMapper;
@Transactional
public ReleaseResult releaseStock(StockReleaseDTO releaseDTO) {
// 1. 查找预扣记录(LOCKED或CONFIRMED状态都需要处理)
StockFlow flow = stockFlowMapper.selectByOrderNoAndProductId(
releaseDTO.getOrderNo(),
releaseDTO.getProductId()
);
if (flow == null) {
throw new BusinessException("未找到库存记录");
}
// 2. 根据当前状态执行不同释放逻辑
if (flow.getStatus() == StockFlowStatus.LOCKED) {
releaseLockedStock(flow, releaseDTO);
} else if (flow.getStatus() == StockFlowStatus.CONFIRMED) {
releaseConfirmedStock(flow, releaseDTO);
} else {
throw new BusinessException("当前状态不可释放");
}
// 3. 更新流水状态
flow.setStatus(StockFlowStatus.RELEASED);
flow.setReleaseReason(releaseDTO.getReason());
stockFlowMapper.updateStatus(flow);
// 4. 获取最新库存
Stock currentStock = stockMapper.selectByProductId(releaseDTO.getProductId());
return new ReleaseResult(
releaseDTO.getProductId(),
releaseDTO.getQuantity(),
currentStock.getAvailableStock()
);
}
private void releaseLockedStock(StockFlow flow, StockReleaseDTO dto) {
// 释放预扣库存(增加可用库存,减少锁定库存)
stockMapper.releaseLockedStock(dto.getProductId(), dto.getQuantity());
}
private void releaseConfirmedStock(StockFlow flow, StockReleaseDTO dto) {
// 释放已确认库存(增加总库存和可用库存)
stockMapper.releaseConfirmedStock(dto.getProductId(), dto.getQuantity());
}
}
七、容错设计
-
库存预扣超时释放
-
定时任务扫描超过30分钟未确认的预扣记录
-
自动释放库存并更新流水状态
-
-
Sentinel流控规则
七、容错设计 库存预扣超时释放 定时任务扫描超过30分钟未确认的预扣记录 自动释放库存并更新流水状态 Sentinel流控规则
-
Seata事务恢复
-
TC Server记录全局事务状态
-
定时补偿未完成的事务分支
-
八、任务清单
-
基础任务
-
搭建Nacos注册中心
-
实现订单服务创建接口
-
实现库存锁定/释放接口
-
集成MyBatis-Plus操作数据库
-
添加Seata分布式事务支持
-
-
进阶任务
-
实现库存释放定时任务
-
集成Sentinel添加流控规则
-
使用Redis优化库存查询
-
设计库存扣减幂等机制
-
-
挑战任务
-
实现分库分表方案
-
设计库存热点数据解决方案
-
实现扣减库存的TCC模式
-
九、部署说明
-
启动基础设施:
# 启动Nacos sh nacos/bin/startup.sh -m standalone # 启动Seata sh seata/bin/seata-server.sh
-
服务启动顺序:
库存服务 → 订单服务 → 网关(可选)
-
测试用例:
# 测试正常下单 curl -X POST http://localhost:8081/orders \ -H "Content-Type: application/json" \ -d '{"userId":1001,"productId":2001,"quantity":2}' # 测试库存不足 curl ... {"quantity": 1000}
接口测试用例
库存确认测试场景:
-
正常确认已预扣库存
-
重复确认请求(测试幂等性)
-
确认不存在的预扣记录
-
确认数量大于预扣数量
-
并发确认测试(模拟乐观锁冲突)
库存释放测试场景:
-
释放已预扣库存(订单取消)
-
释放已确认库存(售后退货)
-
部分释放库存
-
释放后库存超卖测试
-
释放原因分类统计
本设计重点训练微服务拆分、分布式事务、并发控制能力。建议学生先完成基础流程,再逐步添加熔断、监控等生产级功能。关键要理解库存扣减的"预扣-确认-释放"三段式设计,这是解决分布式事务的经典模式。
往期免费源码 (无删减,无套路):🔥🔥🔥
https://pan.baidu.com/s/1sjAr08PU9Xe7MQf1gjGM5w?pwd=6666
「在线考试系统源码(含搭建教程)」 (无删减,无套路):🔥🔥🔥
链接:https://pan.quark.cn/s/96c4f00fdb43 提取码:WR6M
往期免费源码对应视频:
免费获取--SpringBoot+Vue宠物商城网站系统
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我