学习日报 20250930|多优惠券叠加核销及场景互斥逻辑
要实现多优惠券叠加核销及场景互斥逻辑,需从规则定义、校验流程、核销执行三方面入手,结合优惠券模板(CouponTemplate
)的配置来控制业务逻辑。以下是具体实现方案:
一、核心思路
通过优惠券模板(CouponTemplate
)配置每张优惠券的叠加规则和适用场景,在核销前先校验多券的兼容性,再按规则执行核销。
二、具体实现步骤
1. 扩展优惠券模板(CouponTemplate
)
import lombok.Data;
import java.util.List;/*** 优惠券模板:定义优惠券的基础规则(可叠加、适用场景等)*/
@Data
public class CouponTemplate {private Long id;private String name;private CouponTypeEnum type; // 优惠券类型(满减、折扣等)/** 可叠加的优惠券类型(为空表示可与所有类型叠加;非空则仅能与指定类型叠加) */private List<CouponTypeEnum> allowStackWith;/** 适用场景标签(如 "生鲜"、"数码"、"全品类" 等) */private List<String> sceneTags;
}
2. 多优惠券叠加核销实现
步骤 1:校验多券叠加规则在核销前,先检查所有优惠券是否满足 “可叠加” 规则:
/*** 校验多优惠券是否可叠加* @param coupons 待核销的优惠券列表(需包含关联的 CouponTemplate)* @return 是否可叠加*/
private boolean checkStackable(List<UserCoupon> coupons) {// 若只有1张券,默认可核销if (coupons.size() <= 1) return true;// 遍历每两张券,检查是否满足互相叠加规则for (int i = 0; i < coupons.size(); i++) {for (int j = i + 1; j < coupons.size(); j++) {UserCoupon couponA = coupons.get(i);UserCoupon couponB = coupons.get(j);CouponTemplate templateA = couponA.getTemplate();CouponTemplate templateB = couponB.getTemplate();// 检查 A 是否允许与 B 类型叠加if (!templateA.getAllowStackWith().contains(templateB.getType())) {log.warn("优惠券{}(类型:{})不允许与优惠券{}(类型:{})叠加",couponA.getId(), templateA.getType(),couponB.getId(), templateB.getType());return false;}}}return true;
}
步骤 2:执行多券核销若校验通过,循环调用各券的核销逻辑:
public void writeOffMultipleCoupons(List<UserCoupon> coupons) {// 1. 校验叠加规则if (!checkStackable(coupons)) {throw new CouponStackException("优惠券不可叠加");}// 2. 循环核销每张券for (UserCoupon coupon : coupons) {CouponTypeEnum type = coupon.getTemplate().getType();ICouponWriteOffService service = factory.getWriteOffService(type);service.writeOff(coupon);}
}
3. 多优惠券场景互斥实现
步骤 1:定义订单与商品的场景标签订单或商品需携带 “场景标签”,与优惠券模板的 sceneTags
匹配:
@Data
public class Order {private Long id;private List<OrderItem> items; // 订单商品private List<String> sceneTags; // 订单整体场景(如 "全品类")
}@Data
public class OrderItem {private Long id;private String sceneTag; // 商品所属场景(如 "生鲜")private BigDecimal price;
}
步骤 2:校验优惠券场景兼容性
/*** 校验优惠券与订单场景是否匹配* @param coupons 待核销优惠券* @param order 目标订单* @return 是否匹配*/
private boolean checkSceneMatch(List<UserCoupon> coupons, Order order) {for (UserCoupon coupon : coupons) {CouponTemplate template = coupon.getTemplate();List<String> couponScenes = template.getSceneTags();// 检查订单整体场景是否匹配boolean orderSceneMatch = order.getSceneTags().stream().anyMatch(couponScenes::contains);// 检查订单商品场景是否匹配boolean itemSceneMatch = order.getItems().stream().anyMatch(item -> couponScenes.contains(item.getSceneTag()));if (!orderSceneMatch && !itemSceneMatch) {log.warn("优惠券{}(场景:{})与订单场景不匹配",coupon.getId(), couponScenes);return false;}}return true;
}
步骤 3:整合场景校验到核销流程
public void writeOffCouponsWithSceneCheck(List<UserCoupon> coupons, Order order) {// 1. 校验场景匹配if (!checkSceneMatch(coupons, order)) {throw new CouponSceneException("优惠券场景不匹配");}// 2. 校验叠加规则(复用之前的 checkStackable 方法)if (!checkStackable(coupons)) {throw new CouponStackException("优惠券不可叠加");}// 3. 执行核销writeOffMultipleCoupons(coupons);
}
三、总结
- 多券叠加:通过
CouponTemplate
的allowStackWith
配置可叠加的优惠券类型,核销前校验每两张券是否满足互相叠加规则。 - 场景互斥:通过
CouponTemplate
的sceneTags
配置适用场景,核销前校验优惠券场景与订单 / 商品场景是否匹配。 - 优势:所有规则通过
CouponTemplate
外部配置,无需修改代码即可灵活控制优惠券的叠加和场景逻辑,扩展性极强。