建设部网站继续教育,无锡网页制作服务,重庆做网站建设公司排名,上海网站建设免在电商系统中#xff0c;促销计算是业务逻辑最复杂、变更最频繁的模块之一。它不仅需要处理多种促销类型#xff08;满减、折扣、优惠券等#xff09;#xff0c;还要管理它们之间的优先级和互斥关系。
Shoptnt 设计了一套基于 策略模式 (Strategy Pattern) 和 责任链模式…在电商系统中促销计算是业务逻辑最复杂、变更最频繁的模块之一。它不仅需要处理多种促销类型满减、折扣、优惠券等还要管理它们之间的优先级和互斥关系。
Shoptnt 设计了一套基于 策略模式 (Strategy Pattern) 和 责任链模式 (Chain of Responsibility) 的促销计算引擎实现了极高的灵活性和可扩展性。本文将深入剖析其核心实现揭示其如何优雅地管理复杂的促销规则。
项目地址 https://gitee.com/bbc-se/shoptnt
一、 核心架构PromotionHandler 策略接口
一切的核心是 PromotionHandler 接口。它定义了一个促销计算单元的契约是一种典型的策略模式应用。
java
public interface PromotionHandler {// 策略方法执行促销计算ListPromotionResult execute(Promotion promotion, ListSkuDeal skuDealList);// 标识策略类型处理哪种促销如 HALF_PRICEPromotionTypeEnum promotionType();// 标识策略层级处理哪个层级SKU, SHOP, PLATFORMPromotionLevel promotionLevel();
}
设计要点 单一职责 每个 PromotionHandler 只负责一种特定类型促销的计算逻辑如 HalfPriceHandler 只处理第二件半价。 开闭原则 新增促销类型时只需实现一个新的 PromotionHandler无需修改现有代码。 明确标识 promotionType() 和 promotionLevel() 方法使得调度器可以精准地找到并调用对应的处理器。
二、 调度中心PromotionCalculateClientImpl
PromotionCalculateClientImpl 是促销计算的调度中心或上下文 (Context)。它的核心作用是收集所有 PromotionHandler 策略并按需调用它们。
1. 自动收集所有策略 通过 Spring 的依赖注入所有实现了 PromotionHandler 的 Bean 都会被自动注入到 promotionHandlerList 中。
java
Autowired
private ListPromotionHandler promotionHandlerList; // 所有促销策略的集合
2. 分层过滤与执行 在 calculateShopPromotion 方法中调度器首先过滤出非平台级别的处理器!handler.promotionLevel().equals(PromotionLevel.platform)然后遍历这些处理器进行计算。
java
// 1. 过滤出需要的策略店铺级和SKU级
ListPromotionHandler shopHandlerList promotionHandlerList.stream().filter(handler - !handler.promotionLevel().equals(PromotionLevel.platform)).collect(Collectors.toList());// 2. 遍历策略列表让每个策略都尝试计算
for (PromotionHandler promotionHandler : shopHandlerList) {ListPromotionResult resultList calculateShopPromotion(promotionList, promotionHandler, skuList);promotionResultList.addAll(resultList);
}
3. 策略匹配 在 calculateShopPromotion (私有方法) 中调度器会遍历所有促销活动将活动类型与处理器的类型进行匹配。只有匹配的处理器才会被执行。
java
private ListPromotionResult calculateShopPromotion(ListPromotion promotionList,PromotionHandler promotionHandler,ListSkuDeal skuList) {for (Promotion promotion : promotionList) {// 关键促销活动类型 必须 匹配 处理器类型if (promotion.getType().equals(promotionHandler.promotionType())) {// 匹配成功执行该策略的计算逻辑ListPromotionResult promotionResults promotionHandler.execute(promotion, skuList);promotionResultList.addAll(promotionResults);}}
}
这个过程形成了一个隐式的责任链调度器将促销活动和商品信息传递给一系列处理器每个处理器只处理自己关心的那部分。
三、 策略实现以 HalfPriceHandler 为例
让我们以 HalfPriceHandler 为例看一个具体的策略是如何实现的。
1. 标识身份
java
Service
Order(PromotionOrder.HalfPrice) // 定义计算优先级
public class HalfPriceHandler implements PromotionHandler {Overridepublic PromotionTypeEnum promotionType() {return PromotionTypeEnum.HALF_PRICE; // 我负责处理第二件半价}Overridepublic PromotionLevel promotionLevel() {return PromotionLevel.sku; // 我是SKU级别的活动}
}
Order(PromotionOrder.HalfPrice) 注解至关重要它定义了该处理器在 promotionHandlerList 中的执行顺序确保了“单品优惠”先于“组合优惠”计算。
2. 核心计算逻辑 (execute 方法) 遍历商品 处理器会遍历传入的所有商品 (SkuDeal)。 检查资格 检查商品是否参与了当前的第二件半价活动 (promotion.getSkuIdList().contains(...))。 计算优惠 如果满足条件则计算优惠金额。逻辑是优惠金额 (购买数量 / 2) * (单价 / 2)。 构建结果 将计算结果封装成一个 SkuPromotionResult 对象并返回。注意它修改了 SkuDeal 的 subtotal小计金额这个修改后的值会传递给后续的处理器从而实现促销的叠加计算。
java
// 在 handle 方法中
double subtotal skuDeal.getSubtotal(); // 获取当前小计可能已被之前的处理器优惠过
subtotal CurrencyUtil.sub(subtotal, discount); // 减去本次优惠金额
skuDeal.setSubtotal(subtotal); // 设置新的小计影响后续计算
四、 计算顺序的控制Order 注解
PromotionOrder 类定义了不同促销类型的执行顺序这是保证复杂促销规则能正确叠加的关键。
java
public class PromotionOrder {public static final int Minus 10; // 单品立减public static final int Seckill 15; // 秒杀public static final int HalfPrice 15; // 第二件半价public static final int FullMinus 25; // 满减public static final int ShopCoupon 30; // 店铺券public static final int PlatformCoupon 35; // 平台券
}
执行顺序规则 价格直降型优先 如 Minus立减、Seckill秒杀、HalfPrice第二件半价等直接修改商品单价的活动最先计算。 满减活动次之 FullMinus满减等基于总价条件的活动随后计算。 优惠券最后 ShopCoupon 和 PlatformCoupon 最后计算因为它们通常是基于所有优惠后的最终金额进行减免。
这种顺序符合商业直觉先享受单品折扣再享受满减优惠最后用券抵扣。
五、 结果的统一抽象PromotionResult 体系
所有促销计算的结果都统一返回为 PromotionResult 或其子类 (SkuPromotionResult, ShopPromotionResult, PlatformPromotionResult)。这种设计 统一了返回格式 无论何种促销应用端 (cart 模块) 都使用同一套接口来处理结果。 包含了丰富信息 不仅包含优惠金额 (cashBack)还包含赠品信息 (giftList)、运费减免 (isFreeFreight)、提示信息 (promotionTips) 等。 支持多态 使用 JsonTypeInfo 注解方便在序列化和反序列化时自动处理不同的子类。
总结如何新增一个促销规则
假设我们要增加一个“买三送一”的活动。 定义促销类型 在 PromotionTypeEnum 中新增 BUY_THREE_GET_ONE。 实现策略处理器 创建一个新的 BuyThreeGetOneHandler 类实现 PromotionHandler 接口。 在 promotionType() 中返回 BUY_THREE_GET_ONE。 在 promotionLevel() 中返回 PromotionLevel.sku。 在 execute() 方法中实现“买三送一”的逻辑计算应赠送的数量并可能修改 SkuDeal 的数量或设置赠品信息到 PromotionResult 中。 定义执行顺序 在 PromotionOrder 中为其定义一个顺序值例如 18位于单品折扣和满减之间。 完成 由于调度器是自动收集所有 PromotionHandler 的你的新处理器会自动被纳入计算流程无需修改任何调度逻辑。
架构优势 极致解耦 计算逻辑 (promotion 模块) 与应用逻辑 (cart 模块) 完全分离通过 PromotionResult DTO 进行通信。 高可扩展性 新增促销类型如同插拔组件符合开闭原则。 灵活的计算顺序 通过 Order 轻松管理复杂的优先级和叠加规则。 易于测试 每个 PromotionHandler 都可以被单独测试。
Shoptnt 的促销计算引擎是一个经典且优秀的设计范例完美展示了如何用设计模式解决复杂的业务问题。欢迎访问项目源码深入学习
https://gitee.com/bbc-se/shoptnt