大纲
1.订单系统核心业务流程
2.Spring Cloud Alibaba在订单业务中的落地方案
3.面向分布式全链路故障设计的高可靠架构方案
4.分布式订单系统的技术栈与代码规范
5.分布式订单系统的DDD领域模型实践简述
1.订单系统核心业务流程
(1)生成订单时序图
(2)支付订单流程图
(3)取消订单流程图
这里主要介绍生单和退款两个核心链路:一是订单正向核心链路,二是订单逆向核心链路。
(1)生成订单时序图
生成订单是电商系统最核心的正向业务流程,涉及多个微服务的协同工作,关键节点说明如下:
typescript
// 一.用户请求阶段
// 客户端发起创建订单请求(携带商品信息、收货地址、优惠券等数据)
// API网关进行身份认证和基础参数校验
// 订单服务接收请求并进行业务参数校验
// 二.风控检查阶段
// 调用风控服务进行用户行为分析
// 检查是否存在刷单风险
// 验证用户信用等级和购买限额
// 三.价格计算阶段
// 调用商品服务获取最新商品信息和实时库存
// 调用营销服务计算优惠券折扣
// 调用运费模板服务计算配送费用
// 进行最终价格核算与验证
// 四.库存预占阶段
// 调用库存服务进行库存预占
// 采用TCC模式保证库存操作的原子性
// 设置库存预占时效(通常30分钟)
// 五.订单创建阶段
// 生成唯一订单号(采用雪花算法)
// 持久化订单主表和子表数据
// 记录订单操作日志
// 六.后续处理阶段
// 发送订单创建成功消息到MQ
// 更新用户购物车状态
// 返回订单创建结果给客户端
@Service
public class OrderServiceImpl implements OrderService {
@DubboReference
private ProductService productService;
@DubboReference
private InventoryService inventoryService;
@DubboReference
private MarketingService marketingService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Autowired
private RedissonClient redissonClient;
@Override
@Transactional(rollbackFor = Exception.class)
public CreateOrderResult createOrder(CreateOrderRequest request) {
// 参数校验
validateCreateOrderRequest(request);
// 风控检查
checkRiskControl(request);
// 获取分布式锁
String lockKey = "order:create:" + request.getUserId();
RLock lock = redissonClient.getLock(lockKey);
try {
// 加锁,防止重复提交
boolean locked = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!locked) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CREATE_LOCK_FAIL);
}
// 价格计算
CalculateOrderAmountDTO amountDTO = calculateOrderAmount(request);
// 库存预占
reserveInventory(request, amountDTO);
// 创建订单数据
OrderInfoDO orderInfo = buildOrderInfo(request, amountDTO);
orderMapper.insert(orderInfo);
// 创建订单商品项
createOrderItems(request, orderInfo);
// 发送订单创建事件
sendOrderCreatedEvent(orderInfo);
return buildCreateOrderResult(orderInfo);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CREATE_ERROR);
} finally {
lock.unlock();
}
}
private void validateCreateOrderRequest(CreateOrderRequest request) {
if (request.getUserId() == null) {
throw new OrderBizException(OrderErrorCodeEnum.USER_ID_IS_NULL);
}
// 其他参数校验...
}
private void checkRiskControl(CreateOrderRequest request) {
CheckOrderRiskRequest riskRequest = new CheckOrderRiskRequest();
BeanUtils.copyProperties(request, riskRequest);
Result<CheckOrderRiskDTO> result = riskService.checkOrderRisk(riskRequest);
if (!result.isSuccess()) {
throw new OrderBizException(result.getCode(), result.getMessage());
}
}
private CalculateOrderAmountDTO calculateOrderAmount(CreateOrderRequest request) {
// 详细价格计算逻辑...
}
private void reserveInventory(CreateOrderRequest request, CalculateOrderAmountDTO amountDTO) {
// 库存预占逻辑...
}
private OrderInfoDO buildOrderInfo(CreateOrderRequest request, CalculateOrderAmountDTO amountDTO) {
OrderInfoDO orderInfo = new OrderInfoDO();
// 订单信息构建...
return orderInfo;
}
private void createOrderItems(CreateOrderRequest request, OrderInfoDO orderInfo) {
// 订单商品项创建...
}
private void sendOrderCreatedEvent(OrderInfoDO orderInfo) {
OrderCreatedEvent event = new OrderCreatedEvent();
event.setOrderId(orderInfo.getOrderId());
rocketMQTemplate.syncSend("order-created-topic", event);
}
}
(2)支付订单流程图
支付流程是订单系统的关键环节,涉及与第三方支付平台的交互,关键节点说明如下:
scss
// 一.支付准备阶段
// 验证订单状态是否可支付
// 检查订单金额是否合法
// 生成支付流水号
// 二.支付执行阶段
// 调用支付网关发起支付
// 支持多种支付方式(支付宝、微信、银联等)
// 处理支付渠道返回结果
// 三.支付结果处理
// 同步返回支付结果
// 异步处理支付回调
// 更新订单支付状态
// 四.后续处理
// 触发订单履约流程
// 发送支付成功通知
// 更新用户积分和优惠券状态
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private OrderMapper orderMapper;
@DubboReference
private PayGatewayService payGatewayService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public PaymentResult processPayment(PaymentRequest request) {
// 参数校验
validatePaymentRequest(request);
// 查询订单
OrderInfoDO order = getOrderForPayment(request.getOrderId());
// 检查订单状态
checkOrderStatus(order);
// 调用支付网关
GatewayPaymentRequest gatewayRequest = buildGatewayRequest(order, request);
GatewayPaymentResult gatewayResult = payGatewayService.createPayment(gatewayRequest);
// 处理支付结果
return processGatewayResult(order, gatewayResult);
}
@Override
public void handlePaymentCallback(PaymentCallbackRequest callbackRequest) {
// 验证回调签名
verifyCallbackSignature(callbackRequest);
// 查询订单
OrderInfoDO order = orderMapper.selectByOrderId(callbackRequest.getOrderId());
if (order == null) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_NOT_FOUND);
}
// 幂等处理
if (OrderStatusEnum.PAID.getCode().equals(order.getOrderStatus())) {
log.warn("订单已支付,重复回调,orderId:{}", order.getOrderId());
return;
}
// 更新订单状态
updateOrderPaymentStatus(order, callbackRequest);
// 发送支付成功事件
sendPaymentSuccessEvent(order);
}
private void validatePaymentRequest(PaymentRequest request) {
// 支付请求参数校验...
}
private OrderInfoDO getOrderForPayment(String orderId) {
OrderInfoDO order = orderMapper.selectByOrderId(orderId);
if (order == null) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_NOT_FOUND);
}
return order;
}
private void checkOrderStatus(OrderInfoDO order) {
if (!OrderStatusEnum.CREATED.getCode().equals(order.getOrderStatus())) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_STATUS_ERROR);
}
if (order.getExpireTime() != null && order.getExpireTime().before(new Date())) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_PRE_PAY_EXPIRE_ERROR);
}
}
private GatewayPaymentRequest buildGatewayRequest(OrderInfoDO order, PaymentRequest request) {
// 构建支付网关请求...
}
private PaymentResult processGatewayResult(OrderInfoDO order, GatewayPaymentResult gatewayResult) {
// 处理支付网关返回结果...
}
private void verifyCallbackSignature(PaymentCallbackRequest callbackRequest) {
// 验证回调签名...
}
private void updateOrderPaymentStatus(OrderInfoDO order, PaymentCallbackRequest callbackRequest) {
// 更新订单支付状态...
}
private void sendPaymentSuccessEvent(OrderInfoDO order) {
OrderPaidEvent event = new OrderPaidEvent();
event.setOrderId(order.getOrderId());
rocketMQTemplate.syncSend("order-paid-topic", event);
}
}
(3)取消订单流程图
订单取消是典型的逆向流程,需要处理各种资源释放和数据一致性,关键节点说明如下:
typescript
// 一.取消申请阶段
// 验证取消权限和原因
// 检查订单是否可取消
// 记录取消申请
// 二.资源释放阶段
// 释放预占库存
// 退回使用的优惠券
// 取消关联的物流单
// 三.支付退款阶段
// 对于已支付订单发起退款
// 处理退款回调
// 更新订单退款状态
// 四.状态更新阶段
// 更新订单主状态
// 记录操作日志
// 发送取消通知
@Service
public class OrderCancelServiceImpl implements OrderCancelService {
@Autowired
private OrderMapper orderMapper;
@DubboReference
private InventoryService inventoryService;
@DubboReference
private MarketingService marketingService;
@DubboReference
private PaymentService paymentService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
public CancelOrderResult cancelOrder(CancelOrderRequest request) {
// 参数校验
validateCancelRequest(request);
// 查询订单
OrderInfoDO order = getOrderForCancel(request.getOrderId());
// 检查订单状态
checkOrderStatusForCancel(order);
// 获取分布式锁
String lockKey = "order:cancel:" + order.getOrderId();
RLock lock = redissonClient.getLock(lockKey);
try {
boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CANCEL_LOCK_FAIL);
}
// 更新订单状态为取消中
updateOrderStatusToCancelling(order);
// 释放库存
releaseInventory(order);
// 退回优惠券
returnCoupon(order);
// 处理退款
processRefund(order, request);
// 更新订单状态为已取消
updateOrderStatusToCancelled(order, request);
// 发送订单取消事件
sendOrderCancelledEvent(order);
return buildCancelResult(order);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CANCEL_ERROR);
} finally {
lock.unlock();
}
}
private void validateCancelRequest(CancelOrderRequest request) {
if (StringUtils.isBlank(request.getOrderId())) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_ID_IS_NULL);
}
// 其他参数校验...
}
private OrderInfoDO getOrderForCancel(String orderId) {
OrderInfoDO order = orderMapper.selectByOrderId(orderId);
if (order == null) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_NOT_FOUND);
}
return order;
}
private void checkOrderStatusForCancel(OrderInfoDO order) {
if (OrderStatusEnum.CANCELLED.getCode().equals(order.getOrderStatus())) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CANCEL_REPEAT);
}
if (OrderStatusEnum.DELIVERED.getCode().equals(order.getOrderStatus())) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_CANNOT_CANCEL_AFTER_DELIVERY);
}
}
private void updateOrderStatusToCancelling(OrderInfoDO order) {
// 更新订单状态为取消中...
}
private void releaseInventory(OrderInfoDO order) {
// 释放库存逻辑...
}
private void returnCoupon(OrderInfoDO order) {
// 退回优惠券逻辑...
}
private void processRefund(OrderInfoDO order, CancelOrderRequest request) {
if (OrderPaymentStatusEnum.PAID.getCode().equals(order.getPaymentStatus())) {
// 发起退款...
}
}
private void updateOrderStatusToCancelled(OrderInfoDO order, CancelOrderRequest request) {
// 更新订单状态为已取消...
}
private void sendOrderCancelledEvent(OrderInfoDO order) {
OrderCancelledEvent event = new OrderCancelledEvent();
event.setOrderId(order.getOrderId());
rocketMQTemplate.syncSend("order-cancelled-topic", event);
}
}
2.Spring Cloud Alibaba在订单业务中的落地方案
(1)基于Dubbo + Nacos实现全RPC调用链路
(2)基于Seata实现正向核心链路的数据强一致性
(3)基于RocketMQ延迟消息取消超时未支付订单
(4)基于Builder和Template模式构建对象和发通知
(5)基于Redisson分布式锁解决并发预支付问题
(6)订单履约强一致解决方案
(7)订单履约幂等性解决方案
(8)基于Seata实现逆向核心链路数据强一致性
(9)上述方案的注意事项
SCA包含了:Dubbo、Nacos、Sentinel、Seata、RocketMQ等组件,SCA技术栈在订单业务中的落地方案包括如下:
(1)基于Dubbo + Nacos实现全RPC调用链路
一.服务注册与发现机制
less
// 一.服务注册
// 所有微服务启动时向Nacos注册中心注册服务元数据
// 包含服务名称、IP地址、端口、健康状态、权重等信息
// 支持集群部署和多机房注册
// 二.服务发现
// 消费者通过Dubbo的@DubboReference注解发现服务
// 内置负载均衡策略(随机、轮询、最少活跃等)
// 支持服务分组和版本控制
// 三.健康检查
// 基于心跳机制实现服务健康监测
// 自动剔除不健康节点
// 与K8s健康检查集成
// 1.服务提供方配置
@DubboService(version = "1.0.0", group = "order")
public class OrderServiceImpl implements OrderService {
@Override
public OrderResult createOrder(OrderRequest request) {
// 业务逻辑
}
}
// 2.服务消费方调用
@DubboReference(version = "1.0.0", group = "order", check = false, timeout = 3000)
private OrderService orderService;
// 3.Nacos注册中心配置
dubbo:
registry:
address: nacos://127.0.0.1:8848
parameters:
namespace: dev
二.配置中心集成
csharp
// 一.动态配置
// 订单相关配置存储在Nacos配置中心
// 支持配置的动态更新和实时生效
// 配置变更自动通知所有服务节点
// 二.多环境支持
// 通过namespace隔离不同环境配置
// 支持配置的版本管理和回滚
// 配置变更审计日志
// 配置示例
# 订单服务Dubbo配置
dubbo:
application:
name: order-service
protocol:
name: dubbo
port: 20880
registry:
address: nacos://${nacos.server-addr}
parameters:
namespace: ${spring.profiles.active}
consumer:
check: false
timeout: 3000
retries: 2
(2)基于Seata实现正向核心链路数据强一致性
针对生单 -> 优惠券锁定 -> 库存锁定等正向核心链路流程,使用Seata的AT模式确保涉及分布式事务的正向核心链路的数据强一致性。
scss
// 一.AT模式实现原理
// 一阶段:业务数据和回滚日志记录在同一个本地事务中提交
// 二阶段:提交异步化,快速完成;回滚通过一阶段的回滚日志进行反向补偿
// 二.订单创建事务场景
// 主事务:订单服务创建订单记录
// 分支事务1:库存服务扣减库存
// 分支事务2:营销服务锁定优惠券
// 分支事务3:积分服务预增积分
@Service
public class OrderCreateServiceImpl implements OrderCreateService {
@Autowired
private OrderMapper orderMapper;
@DubboReference
private InventoryService inventoryService;
@DubboReference
private CouponService couponService;
@Override
@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
public CreateOrderResult createOrder(CreateOrderRequest request) {
// 1.创建订单
OrderInfoDO order = createOrderRecord(request);
// 2.扣减库存
reduceInventory(order);
// 3.锁定优惠券
lockCoupon(order);
return buildResult(order);
}
private OrderInfoDO createOrderRecord(CreateOrderRequest request) {
// 创建订单逻辑...
}
private void reduceInventory(OrderInfoDO order) {
InventoryReduceRequest reduceRequest = new InventoryReduceRequest();
// 构建请求参数...
inventoryService.reduceInventory(reduceRequest);
}
private void lockCoupon(OrderInfoDO order) {
CouponLockRequest lockRequest = new CouponLockRequest();
// 构建请求参数...
couponService.lockCoupon(lockRequest);
}
}
(3)基于RocketMQ延迟消息取消超时未支付订单
通过Redisson分布式锁 + Elastic-Job实现补偿。
typescript
// 一.延迟消息取消超时未支付订单
// 订单创建成功后发送延迟消息
// 消息延迟时间与订单支付超时时间一致
// 消费者处理消息时检查订单状态
// 二.消息可靠性保障
// 生产者保障:事务消息机制、发送重试策略、消息落库 + 定时任务补偿
// Broker保障:多副本同步复制、刷盘策略配置、消息轨迹追踪
// 消费者保障:幂等处理、消费重试机制、死信队列处理
@Service
public class OrderTimeoutServiceImpl implements OrderTimeoutService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Autowired
private OrderMapper orderMapper;
public void sendOrderTimeoutMessage(String orderId, Date expireTime) {
long delay = expireTime.getTime() - System.currentTimeMillis();
if (delay <= 0) {
return;
}
Message<OrderTimeoutMessage> message = MessageBuilder.withPayload(
new OrderTimeoutMessage(orderId))
.setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, calculateDelayLevel(delay))
.build();
rocketMQTemplate.syncSend("order-timeout-topic", message);
}
@RocketMQMessageListener(topic = "order-timeout-topic", consumerGroup = "order-timeout-consumer-group")
public void handleOrderTimeout(OrderTimeoutMessage message) {
OrderInfoDO order = orderMapper.selectByOrderId(message.getOrderId());
if (order == null || !OrderStatusEnum.CREATED.getCode().equals(order.getOrderStatus())) {
return;
}
// 执行订单取消逻辑
cancelOrder(order);
}
private int calculateDelayLevel(long delayMillis) {
// 计算RocketMQ延迟级别...
}
private void cancelOrder(OrderInfoDO order) {
// 订单取消逻辑...
}
}
// 顺序消息实现方案
public class OrderStatusMessageListener implements MessageListenerOrderly {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> messages, ConsumeOrderlyContext context) {
try {
messages.forEach(message -> {
String orderId = message.getKeys();
// 确保同一个订单的消息顺序处理
processOrderStatusChange(orderId, new String(message.getBody()));
});
return ConsumeOrderlyStatus.SUCCESS;
} catch (Exception e) {
log.error("处理消息失败", e);
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
}
// 消息轨迹追踪
public class MessageTraceInterceptor implements SendMessageHook {
@Override
public String hookName() {
return "MessageTraceInterceptor";
}
@Override
public void sendMessageBefore(SendMessageContext context) {
String traceId = MDC.get("traceId");
if (StringUtils.isNotBlank(traceId)) {
context.getMessage().putUserProperty("trace_id", traceId);
}
}
@Override
public void sendMessageAfter(SendMessageContext context) {
// 记录发送日志
messageTraceRepository.saveSendLog(
context.getMessage().getMsgId(),
context.getMessage().getTopic(),
context.getMessage().getTags(),
context.getMessage().getKeys(),
context.getMessage().getUserProperty("trace_id"),
LocalDateTime.now()
);
}
}
(4)基于Builder和Template模式构建对象和发通知
基于Builder模式实现复杂订单对象的构建、复杂查询规则对象的构建,基于Template模式实现物流配送的通知。
typescript
// 1.Builder模式构建订单
public class OrderBuilder {
private Order order = new Order();
public OrderBuilder withItems(List<Item> items) {
order.setItems(items);
return this;
}
public OrderBuilder withAddress(Address address) {
order.setShippingAddress(address);
return this;
}
public Order build() {
// 校验逻辑
return order;
}
}
// 2.Template模式物流通知
public abstract class LogisticsTemplate {
public final void process(Order order) {
validate(order);
prepareData(order);
sendNotification(order);
recordLog(order);
}
protected abstract void sendNotification(Order order);
}
public class SFLogistics extends LogisticsTemplate {
@Override
protected void sendNotification(Order order) {
// 调用顺丰API
}
}
(5)基于Redisson分布式锁解决并发预支付问题
typescript
// 一.并发预支付问题解决方案
// 问题场景:用户快速重复点击支付按钮、网络延迟导致前端重复提交、支付渠道重复处理回调
// 解决方案:支付令牌机制、订单状态机校验、分布式锁保护核心流程
// 二.锁优化实践
// 锁粒度控制:按订单ID加锁、避免全局锁、读写锁分离
// 锁超时设置:合理设置锁超时时间、自动续期机制、避免死锁
// 锁性能监控:锁等待时间监控、锁竞争告警、锁使用统计分析
@Service
public class OrderPaymentServiceImpl implements OrderPaymentService {
@Autowired
private RedissonClient redissonClient;
@Override
public PaymentResult processPayment(PaymentRequest request) {
String lockKey = "order:payment:" + request.getOrderId();
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,最多等待1秒,锁有效期30秒
boolean locked = lock.tryLock(1, 30, TimeUnit.SECONDS);
if (!locked) {
throw new OrderBizException(OrderErrorCodeEnum.ORDER_PAYMENT_LOCK_FAIL);
}
// 核心支付处理逻辑
return doProcessPayment(request);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new OrderBizException(OrderErrorCodeEnum.ORDER_PAYMENT_ERROR);
} finally {
lock.unlock();
}
}
private PaymentResult doProcessPayment(PaymentRequest request) {
// 实际支付处理逻辑...
}
}
(6)订单履约强一致解决方案
基于RocketMQ的柔性分布式事务解决方案。
typescript
// 1.发送半消息
public void fulfillOrder(Order order) {
// 1.准备本地事务
orderDao.updateStatus(order.getId(), "FULFILLING");
// 2.发送事务消息
rocketMQTemplate.sendMessageInTransaction(
"fulfill_topic",
MessageBuilder.withPayload(order).build(),
null
);
}
// 2.实现TransactionListener
public class FulfillListener implements TransactionListener {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
// 执行本地履约逻辑
fulfillService.process((Order)msg.getPayload());
return LocalTransactionState.COMMIT_MESSAGE;
} catch (Exception e) {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 检查本地事务状态
Order order = orderDao.getById(msg.getKeys());
return order.getStatus().equals("FULFILLED") ?
LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.UNKNOW;
}
}
(7)订单履约幂等性解决方案
基于Redisson分布式锁 + 前置状态检查实现幂等。
csharp
public FulfillResult fulfillOrder(String orderId) {
// 1.获取分布式锁
RLock lock = redisson.getLock("fulfill:" + orderId);
if (!lock.tryLock()) {
throw new BizException("履约操作正在处理中");
}
try {
// 2.前置状态检查
Order order = orderDao.getById(orderId);
if (order.getStatus().equals("FULFILLED")) {
return FulfillResult.existed();
}
// 3.业务处理
return doFulfill(order);
} finally {
lock.unlock();
}
}
// 数据库幂等设计
CREATE TABLE fulfill_record (
id BIGINT PRIMARY KEY,
order_id VARCHAR(64) UNIQUE, -- 唯一约束
status TINYINT,
...
);
(8)基于Seata实现逆向核心链路数据强一致性
针对取消订单 -> 取消履约 -> 释放资产等逆向核心链路流程,使用Seata的AT模式确保涉及分布式事务的逆向核心链路的数据强一致性。
java
// 一.订单取消事务场景
// 主事务:订单服务更新订单状态
// 分支事务1:库存服务释放库存
// 分支事务2:营销服务退回优惠券
// 分支事务3:支付服务处理退款
// 二.异常处理机制
// 设置事务超时时间
// 重试机制配置
// 人工干预接口
// 三.性能优化
// 减少事务参与者
// 缩短事务执行时间
// 异步化非核心操作
@GlobalTransactional
public void cancelOrder(String orderId) {
// 1.取消订单
orderDao.updateStatus(orderId, "CANCELLED");
// 2.释放库存
inventoryFeignClient.releaseStock(orderId);
// 3.返还优惠券
couponService.unlock(orderId);
// 4.退款处理
refundService.process(orderId);
}
// 逆向操作undo_log示例
// Seata会自动记录如下SQL的逆向操作:
UPDATE inventory SET stock = stock + 1 WHERE sku = 'SKU123';
UPDATE coupon SET status = 1 WHERE id = 'COUPON_001';
(9)上述方案的注意事项
css
Dubbo Filter链 需配置Nacos集群地址
Seata AT模式 避免跨长事务
RocketMQ延迟消息 需配置Broker的延迟级别
Builder设计模式 Builder中的方法需保证线程安全
Redisson看门狗 注意锁续期时间配置
RocketMQ事务消息表 需实现checkLocalTransaction
MySQL唯一索引 配合状态机使用更佳
Seata Saga模式 适合长流程逆向操作
简单介绍完SCA在复杂订单业务中的基础技术方案,接下来简单介绍在分布式架构中面向分布式全链路故障而设计的高可靠架构方案。
3.面向分布式全链路故障设计的高可靠架构方案
(1)订单系统Dubbo服务高并发压力优化
(2)订单系统引入Spring Cloud Alibaba Sentinel
(3)大促活动网关层限流解决方案
(4)订单系统自适应流控解决方案
(5)订单集群柔性限流解决方案
(6)订单核心链路雪崩场景保护方案
(7)防恶意刷单自动发现与黑名单方案
(8)库存系统异构存储的TCC分布式事务解决方案
(9)仓储系统老旧代码的Saga分布式事务解决方案
(10)物流系统多数据库的XA分布式事务解决方案
(11)Nacos+ZooKeeper双注册中心高可用方案
(12)Dubbo+Nacos多机房部署流量Mesh管控方案
分布式系统的技术难点,其实就是分布式系统链路长、故障多,如果任何一个环节出了故障就会导致系统出问题。所以要分析全链路里会有哪些问题,要用哪些方案来确保系统运行稳定。
分布式订单系统就采用了如下方案来保证系统稳定:
Dubbo服务高并发压力生产优化、大促活动网关层限流解决方案、订单集群柔性限流解决方案、订单系统自适应流控解决方案、订单系统核心链路雪崩解决方案、防恶意刷单与黑名单解决方案、库存系统异构存储架构TCC分布式事务解决方案、仓储系统老旧代码Saga分布式事务解决方案、物流系统多数据库XA分布式事务解决方案、Nacos + ZooKeeper双注册中心高可用方案、Dubbo + Nacos多机房部署流量Mesh管控方案。
(1)订单系统Dubbo服务高并发压力优化
首先针对订单系统核心接口逐个进行高并发压测,然后逐步分析Linux OS、Dubbo线程池、数据库连接池、数据库索引和SQL语句、业务逻辑效率等各个环节存在的问题。并得出在机器负载可控情况下,订单系统可以接受的最大并发压力。
java
// 一.全链路压测实施
// 压测准备:生产环境隔离压测、影子库表配置、压测数据构造
// 压测执行:梯度增压测试、稳定性测试、峰值冲击测试
// 压测分析:性能瓶颈定位、资源使用分析、容量规划建议
// 二.性能优化措施
// 数据库优化:索引优化、SQL调优、分库分表
// 缓存优化:多级缓存架构、热点数据探测、缓存一致性方案
// JVM优化:堆内存配置、GC策略选择、JIT参数调优
// 自定义线程池工厂(扩展Dubbo的ThreadPool)
public class OrderedThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
String name = url.getParameter("threadname", "dubbo-order");
int cores = url.getParameter("corethreads", 50);
int max = url.getParameter("threads", 500);
int queues = url.getParameter("queues", 1000);
return new ThreadPoolExecutor(cores, max, 60L, TimeUnit.SECONDS,
new OrderedMemorySafeLinkedBlockingQueue<>(queues), //有序且内存安全的队列
new NamedThreadFactory(name, true),
new ThreadPoolExecutor.AbortPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 记录拒绝日志并发送告警
MonitorService.logReject(r);
throw new RejectedExecutionException("Order service thread pool exhausted");
}
});
}
}
(2)订单系统引入Spring Cloud Alibaba Sentinel
此时需要将Sentinel客户端引入订单系统工程,同时完成Sentinel Dashboard的搭建。
Sentinel Dashboard会跟Nacos整合,Sentienl Dashboard Push到Nacos。然后Sentinel的Rule Datasource监听变化,被Nacos Push过来最新规则。
less
// Sentinel动态规则扩展(Nacos持久化)
public class NacosDataSourceInit {
@PostConstruct
public void init() {
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(
"nacos:8848", "order-service-flow-rules",
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
.addListener(new FlowRuleUpdateListener());
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
// 熔断规则
...
}
}
// 控制台规则推送接口
@RestController
@RequestMapping("/sentinel")
public class SentinelRuleController {
@Autowired
private ConfigService nacosConfigService;
@PostMapping("/push-rule")
public String pushRule(@RequestBody RuleDTO rule) {
// 推送到Nacos
nacosConfigService.publishConfig(
rule.getDataId(),
"SENTINEL_GROUP",
JSON.toJSONString(rule.getRules()));
return "success";
}
}
(3)大促活动网关层限流解决方案
首先演示在大促场景下,瞬时高并发是如何击垮订单系统的,同时评估出订单集群部署下的最大可抗压力,然后设计网关大促限流方案。可以基于Spring Cloud Gateway实现一个订单系统前置网关,对订单系统集群部署做负载均衡 + 部署网关集群。通过在网关引入Sentinel来实现限流,在流量入口处保护订单系统不被击垮。
less
// 自定义限流过滤器
public class OrderRateLimitFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String userId = exchange.getRequest().getHeaders().getFirst("USER-ID");
// 黑名单检查
if (blackListService.isBlocked(userId)) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().writeWith(Mono.just(bufferize("{"code":429,"msg":"操作过于频繁"}")));
}
// 基于用户ID的令牌桶限流
if (!rateLimiter.tryAcquire(userId)) {
return handleTooManyRequests(exchange);
}
return chain.filter(exchange);
}
}
// 网关动态路由配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_service", r -> r.path("/api/order/**")
.filters(f -> f.stripPrefix(1)
.requestRateLimiter(config -> {
config.setKeyResolver(exchange ->
Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()));
config.setRateLimiter(redisRateLimiter());
}))
.uri("lb://order-service"))
.build();
}
(4)订单系统自适应流控解决方案
首先演示订单系统单实例流量超载的问题。然后根据订单系统部署机器的配置、高压下的机器负载、业务逻辑的复杂度,基于Sentinel设计订单系统的自适应流控方案。也就是根据机器负载、响应时间、请求速率,自适应调整机器的流量阈值,从而实现柔性流控效果。最后演示大流量下,流量被网关层限流后穿透到订单层,各个机器上的自适应流控效果。
scss
// 流控规则配置:QPS限流、并发线程数限流、关联资源限流
// 降级策略:慢调用比例、异常比例、异常数
// 热点参数限流:针对热点参数特殊限流、参数例外项配置、集群模式支持
@Configuration
public class SentinelConfig {
@PostConstruct
public void init() {
// 订单创建流控规则
FlowRule orderCreateRule = new FlowRule();
orderCreateRule.setResource("createOrder");
orderCreateRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
orderCreateRule.setCount(1000); // 默认阈值
orderCreateRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
orderCreateRule.setWarmUpPeriodSec(10);
// 自适应流控规则
FlowRule adaptiveRule = new FlowRule();
adaptiveRule.setResource("adaptiveResource");
adaptiveRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
adaptiveRule.setCount(500);
adaptiveRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
adaptiveRule.setMaxQueueingTimeMs(500);
adaptiveRule.setStrategy(RuleConstant.STRATEGY_ADAPTIVE);
FlowRuleManager.loadRules(Arrays.asList(orderCreateRule, adaptiveRule));
}
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
// 系统指标监控
public class SystemMetricCollector implements Runnable {
@Override
public void run() {
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
double cpuLoad = osBean.getSystemLoadAverage();
long freeMem = Runtime.getRuntime().freeMemory();
// 动态调整流控阈值
adjustFlowRules(cpuLoad, freeMem);
}
private void adjustFlowRules(double cpuLoad, long freeMem) {
List<FlowRule> rules = new ArrayList<>();
// CPU>80%时降级50%流量
if (cpuLoad > 0.8) {
rules.add(new FlowRule("createOrder")
.setCount(500) // 原阈值1000
.setGrade(RuleConstant.FLOW_GRADE_QPS));
}
FlowRuleManager.loadRules(rules);
}
}
// 注册定时任务
@Scheduled(fixedRate = 5000)
public void collectMetrics() {
systemMetricCollector.run();
}
(5)订单集群柔性限流解决方案
首先针对订单系统各核心接口,评估出集群模式下每个接口的最大负载压力。然后演示出集群模式下的接口,在访问超载时引发的问题。接着基于Sentinel设计各个核心接口的柔性限流方案。最后对订单接口进行超压力访问,演示接口级的柔性流控效果。
typescript
// 注意: Sentinel规则加载失败的处理
public class SentinelRuleLoaderListener implements InitFunc {
@Override
public void init() {
// 本地文件降级规则
String rulePath = "/home/sentinel/rules/order-service-flow.json";
FileDataSource<List<FlowRule>> ds = new FileDataSource<>(rulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
FlowRuleManager.register2Property(ds.getProperty());
}
}
(6)订单核心链路雪崩场景保护方案
首先演示订单核心链路的服务雪崩场景,单服务崩溃是如何引发服务链路全面崩溃的。接着对订单核心链路的各个服务,基于Sentinel设计服务链路防雪崩方案,避免核心链路任何一个服务崩溃引发的服务链路雪崩问题。最后演示单服务崩溃时,整个订单服务的防雪崩效果。
订单核心链路的各个服务有:订单服务、库存服务、营销服务、仓储服务、物流服务、风控服务。
typescript
// 一.服务熔断设计
// 熔断策略:基于错误率熔断、基于响应时间熔断、基于并发数熔断
// 熔断恢复:半开状态探测、自动恢复机制、手动干预接口
// 熔断监控:熔断事件上报、熔断告警通知、熔断仪表盘
// 二.服务降级方案
// 降级策略:返回兜底数据、缓存数据返回、空操作处理
// 降级开关:配置中心动态开关、多级降级策略、白名单机制
// 熔断规则配置
public class CircuitBreakerConfig {
@Bean
public DegradeRule degradeRule() {
DegradeRule rule = new DegradeRule("queryOrderDetail")
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setCount(0.5) //异常比例阈值50%
.setTimeWindow(30) //熔断30秒
.setMinRequestAmount(20) //最小请求数
.setStatIntervalMs(60000); //统计窗口60秒
DegradeRuleManager.loadRules(Collections.singletonList(rule));
return rule;
}
}
@Service
public class OrderQueryServiceImpl implements OrderQueryService {
@SentinelResource(
value = "queryOrderDetail",
blockHandler = "queryOrderDetailBlockHandler",
fallback = "queryOrderDetailFallback")
public OrderDetailDTO queryOrderDetail(String orderId) {
// 正常查询逻辑...
}
// 流控降级处理
public OrderDetailDTO queryOrderDetailBlockHandler(String orderId, BlockException ex) {
log.warn("订单查询被限流,orderId:{}", orderId);
return getCachedOrderDetail(orderId);
}
// 异常降级处理
public OrderDetailDTO queryOrderDetailFallback(String orderId, Throwable ex) {
log.error("订单查询异常,orderId:{}", orderId, ex);
return getBasicOrderDetail(orderId);
}
}
(7)防恶意刷单自动发现与黑名单方案
首先演示单用户恶意刷单行为和效果,接着基于基于Sentinel设计自动识别用户恶意刷单的方案,将恶意刷单的用户ID自动加入访问控制黑名单。从而实现自动化识别 + 防止恶意刷单 + 黑名单控制的机制。
typescript
// 用户行为分析服务
public class UserBehaviorAnalyzer {
private final Map<String, BehaviorWindow> userWindows = new ConcurrentHashMap<>();
public boolean isMalicious(String userId) {
BehaviorWindow window = userWindows.computeIfAbsent(userId, k -> new BehaviorWindow(60));
window.recordOperation();
return window.getOperationsPerMinute() > 100; //每分钟超过100次操作
}
}
// 黑名单拦截器
public class BlackListInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String userId = request.getHeader("USER-ID");
if (blackListService.isBlocked(userId)) {
response.setStatus(403);
response.getWriter().write("{"code":403,"msg":"账号已被限制"}");
return false;
}
return true;
}
}
// 自动化封禁逻辑
@Scheduled(fixedRate = 60000)
public void autoBlockUsers() {
userBehaviorAnalyzer.getMaliciousUsers().forEach(userId -> {
blackListService.blockUser(userId, 24, TimeUnit.HOURS); // 封禁24小时
alertService.sendAlert("用户封禁警告", userId);
});
}
(8)库存系统异构存储的TCC分布式事务解决方案
常见的库存架构是Redis + 数据库异构存储架构。由于订单系统又会强依赖库存系统,所以库存系统的异构存储架构的分布式事务解决方案,通常是基于Seata的TCC模式来实现的。
java
// TCC模式实现库存扣减
// Try阶段:检查库存是否充足、冻结库存数量、记录冻结日志
// Confirm阶段:扣减真实库存、删除冻结记录、更新订单状态
// Cancel阶段:释放冻结库存、删除冻结记录、记录取消日志
// TCC接口定义
@LocalTCC
public interface InventoryService {
@TwoPhaseBusinessAction(name = "deduct", commitMethod = "commitReduce", rollbackMethod = "cancelReduce")
boolean prepareReduce(InventoryReduceRequest request);
boolean commitReduce(InventoryReduceRequest request);
boolean cancelReduce(InventoryReduceRequest request);
}
@Service
public class InventoryServiceImpl implements InventoryService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean prepareReduce(InventoryReduceRequest request) {
// 检查库存
InventoryDO inventory = checkInventory(request);
// 冻结库存
freezeInventory(inventory, request);
// 记录冻结日志
recordFreezeLog(request);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean commitReduce(InventoryReduceRequest request) {
// 扣减真实库存
reduceRealInventory(request);
// 删除冻结记录
deleteFreezeLog(request);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean cancelReduce(InventoryReduceRequest request) {
// TCC空回滚处理
// 检查Try是否执行过
if (!transactionLogDao.exists(request.getXid())) {
log.warn("空回滚,xid={}", request.getXid());
return true;
}
// 释放冻结库存
releaseFreezeInventory(request);
// 删除冻结记录
deleteFreezeLog(request);
return true;
}
}
(9)仓储系统老旧代码的Saga分布式事务解决方案
由于仓储系统的逻辑通常会非常复杂,而且会包含多个服务协同工作,所以对这类系统做分布式事务改造的难度比较大。仓储系统的多服务链路老旧代码的分布式事务解决方案,通常是基于Seata的Saga模式来实现的。
scss
// 一.正向操作:
// 创建履约单
// 分配仓库
// 生成拣货单
// 二.补偿操作:
// 取消履约单
// 释放仓库分配
// 作废拣货单
@Service
public class OrderFulfillmentSagaServiceImpl implements OrderFulfillmentSagaService {
@Override
public void createFulfillment(OrderFulfillmentRequest request) {
// 创建履约单
createFulfillmentOrder(request);
// 分配仓库
allocateWarehouse(request);
// 生成拣货单
generatePickList(request);
}
@Override
public void cancelFulfillment(OrderFulfillmentRequest request) {
// 作废拣货单
cancelPickList(request);
// 释放仓库分配
releaseWarehouseAllocation(request);
// 取消履约单
cancelFulfillmentOrder(request);
}
}
(10)物流系统多数据库的XA分布式事务解决方案
java
// 多数据源配置
@Bean
public DataSource logisticsDataSource() {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
ds.setUniqueResourceName("logisticsDB");
ds.setXaProperties(xaProperties());
return ds;
}
// JTA事务管理器
@Bean
public JtaTransactionManager transactionManager() {
UserTransactionManager utm = new UserTransactionManager();
utm.setForceShutdown(true);
UserTransaction ut = new UserTransactionImp();
return new JtaTransactionManager(ut, utm);
}
(11)Nacos+ZooKeeper双注册中心高可用方案
typescript
// 注册中心配置
@Bean
public RegistryConfig nacosRegistry() {
return new RegistryConfig()
.setProtocol("nacos")
.setAddress("nacos:8848")
.setDefault(false);
}
@Bean
public RegistryConfig zkRegistry() {
return new RegistryConfig()
.setProtocol("zookeeper")
.setAddress("zk:2181")
.setDefault(true);
}
// 服务引用时指定
@DubboReference(registry = {"nacos", "zk"})
private OrderService orderService;
(12)Dubbo+Nacos多机房部署流量Mesh管控方案
yaml
# envoy-traffic-split.yaml
resources:
- "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
name: order_route
virtual_hosts:
- name: order_service
domains: ["order.example.com"]
routes:
- match: { prefix: "/" }
route:
weighted_clusters:
clusters:
- name: order_service_hz
weight: 70
- name: order_service_sh
weight: 30
4.分布式订单系统的技术栈与代码规范
(1)订单系统的技术栈
(2)各层方法的命名规范
(3)领域模型POJO类命名规范
(4)统一异常处理规范
(5)统一返回结果处理规范
(1)订单系统的技术栈
(2)各层方法的命名规范
开发规范基于阿⾥巴巴的《Java开发⼿册》:
一.总的原则是⽤动词做前缀,名词在后⾯
二.获取单个对象时使⽤get做前缀
三.获取多个对象时使⽤list做前缀如listOrders
四.获取统计值时使⽤count做前缀
五.插⼊数据时使⽤save/insert做前缀
六.删除数据时使⽤remove/delete做前缀
七.修改数据时使⽤update做前缀
(3)领域模型POJO类命名规范
一.数据对象:xxxDO,xxx即为数据表名
二.Controller层返回结果,展示对象⽤xxxVO
请求⼊参的命名格式⽤xxxRequest,查询条件封装对象⽤xxxQuery。
三.Dubbo API层返回结果,返回对象⽤xxxDTO
请求的命名格式为xxxRequest,查询条件封装对象⽤xxxQuery。
四.业务层内部的数据传输对象⼀般⽤xxxDTO,不做强制规定。
每个POJO类都会继承AbstractObject类,方便不同POJO之间的属性克隆。
kotlin
//基础POJO类
//浅克隆:
//复制对象时仅仅复制对象本身,包括基本属性;
//但该对象的属性引用其他对象时,该引用对象不会被复制;
//即拷贝出来的对象与被拷贝出来的对象中的属性引用的对象是同一个;
//深克隆:
//复制对象本身的同时,也复制对象包含的引用指向的对象;
//即修改被克隆对象的任何属性都不会影响到克隆出来的对象。
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class AbstractObject {
//深度克隆
//@param targetClazz 目标对象的Class类型
//@return 目标对象实例
public <T> T clone(Class<T> targetClazz) {
try {
T target = targetClazz.newInstance();
BeanCopierUtil.copyProperties(this, target);
return getTarget(target);
} catch (Exception e) {
throw new RuntimeException("error", e);
}
}
//浅度克隆
//@param target 目标对象实例
//@return 目标对象实例
public <T> T clone(T target) {
try {
BeanCopierUtil.copyProperties(this, target);
return getTarget(target);
} catch (Exception e) {
throw new RuntimeException("error", e);
}
}
...
}
(4)统一异常处理规范
一.定义⼀个GlobalExceptionHandler组件
通过对该组件添加@RestControllerAdvice注解,让该组件成为默认的Controller全局异常处理增强组件。在这个组件中,会分别对系统级别未知系统、客户端异常、服务端异常都做了统⼀处理。
typescript
//默认的Controller全局异常处理增强组件
@RestControllerAdvice
@Order
public class GlobalExceptionHandler {
// =========== 系统级别未知异常 =========
@ExceptionHandler(value = Exception.class)
public JsonResult<Object> handle(Exception e) {
log.error("[ 系统未知错误 ]", e);
return JsonResult.buildError(CommonErrorCodeEnum.SYSTEM_UNKNOWN_ERROR);
}
// =========== 客户端异常 =========
//1001 HTTP请求方法类型错误
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public JsonResult<Object> handle(HttpRequestMethodNotSupportedException e) {
log.error("[客户端HTTP请求方法错误]", e);
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_HTTP_METHOD_ERROR);
}
//1002 客户端请求体参数校验不通过
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public JsonResult<Object> handle(MethodArgumentNotValidException e) {
log.error("[客户端请求体参数校验不通过]", e);
String errorMsg = this.handle(e.getBindingResult().getFieldErrors());
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_CHECK_ERROR.getErrorCode(), errorMsg);
}
private String handle(List<FieldError> fieldErrors) {
StringBuilder sb = new StringBuilder();
for (FieldError obj : fieldErrors) {
sb.append(obj.getField());
sb.append("=[");
sb.append(obj.getDefaultMessage());
sb.append("] ");
}
return sb.toString();
}
//1003 客户端请求体JSON格式错误或字段类型不匹配
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public JsonResult<Object> handle(HttpMessageNotReadableException e) {
log.error("[客户端请求体JSON格式错误或字段类型不匹配]", e);
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_FORMAT_ERROR);
}
//1004 客户端URL中的参数类型错误
@ExceptionHandler(value = BindException.class)
public JsonResult<Object> handle(BindException e) {
log.error("[客户端URL中的参数类型错误]", e);
FieldError fieldError = e.getBindingResult().getFieldError();
String errorMsg = null;
if (fieldError != null) {
errorMsg = fieldError.getDefaultMessage();
if (errorMsg != null && errorMsg.contains("java.lang.NumberFormatException")) {
errorMsg = fieldError.getField() + "参数类型错误";
}
}
if (errorMsg != null && !"".equals(errorMsg)) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR);
}
//1005 客户端请求参数校验不通过
@ExceptionHandler(value = ConstraintViolationException.class)
public JsonResult<Object> handle(ConstraintViolationException e) {
log.error("[客户端请求参数校验不通过]", e);
Iterator<ConstraintViolation<?>> it = e.getConstraintViolations().iterator();
String errorMsg = null;
if (it.hasNext()) {
errorMsg = it.next().getMessageTemplate();
}
if (errorMsg != null && !"".equals(errorMsg)) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR);
}
//1006 客户端请求缺少必填的参数
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public JsonResult<Object> handle(MissingServletRequestParameterException e) {
log.error("[客户端请求缺少必填的参数]", e);
String errorMsg = null;
String parameterName = e.getParameterName();
if (!"".equals(parameterName)) {
errorMsg = parameterName + "不能为空";
}
if (errorMsg != null) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR);
}
// =========== 服务端异常 =========
//2001 业务方法参数检查不通过
@ExceptionHandler(value = IllegalArgumentException.class)
public JsonResult<Object> handle(IllegalArgumentException e) {
log.error("[业务方法参数检查不通过]", e);
return JsonResult.buildError(CommonErrorCodeEnum.SERVER_ILLEGAL_ARGUMENT_ERROR);
}
//系统自定义业务异常
@ExceptionHandler(value = BaseBizException.class)
public JsonResult<Object> handle(BaseBizException e) {
log.error("[ 业务异常 ]", e);
return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());
}
}
二.继承基础业务异常类BaseBizException
在业务代码开发中,可以直接抛出这个异常类,也可以在具体的业务代码⼯程新建⼀个类继承BaseBizException,然后抛出⾃定义的异常类。⽐如在订单中⼼,新建⼀个OrderBizException⾃定义类,然后继承BaseBizException,最后在业务代码中抛出OrderBizException。
typescript
//基础自定义业务异常
public class BaseBizException extends RuntimeException {
//默认错误码
private static final String DEFAULT_ERROR_CODE = "-1";
private String errorCode;
private String errorMsg;
public BaseBizException(String errorMsg) {
super(errorMsg);
this.errorCode = DEFAULT_ERROR_CODE;
this.errorMsg = errorMsg;
}
public BaseBizException(String errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum) {
super(baseErrorCodeEnum.getErrorMsg());
this.errorCode = baseErrorCodeEnum.getErrorCode();
this.errorMsg = baseErrorCodeEnum.getErrorMsg();
}
public BaseBizException(String errorCode, String errorMsg, Object... arguments) {
super(MessageFormat.format(errorMsg, arguments));
this.errorCode = errorCode;
this.errorMsg = MessageFormat.format(errorMsg, arguments);
}
public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum, Object... arguments) {
super(MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments));
this.errorCode = baseErrorCodeEnum.getErrorCode();
this.errorMsg = MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments);
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
//订单中心自定义业务异常类
public class OrderBizException extends BaseBizException {
public OrderBizException(String errorMsg) {
super(errorMsg);
}
public OrderBizException(String errorCode, String errorMsg) {
super(errorCode, errorMsg);
}
public OrderBizException(BaseErrorCodeEnum baseErrorCodeEnum) {
super(baseErrorCodeEnum);
}
public OrderBizException(String errorCode, String errorMsg, Object... arguments) {
super(errorCode, errorMsg, arguments);
}
public OrderBizException(BaseErrorCodeEnum baseErrorCodeEnum, Object... arguments) {
super(baseErrorCodeEnum, arguments);
}
}
@Service
public class OrderServiceImpl implements OrderService {
...
//风控检查
private void checkRisk(CreateOrderRequest createOrderRequest) {
//调用风控服务进行风控检查
CheckOrderRiskRequest checkOrderRiskRequest = createOrderRequest.clone(CheckOrderRiskRequest.class);
JsonResult<CheckOrderRiskDTO> jsonResult = riskApi.checkOrderRisk(checkOrderRiskRequest);
if (!jsonResult.getSuccess()) {
throw new OrderBizException(jsonResult.getErrorCode(), jsonResult.getErrorMessage());
}
}
...
}
三.提供BaseErrorCodeEnum错误信息枚举接⼝
各个业务服务在⾃定义错误枚举信息时,会继承该接口。在抛出业务异常时,统⼀使⽤错误枚举信息,可以很好地集中管理错误信息并定义错误码。
swift
public enum OrderErrorCodeEnum implements BaseErrorCodeEnum {
//正向订单错误码105开头
USER_ID_IS_NULL("105001", "用户ID不能为空"),
ORDER_NO_TYPE_ERROR("105002", "订单号类型错误"),
CREATE_ORDER_REQUEST_ERROR("105003", "生单请求参数错误"),
ORDER_ID_IS_NULL("105004", "订单号不能为空"),
BUSINESS_IDENTIFIER_IS_NULL("105005", "业务线标识不能为空"),
BUSINESS_IDENTIFIER_ERROR("105006", "业务线标识错误"),
ORDER_TYPE_IS_NULL("105007", "订单类型不能为空"),
ORDER_TYPE_ERROR("105008", "订单类型错误"),
SELLER_ID_IS_NULL("105009", "卖家ID不能为空"),
USER_ADDRESS_ERROR("105010", "地址信息错误"),
DELIVERY_TYPE_IS_NULL("105011", "配送类型不能为空"),
USER_LOCATION_IS_NULL("105011", "地理位置不能为空"),
ORDER_RECEIVER_IS_NULL("105012", "收货人不能为空"),
ORDER_CHANNEL_IS_NULL("105013", "下单渠道信息不能为空"),
CLIENT_IP_IS_NULL("105014", "客户端IP不能为空"),
ORDER_ITEM_IS_NULL("105015", "订单商品信息不能为空"),
ORDER_ITEM_PARAM_ERROR("105016", "订单商品信息错误"),
ORDER_AMOUNT_IS_NULL("105017", "订单费用信息不能为空"),
ORDER_AMOUNT_TYPE_IS_NULL("105018", "订单费用类型不能为空"),
ORDER_AMOUNT_TYPE_PARAM_ERROR("105019", "订单费用类型错误"),
ORDER_ORIGIN_PAY_AMOUNT_IS_NULL("105020", "订单支付原价不能为空"),
ORDER_SHIPPING_AMOUNT_IS_NULL("105021", "订单运费不能为空"),
ORDER_REAL_PAY_AMOUNT_IS_NULL("105022", "订单实付金额不能为空"),
ORDER_DISCOUNT_AMOUNT_IS_NULL("105023", "订单优惠券抵扣金额不能为空"),
ORDER_PAYMENT_IS_NULL("105024", "订单支付信息不能为空"),
PAY_TYPE_PARAM_ERROR("105025", "支付类型错误"),
ACCOUNT_TYPE_PARAM_ERROR("105026", "账户类型错误"),
PRODUCT_SKU_CODE_ERROR("105027", "商品{0}不存在"),
CALCULATE_ORDER_AMOUNT_ERROR("105028", "计算订单价格失败"),
ORDER_CHECK_REAL_PAY_AMOUNT_FAIL("105029", "订单验价失败"),
ORDER_NOT_ALLOW_INFORM_WMS_RESULT("105029", "订单不允许通知物流配送结果"),
ORDER_NOT_ALLOW_TO_DELIVERY("105030", "订单不允许配送"),
ORDER_NOT_ALLOW_TO_SIGN("105031", "订单不允许签收"),
SKU_CODE_IS_NULL("105032", "skuCode 不能为空"),
AFTER_SALE_ID_IS_NULL("105033", "售后ID不能为空"),
LACK_ITEM_IS_NULL("105034", "缺品项不能为空"),
LACK_NUM_IS_LT_0("105035", "缺品数量不能小于0"),
ORDER_NOT_FOUND("105036", "查无此订单"),
LACK_ITEM_NOT_IN_ORDER("105037", "缺品商品并未下单"),
LACK_NUM_IS_GE_SKU_ORDER_ITEM_SIZE("105038", "缺品数量不能大于或等于下单商品数量"),
ORDER_NOT_ALLOW_TO_LACK("105039", "订单不允许发起缺品"),
REGION_ID_IS_NULL("105040", "区域ID不能为空"),
ORDER_PAY_AMOUNT_ERROR("105041", "订单支付金额错误"),
ORDER_PRE_PAY_ERROR("105042", "订单支付发生错误"),
ORDER_PRE_PAY_EXPIRE_ERROR("105042", "已经超过支付订单的截止时间"),
ORDER_PAY_CALLBACK_ERROR("105043", "订单支付回调发生错误"),
ORDER_INFO_IS_NULL("105044", "订单信息不存在"),
ORDER_CALLBACK_PAY_AMOUNT_ERROR("105045", "订单支付金额错误"),
ORDER_CANCEL_PAY_CALLBACK_ERROR("105046", "接收到支付回调时,订单已经取消"),
ORDER_CANCEL_PAY_CALLBACK_PAY_TYPE_SAME_ERROR("105047", "接收到支付回调的时候订单已经取消,且支付回调为同种支付方式"),
ORDER_CANCEL_PAY_CALLBACK_PAY_TYPE_NO_SAME_ERROR("105047", "接收到支付回调的时候订单已经取消,且支付回调非同种支付方式"),
ORDER_CANCEL_PAY_CALLBACK_REPEAT_ERROR("105048", "不同支付方式下的重复支付回调"),
ORDER_CANNOT_REMOVE("105046", "订单不允许删除"),
ORDER_NOT_ALLOW_TO_ADJUST_ADDRESS("105047", "订单不允许调整配送地址"),
ORDER_DELIVERY_NOT_FOUND("105048", "订单配送记录不存在"),
ORDER_DELIVERY_ADDRESS_HAS_BEEN_ADJUSTED("105049", "订单配送地址已被调整过"),
ORDER_FULFILL_ERROR("105050", "订单履约失败"),
AFTER_SALE_NOT_FOUND("105051", "查无此售后单"),
AFTER_SALE_CANNOT_REVOKE("105052", "售后单无法撤销"),
ORDER_STATUS_ERROR("105049", "订单状态异常"),
ORDER_PAY_STATUS_IS_PAID("105050", "订单已经是已完成支付状态"),
RETURN_GOODS_REQUEST_IS_NULL("105051", "手动退货入参不能为空"),
AFTER_SALE_TIME_IS_NULL("105052", "申请售后时间不能为空"),
ORDER_CURRENT_TYPE_IS_NULL("105053", "当前订单状态不能为空"),
SKU_IS_NULL("105054", "sku列表不能为空"),
RETURN_GOODS_NUM_IS_NULL("105055", "退货数量不能为空"),
RETURN_GOODS_CODE_IS_NULL("105056", "退货原因选项不能为空"),
AFTER_SALE_TYPE_IS_NULL("105057", "售后类型不能为空"),
ORDER_STATUS_CHANGED("105058", "当前订单状态已变更,请重新刷新"),
ORDER_STATUS_CANCELED("105058", "当前订单已取消"),
ORDER_STATUS_IS_NULL("105059", "当前订单状态不能为空"),
ORDER_PAY_TRADE_NO("105060", "当前订单已产生支付流水号,不能取消"),
CANCEL_REQUEST_IS_NULL("105061", "取消订单入参不能为空"),
CANCEL_TYPE_IS_NULL("105062", "取消订单类型不能为空"),
CANCEL_ORDER_ID_IS_NULL("105063", "取消订单ID不能为空"),
CANCEL_USER_ID_IS_NULL("105064", "取消订单用户ID不能为空"),
CANCEL_ORDER_REPEAT("105065", "取消订单重复"),
CANCEL_ORDER_FULFILL_ERROR("105066", "调用履约系统失败"),
PROCESS_REFUND_REPEAT("105067", "处理退款重复"),
CALCULATING_REFUND_AMOUNT_FAILED("105071", "取消订单用户ID不能为空"),
PROCESS_REFUND_FAILED("105072", "退款前准备工作失败"),
SEND_MQ_FAILED("105073", "发送MQ消息失败"),
CONSUME_MQ_FAILED("105074", "消费MQ消息失败"),
ORDER_REFUND_AMOUNT_FAILED("105075", "调用支付退款接口失败"),
PROCESS_PAY_REFUND_CALLBACK_REPEAT("105076", "处理支付退款回调重复"),
PROCESS_PAY_REFUND_CALLBACK_FAILED("105077", "处理支付退款回调失败"),
PROCESS_PAY_REFUND_CALLBACK_BATCH_NO_IS_NULL("105078", "处理支付退款回调批次号不能为空"),
PROCESS_PAY_REFUND_CALLBACK_STATUS_NO_IS_NUL("105079", "处理支付退款回调退款状态不能为空"),
PROCESS_PAY_REFUND_CALLBACK_FEE_NO_IS_NUL("105080", "处理支付退款回调退款金额不能为空"),
PROCESS_PAY_REFUND_CALLBACK_TOTAL_FEE_NO_IS_NUL("105081", "处理支付退款回调退款总额不能为空"),
PROCESS_PAY_REFUND_CALLBACK_SIGN_NO_IS_NUL("105082", "处理支付退款回调签名不能为空"),
PROCESS_PAY_REFUND_CALLBACK_TRADE_NO_IS_NUL("105083", "处理支付退款回调交易号不能为空"),
PROCESS_AFTER_SALE_RETURN_GOODS("105084", "处理售后退款重复"),
PROCESS_PAY_REFUND_CALLBACK_AFTER_SALE_ID_IS_NULL("105085", "处理支付退款回调售后订单号不能为空"),
PROCESS_PAY_REFUND_CALLBACK_AFTER_SALE_REFUND_TIME_IS_NULL("105086", "处理支付退款回调售后退款时间不能为空"),
REPEAT_CALLBACK("105087", "重复的退款回调"),
REPEAT_CALL("105088", "当前接口被重复调用"),
REFUND_MONEY_REPEAT("105089", "执行退款操作重复"),
ORDER_CANNOT_REPEAT_OPERATE("105090", "当前订单不能重复操作"),
PROCESS_APPLY_AFTER_SALE_CANNOT_REPEAT("105091", "不能重复发起售后"),
CUSTOMER_AUDIT_CANNOT_REPEAT("105092", "不能重复发起客服审核"),
AFTER_SALE_ITEM_CANNOT_NULL("105093", "售后商品信息不能为空"),
//通用异常
COLLECTION_PARAM_CANNOT_BEYOND_MAX_SIZE("108001", "[{0}]大小不能超过{1}"),
ENUM_PARAM_MUST_BE_IN_ALLOWABLE_VALUE("108002", "[{0}]的取值必须为{1}"),
DELIVERY_TYPE_ERROR("105080", "配送类型错误"),
;
private String errorCode;
private String errorMsg;
...
}
(5)统一返回结果处理规范
规范一: Web层各个Controller组件可统⼀使⽤JsonResult组件作为返回值,JsonResult主要是定义了统⼀返回给前端的Json格式。
其中,success字段表示请求是否成功,data字段表业务数据。请求成功时才会返回业务数据,请求失败时data是null。当success字段为false时,表示请求失败,此时errorCode与errorMessage才会有值,errorCode与errorMessage分别表示错误码与错误提示信息。
typescript
//统一的Spring mvc响应结果封装对象
public class JsonResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
private static final boolean REQUEST_SUCCESS = true;//请求成功
private static final boolean REQUEST_FAIL = false;//请求失败
private static final String DEFAULT_ERROR_CODE = "-1";//默认错误码
private Boolean success;//请求是否成功
private T data;//业务数据
private String errorCode;//错误码
private String errorMessage;//错误提示语
public JsonResult() {
}
public JsonResult(Boolean success, T data, String errorCode, String errorMessage) {
this.success = success;
this.data = data;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
//成功,不用返回数据
public static <T> JsonResult<T> buildSuccess() {
return new JsonResult<>(REQUEST_SUCCESS, null, null, null);
}
//成功,返回数据
public static <T> JsonResult<T> buildSuccess(T data) {
return new JsonResult<>(REQUEST_SUCCESS, data, null, null);
}
//失败,固定状态码
public static <T> JsonResult<T> buildError(String errorMsg) {
return new JsonResult<>(REQUEST_FAIL, null, DEFAULT_ERROR_CODE, errorMsg);
}
//失败,自定义错误码和信息
//@param errorCode 错误码
//@param errorMsg 错误提示
public static <T> JsonResult<T> buildError(String errorCode, String errorMsg) {
return new JsonResult<>(REQUEST_FAIL, null, errorCode, errorMsg);
}
//失败,枚举类定义错误码和信息
public static <T> JsonResult<T> buildError(BaseErrorCodeEnum baseErrorCodeEnum) {
return new JsonResult<>(REQUEST_FAIL, null, baseErrorCodeEnum.getErrorCode(), baseErrorCodeEnum.getErrorMsg());
}
...
}
规范二: Controller中的⽅法也可以不返回JsonResult组件,⽽返回原样的对象,最后会由框架中的GlobalResponseBodyAdvice组件来统⼀拦截处理。
这个组件通过实现ResponseBodyAdvice接口 + 添加@RestControllerAdvice注解,实现了全局Controller⽅法的默认的返回结果格式的统⼀处理。其中,处理逻辑都在beforeBodyWrite()⽅法中。
typescript
//默认的Controller全局响应结果处理增强组件
@RestControllerAdvice
@Order
public class GlobalResponseBodyAdvice implements ResponseBodyAdvice<Object> {
//是否支持advice功能
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
Class<?> declaringClass = returnType.getDeclaringClass();
if (declaringClass.equals(ApiResourceController.class) || declaringClass.equals(Swagger2Controller.class)) {
return false;
}
if (declaringClass.equals(BasicErrorController.class)) {
return false;
}
return true;
}
//处理response的具体业务方法
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (!selectedContentType.equalsTypeAndSubtype(MediaType.APPLICATION_JSON)) {
return body;
}
if (body instanceof JsonResult || body instanceof JsonMap) {
return body;
} else if (body instanceof String) {
try {
HttpServletResponse httpServletResponse = ServletUtil.getResponse();
if (httpServletResponse != null) {
ServletUtil.writeJsonMessage(httpServletResponse, JsonResult.buildSuccess(body));
return null;
}
} catch (Exception e) {
log.warn("响应字符串对象给前端异常", e);
}
return JsonUtil.object2Json(JsonResult.buildSuccess(body));
}
return JsonResult.buildSuccess(body);
}
}
规范三: 如果某接⼝就想返回原样的对象,不想让返回结果被JsonResult封装,那么可以让Controller⽅法返回一个JsonMap对象。
GlobalResponseBodyAdvice.beforeBodyWrite()⽅法会对类型为JsonMap的body进行直接返回。
java
//自定义Map实现,完全兼容java.util.HashMap
public class JsonMap<K, V> extends HashMap<K, V> {
public JsonMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public JsonMap(int initialCapacity) {
super(initialCapacity);
}
public JsonMap() {
}
public JsonMap(Map<? extends K, ? extends V> m) {
super(m);
}
}
5.分布式订单系统的DDD领域模型实践简述
(1)领域模型设计规范
(2)分层架构实现
(3)基础设施实现
(4)生产级校验规则
(1)领域模型设计规范
一.聚合根设计原则
less
单一职责:每个聚合根仅负责一个核心业务生命周期(如OrderAggregate仅处理订单状态流转)
强一致性边界:聚合根内所有修改需保证事务一致性(通过@Transactional或分布式事务)
唯一标识:聚合根ID采用业务语义明确的组合键(如order_id + tenant_id)
typescript
// 订单聚合根
public class OrderAggregate {
private OrderId id; // 值对象,包含订单ID和租户ID
private OrderStatus status;
private List<OrderItem> items;
private PaymentDetail payment;
// 工厂方法: 保证聚合根的创建合法性
public static OrderAggregate create(CreateOrderCommand command, InventoryService inventory) {
// 校验库存
if (!inventory.checkStock(command.getItems())) {
throw new DomainException("库存不足");
}
OrderAggregate order = new OrderAggregate();
order.id = OrderId.generate(command.getTenantId());
order.status = OrderStatus.CREATED;
order.items = command.getItems().stream().map(OrderItem::fromCommand).collect(Collectors.toList());
order.addEvent(new OrderCreatedEvent(order.id));
return order;
}
// 领域行为:支付订单(包含业务规则校验)
public void pay(PaymentCommand command) {
if (this.status != OrderStatus.CREATED) {
throw new DomainException("订单状态非法");
}
this.payment = PaymentDetail.fromCommand(command);
this.status = OrderStatus.PAID;
this.addEvent(new OrderPaidEvent(this.id, command.getAmount()));
}
}
二.值对象设计
arduino
不可变性:所有属性final,通过构造函数注入
自验证:在构造函数中校验业务规则
kotlin
// 值对象示例:订单金额(包含业务规则校验)
public class OrderAmount {
private final BigDecimal total;
private final BigDecimal discount;
private final BigDecimal shippingFee;
public OrderAmount(BigDecimal total, BigDecimal discount, BigDecimal shipping) {
if (total.compareTo(BigDecimal.ZERO) < 0) {
throw new DomainException("金额不能为负数");
}
this.total = total.setScale(2, RoundingMode.HALF_UP);
this.discount = discount.setScale(2, RoundingMode.HALF_UP);
this.shippingFee = shipping.setScale(2, RoundingMode.HALF_UP);
}
// 业务方法:计算实付金额
public BigDecimal getRealPayAmount() {
return total.subtract(discount).add(shippingFee);
}
}
(2)分层架构实现
一.分层依赖规范
严格单向依赖:接口层 → 应用层 → 领域层 ← 基础设施层
防腐层(ACL):所有外部服务调用必须通过适配器隔离
kotlin
// 防腐层实现示例(支付服务调用)
public class PaymentAdapter {
@DubboReference(version = "1.0.0", timeout = 3000)
private PaymentService paymentService;
public PaymentResult process(PaymentCommand command) {
// 转换领域对象为DTO
PaymentRequest request = PaymentConvertor.toRequest(command);
// 调用外部服务(含容错逻辑)
return RetryTemplate.execute(() -> {
return paymentService.process(request);
}, e -> {
throw new InfrastructureException("支付服务调用失败", e);
});
}
}
二.应用层实现
css
事务管理:使用@Transactional注解,仅处理跨聚合的协调
DTO转换:禁止领域对象直接暴露给接口层
java
// 应用服务示例
@Service
@RequiredArgsConstructor
public class OrderAppService {
private final OrderRepository orderRepository;
private final PaymentAdapter paymentAdapter;
private final EventPublisher eventPublisher;
@Transactional
public OrderResult createOrder(CreateOrderCommand command) {
// 调用领域层
OrderAggregate order = OrderAggregate.create(command, inventoryService);
// 持久化
orderRepository.save(order);
// 发布领域事件
eventPublisher.publish(order.getEvents());
// 返回DTO
return OrderConvertor.toResult(order);
}
}
(3)基础设施实现
一.仓库实现(Repository)
聚合根持久化:一个聚合对应一个Repository,禁止跨聚合查询
分库分表:通过ShardingSphere实现按tenant_id分片
less
// 仓库实现示例(MyBatis+ShardingSphere)
@Repository
public class OrderRepositoryImpl implements OrderRepository {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper itemMapper;
@Override
@Transactional
public void save(OrderAggregate order) {
// 保存聚合根
OrderDO orderDO = OrderConvertor.toDO(order);
orderMapper.insert(orderDO);
// 保存子实体
order.getItems().forEach(item -> {
itemMapper.insert(OrderItemConvertor.toDO(item, order.getId()));
});
}
@Override
public OrderAggregate findById(OrderId id) {
// 查询聚合根及子实体
OrderDO orderDO = orderMapper.selectById(id.toString());
List<OrderItemDO> itemDOs = itemMapper.selectByOrderId(id.toString());
// 重建聚合根
return OrderConvertor.toAggregate(orderDO, itemDOs);
}
}
二.领域事件发布
可靠性保证:基于RocketMQ事务消息实现
幂等处理:消费者端记录事件ID
less
// 事件发布实现
@Component
public class RocketMQEventPublisher implements EventPublisher {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
@Transactional
public void publish(List<DomainEvent> events) {
events.forEach(event -> {
Message<DomainEvent> message = MessageBuilder.withPayload(event)
.setHeader("EVENT_ID", event.getEventId())
.build();
rocketMQTemplate.sendMessageInTransaction(
"order-event-topic",
message,
null
);
});
}
}
(4)生产级校验规则
一.单元测试规范
领域层:100%覆盖业务规则,禁止依赖外部资源
scss
class OrderAggregateTest {
@Test
void shouldRejectInvalidPayment() {
OrderAggregate order = OrderAggregate.create(validCommand, inventoryService);
PaymentCommand invalidCommand = new PaymentCommand("-1");
assertThrows(DomainException.class, () -> {
order.pay(invalidCommand);
});
}
}
二.集成测试规范
使用Testcontainers模拟真实中间件
less
@SpringBootTest
@Testcontainers
class OrderAppServiceIntegrationTest {
@Container
static MySQLContainer mysql = new MySQLContainer();
@Container
static NacosContainer nacos = new NacosContainer();
@Test
void shouldCreateOrder() {
// 测试完整业务流程
}
}
三.性能约束
聚合根加载时间 ≤ 50ms(通过二级缓存保障)
领域事件处理延迟 ≤ 1s(监控RocketMQ堆积量)