设计模式实战-责任链模式
一.场景描述
我们业务开发中,碰到复杂的业务逻辑,有些人会把代码写得非常冗长,代码之间纵横交错,阅读性差且不易拓展和维护,比如下面这段代码(不是我写的--滑稽):
java
@Transactional(rollbackFor = Exception.class)
public Boolean updateWmsOut(DmsSaleShippingReq req) {
log.info("销售单-WMS处理的消息通知,监测[{}]", JacksonUtils.obj2String(req));
// 获取发货单
DmsSaleShippingDO shippingDO = dmsSaleShippingReadRepository.selectById(req.getId());
Assert.notNull(shippingDO, "发货单为空, id:" + req.getId());
Assert.isTrue(req.getShippingCode().equals(shippingDO.getShippingCode()), "发货单编码不一致, id:" +
req.getId() +
", 入参编码:" +
req.getShippingCode() +
", 原始编码:" +
shippingDO.getShippingCode());
// 设置销售单ID,后续发消息要用到 勿删
req.setSaleId(shippingDO.getSaleId());
// 销售单code,作为分布式锁
redisDistributedLockTemplate.execute(CodePrefixEnum.DXF.getPrefix() + shippingDO.getSaleCode(), EXPIRE_TIME_FOR_LOCK, () -> {
// 统计发货单申请数量和发货数量
BigDecimal shippingApplySum = BigDecimal.ZERO; // 发货单维度-申请数量
BigDecimal shippingActualSum = BigDecimal.ZERO; // 发货单维度-实发数量
List<DmsSaleShippingItemDO> shippingItemDOList = dmsSaleShippingItemService.getDOListBySaleShippingId(shippingDO.getId());
if (CollectionUtils.isNotEmpty(shippingItemDOList)) {
for (DmsSaleShippingItemDO itemDO : shippingItemDOList) {
shippingApplySum = shippingApplySum.add(itemDO.getApplyNum());
shippingActualSum = shippingActualSum.add(itemDO.getActualNum());
}
}
// 统计销售单申请数量和发货数量
BigDecimal orderAapplySum = BigDecimal.ZERO; // 销售单维度-申请梳理
BigDecimal orderActualSum = BigDecimal.ZERO; // 销售单维度-实发数量
BigDecimal orderCancelledSum = BigDecimal.ZERO; // 销售单维度-取消数量
List<SaleOrderItem> orderItemList = saleOrderItemService.getDOListByOrderId(shippingDO.getSaleId());
if (CollectionUtils.isNotEmpty(orderItemList)) {
for (SaleOrderItem orderItem : orderItemList) {
if (GoodsTypeEnum.GOODS.getCode().equals(orderItem.getIsGift())) {
orderAapplySum = orderAapplySum.add(orderItem.getStandardNumber());
} else {
orderAapplySum = orderAapplySum.add(orderItem.getProductNum());
}
orderActualSum = orderActualSum.add(orderItem.getDeliveredNum());
orderCancelledSum = orderCancelledSum.add(orderItem.getCancelledNum());
}
}
// 记录需要更新的发货单明细
List<DmsSaleShippingItemDO> updateShippingDoList = Lists.newArrayList();
// 处理发货单明细
List<DmsSaleShippingItemReq> itemReqList = req.getItemReqList();
for (DmsSaleShippingItemReq itemReq : itemReqList) {
// 获取发货单明细
DmsSaleShippingItemDO shippingItemDO = dmsSaleShippingItemService.getDOById(itemReq.getId());
Assert.notNull(shippingItemDO, "发货单详情为空, id:" + itemReq.getId());
// 统计
shippingActualSum = shippingActualSum.add(itemReq.getActualNum());
// 更新发货单明细
shippingItemDO.setActualNum(shippingItemDO.getActualNum().add(itemReq.getActualNum()))
.setWmsOutTime(itemReq.getWmsOutTime())
.setUpdateBy(req.getUpdateBy())
.setUpdateTime(req.getUpdateTime())
.setScanRecord(itemReq.getScanRecord())
.setScanExcp(itemReq.getScanExcp())
.setScanLitresEnable(itemReq.getScanLitresEnable())
.setScanNumEnable(itemReq.getScanNumEnable());
dmsSaleShippingItemService.updateDOById(shippingItemDO);
// 重置为发货数量
shippingItemDO.setActualNum(itemReq.getActualNum());
updateShippingDoList.add(shippingItemDO);
// 获取销售单明细
SaleOrderItem orderItem = saleOrderItemService.getDOById(shippingItemDO.getSaleOrderItemId());
Assert.notNull(orderItem, "销售单详情为空, id:" + shippingItemDO.getSaleOrderItemId());
orderActualSum = orderActualSum.add(itemReq.getActualNum());
// 更新销售单明细
SaleOrderItem updateOrderItem = new SaleOrderItem();
updateOrderItem.setId(orderItem.getId())
.setDeliveredNum(orderItem.getDeliveredNum().add(itemReq.getActualNum()))
.setUpdateBy(String.valueOf(req.getUpdateBy()))
.setUpdateTime(req.getUpdateTime());
saleOrderItemService.updateDOById(updateOrderItem);
}
// 更新发货单
DmsSaleShippingDO updateShippingDO = new DmsSaleShippingDO();
updateShippingDO.setId(shippingDO.getId())
.setShippingStatus(shippingActualSum.compareTo(shippingApplySum) <
0 ? ShippingStatusEnum.PART_SHIPPING.getCode() : ShippingStatusEnum.ALL_SHIPPING.getCode())
.setUpdateBy(req.getUpdateBy())
.setUpdateTime(req.getUpdateTime());
dmsSaleShippingWriteRepository.updateById(updateShippingDO);
// 更新销售单
SaleOrder orderDo = new SaleOrder();
orderDo.setId(shippingDO.getSaleId())
.setOrderStatus(orderActualSum.compareTo(orderAapplySum) <
0 ? OrderStatusEnum.PARTIAL_SHIPMENT.getCode() : OrderStatusEnum.SHIPPED.getCode())
.setUpdateBy(String.valueOf(req.getUpdateBy()))
.setUpdateTime(req.getUpdateTime());
if (orderDo.getOrderStatus().equals(OrderStatusEnum.SHIPPED.getCode())) {
orderDo.setDeliveryTime(new Date());
}
saleWriteRepository.updateById(orderDo);
SaleServiceImpl saleService = applicationContext.getBean(SaleServiceImpl.class);
SaleInfoResp saleInfoResp = saleService.searchOrder(shippingDO.getSaleId());
// 记录商品还点记录
List<DmsSaleShippingItemDO> shipItemList = dmsSaleShippingItemService.getDOListBySaleShippingId(shippingDO.getId());
SaleOrder saleOrder = saleWriteRepository.selectById(shippingDO.getSaleId());
addSaleOrderItemMapPoint(shipItemList, shippingDO, saleOrder, itemReqList);
// 更新最近发货时间
dmsStoreInfoExtendService.updateLastShippingTime(shippingDO.getRecCompanyId(), new Date());
try {
// 释放预占接口
stkCenterReleaseHoldStock(shippingDO, shippingItemDOList);
// 财务结算单
Map<Long, BigDecimal> shipItemAmountMap = addSettlementBill(saleInfoResp, shippingDO, updateShippingDoList);
shipItemAmountMap.forEach((shipItemId, bigDecimal) -> {
DmsSaleShippingItemDO shippingItemDO = new DmsSaleShippingItemDO();
shippingItemDO.setId(shipItemId);
shippingItemDO.setSettlementAmount(bigDecimal);
dmsSaleShippingItemService.updateDOById(shippingItemDO);
log.info("更新发货单明细的结算金额,明细id{},金额{}", shipItemId, bigDecimal);
});
// 全部发货情况
if (orderActualSum.compareTo(orderAapplySum) >= 0) {
// 送积分
if (SaleSourceEnum.EOrder.getCode().equals(saleInfoResp.getSource()) &&
PaymentStatusEnum.SHIPPED.getCode().equals(saleInfoResp.getPaymentStatus())) {
operateAddScore(saleInfoResp);
}
// 记录销售单状态扭转日志
DmsSaleShippingServiceImpl bean = applicationContext.getBean(DmsSaleShippingServiceImpl.class);
bean.addAllDipatchLog(shippingDO);
} else {
// 记录销售单状态扭转日志
DmsSaleShippingServiceImpl bean = applicationContext.getBean(DmsSaleShippingServiceImpl.class);
bean.addPartDipatchLog(shippingDO);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
});
return Boolean.TRUE;
}
简单说明下上述代码的功能:
上述代码是一个发货单的商品出库的一个消息通知,代码主要做了以下几件事
- 处理发货单商品数量及状态
- 处理销售单商品数量及状态
- 新增还点记录
- 释放预占的库存
- 新增财务结算单
- 赠送客户会员积分
- 新增销售单的操作日志
我们不难看出,以上代码业务逻辑不算复杂,但是看起来就不舒服, 想要在这个代码上加什么业务逻辑,需要仔细阅读代码. 归结原因是代码责任划分不清晰,哪些逻辑需要什么参数没有明确的说明.这个时候,责任链模式可以很好的解决这些问题.
二.使用责任链模式进行重构
通过分析上述代码,发现整个代码处理逻辑需要的几个数据 无非是:出库通知参数,销售单,销售单商品明细,发货单,发货单商品明细.
1.封装责任链上下文对象
java
@Data
public class SaleDeliveryWmsOutContext {
private DmsSaleShippingReq req;
private DmsSaleShippingDO shippingDO;
private List<DmsSaleShippingItemDO> shippingItemDOList;
private SaleOrder saleOrder;
private List<SaleOrderItem> orderItemList
}
2.定义具体职责
处理销售单
java
/**
* 处理销售单
*/
private Consumer<SaleDeliveryWmsOutContext> dealSaleOrder() {
return context -> {
DmsSaleShippingReq req = context.getReq();
DmsSaleShippingDO shippingDO= context.getShippingDO();
List<DmsSaleShippingItemDO> shippingItemDOList= context.getShippingItemDOList();
SaleOrder saleOrder= context.getSaleOrder();
List<SaleOrderItem> orderItemList= context.getOrderItemList()
....
};
}
处理发货单
java
/**
* 处理发货单
*/
private Consumer<SaleDeliveryWmsOutContext> dealSaleDelivery() {
return context -> {
DmsSaleShippingReq req = context.getReq();
DmsSaleShippingDO shippingDO= context.getShippingDO();
List<DmsSaleShippingItemDO> shippingItemDOList= context.getShippingItemDOList();
SaleOrder saleOrder= context.getSaleOrder();
List<SaleOrderItem> orderItemList= context.getOrderItemList()
....
};
}
新增换点记录
java
/**
* 新增换点记录
*/
private Consumer<SaleDeliveryWmsOutContext> addSaleOrderItemMapPoint() {
return context -> {
...
};
}
释放预占库存
java
/**
* 释放预占库存
*/
private Consumer<SaleDeliveryWmsOutContext> stkCenterReleaseHoldStock() {
return context -> {
...
};
}
新增财务结算单
java
/**
* 新增财务结算单
*/
private Consumer<SaleDeliveryWmsOutContext> addSettlementBill() {
return context -> {
...
};
}
赠送客户会员积分
java
/**
* 赠送客户会员积分
*/
private Consumer<SaleDeliveryWmsOutContext> operateAddScore() {
return context -> {
...
};
}
三. 按顺序构建职责链
所谓责任链,就是一个List,代码如下:
java
private List<Consumer<SaleDeliveryWmsOutContext>> getSaleDeliveryWmsOutActionList() {
List<Consumer<SaleDeliveryWmsOutContext>> consumers = new ArrayList<>();
consumers.add(dealSaleDelivery());
consumers.add(dealSaleOrder());
consumers.add(addSaleOrderItemMapPoint());
consumers.add(stkCenterReleaseHoldStock());
consumers.add(addSettlementBill());
consumers.add(operateAddScore());
return consumers;
}
四. 重构后代码
重构后代码
java
@Transactional(rollbackFor = Exception.class)
public Boolean updateWmsOut(DmsSaleShippingReq req) {
//构建责任链上下文
SaleDeliveryWmsOutContext context = buildSaleDeliveryWmsOutContext(req);
//获得责任链
List<Consumer<SaleDeliveryWmsOutContext>> actionList = getSaleDeliveryWmsOutActionList();
//执行
for (Consumer<SaleDeliveryWmsOutContext> consumer : actionList) {
consumer.accept(context);
}
return Boolean.True;
}
构建上下文对象
java
private SaleDeliveryWmsOutContext buildSaleDeliveryWmsOutContext(DmsSaleShippingReq req) {
//构建责任链上下文
SaleDeliveryWmsOutContext context = new SaleDeliveryWmsOutContext();
context.setReq(req);
...
return context;
}
五. 总结
责任链模式最大的好处就是让代码可阅读性变高, 且易于维护. 能让复杂的业务逻辑一目了然;