业务场景介绍
在出行行业中,价格的配置随随着业务的增加而增加,而刺激出行的促销活动更是频繁。在价格的计算中,通常的流程如下:
- 根据一组价格配置,计算订单基础的价格。
- 根据用户拥有的优惠对象,例如优惠券,积分,会员级别等,计算出优惠以后的价格。
计价的业务详细流程图如下:
分析上述的业务流程,得知:
- 计算价格的流程就是对原始价格的一种包装和修饰,例如,使用优惠券对优惠价格;基于用户对价格进行优惠,注册新人首单减免优惠,vip会员折扣优惠;重大节假日打折优惠,在价格计算的顶层可以使用装饰模式。
- 各类价格的计算,满足固定值,乘法规则,价格区间等不同规则,对于优惠计算方式包括抵扣规格和打折规则。这类根据不同规则实现的业务,使用策略模式实现。
- 在优惠计算时,由于是基于不同规则为切入点进行计算,针对不同的优惠业务对象,例如,优惠券,vip或新人,节假日等等,其获取优惠的计算的条件规则不一致。可以使用工厂类模式实现优惠条件的获取。
- 在其余比较细致的业务中,例如,条件的校验使用责任链模式,乘法规则中,单价和数量的获取根据不同的价格使用组合模式获取。
根据以上的设计,新增一个价格,只需配置具体价格,和计算规则即可。新增一种优惠方式,只需新增价格的优惠装饰类,新增优惠条件对象的工厂类即可,实现了业务的低耦合,高扩展性。各个业务模块详细的设计模式详细如下:
- 价格计算
定义价格计算接口PriceCalculation,代码如下:
public interface PriceCalculation {
/**
* 价格计算
*
* @param orderPriceDto 计价订单dto
* @return
*/
CalculatedPriceBo calculate(OrderPriceDto orderPriceDto);
}
价格计算接口实现类如下:
- 根据价格组计算基础实现类DefaultPriceCalculator
- 根据用户特性的价格优惠实现类PriceWithUserCalculator
- 根据优惠券的价格优惠装饰类PriceWithCouponCalculator
其代码部分如下:
/**
*默认价格计算
*/
public class DefaultPriceCalculator implements PriceCalculation {
/**
* 需要计算的价格列表
*/
private final List<PriceBo> priceList;
@Override
public CalculatedPriceBo calculate(OrderPriceDto orderPriceDto) {
if (CollectionUtils.isEmpty(priceList)) {
return null;
}
CalculatedPriceBo calculatedPrice = new CalculatedPriceBo();
// 根据计价列表计算基础价格
return calculatedPrice;
}
}
/**
*基于用户的价格计算
*/
public class PriceWithCouponCalculator implements PriceCalculation {
/**
* 使用的优惠券
*/
private final CouponListVo usedCoupon;
/**
* 价格计算代理
*/
private final PriceCalculation delegate;
@Override
public CalculatedPriceBo calculate(OrderPriceDto orderPriceDto) {
// 原始的计算价格
CalculatedPriceBo calculatedPriceBo = delegate.calculate(orderPriceDto);
// TODO根据用户特性进行优惠计算
return calculatedPriceBo;
}
}
/**
*基于优惠券的价格计算
*/
public class PriceWithCouponCalculator implements PriceCalculation {
/**
* 使用的优惠券
*/
private final CouponListVo usedCoupon;
/**
* 价格计算代理
*/
private final PriceCalculation delegate;
@Override
public CalculatedPriceBo calculate(OrderPriceDto orderPriceDto) {
// 原始的计算价格
CalculatedPriceBo calculatedPriceBo = delegate.calculate(orderPriceDto);
// 基于优惠券的优惠计价
return calculatedPriceBo;
}
}
- 价格计算策略
定义计价策略接口PricingStrategy,代码如下:
public interface PricingStrategy {
/**
* 计价价格
*/
BigDecimal doPricing(PriceBo priceBo, OrderPriceDto orderPriceDto);
}
根据价格的计算规则,使用策略模式,实现各类计算规则。如下:
- 固定值策略-FixedPricingStrategy
- 乘积策略-MulPricingStrategy
- 固定值+乘积组合策略-FixedOrMulPricingStrategy
- 价格组策略-GroupPricingStrategy
- 价格计算策略上下文-PricingStrategyContext
根据不同的计算策略实现价格计算的PricingStrategyContext,代码如下:
public class PricingStrategyContext {
private final Map<PricingStrategy.TypeEnum, PricingStrategy> pricingStrategyMap;
private PricingStrategyContext() {
ImmutableMap.Builder<PricingStrategy.TypeEnum, PricingStrategy> builder = ImmutableMap.builder();
builder.put(PricingStrategy.TypeEnum.multiplication, new MulPricingStrategy());
builder.put(PricingStrategy.TypeEnum.fixed, new FixedPricingStrategy());
builder.put(PricingStrategy.TypeEnum.mulOrFixed, new FixedOrMulPricingStrategy());
builder.put(PricingStrategy.TypeEnum.group, new GroupPricingStrategy());
builder.put(PricingStrategy.TypeEnum.remote, new RemotePricingStrategy());
pricingStrategyMap = builder.build();
}
/**
* 根据计价类型计算价格
*
* @param pricingStrategyType 价格计算类型
* @param orderPriceDto 计价参数
* @return 价格金额
*/
public static BigDecimal doPricing(PricingStrategy.TypeEnum pricingStrategyType, PriceBo priceBo, OrderPriceDto orderPriceDto) {
PricingStrategy pricingStrategy;
return Objects.nonNull(pricingStrategy = PricingStrategyContextInstance.INSTANCE.pricingStrategyMap.get(pricingStrategyType)) ?
pricingStrategy.doPricing(priceBo, orderPriceDto) : null;
}
// 单例模式
private static class PricingStrategyContextInstance {
private static final PricingStrategyContext INSTANCE = new PricingStrategyContext();
}
}
- 优惠方式计算
定义优惠计算策略接口DiscountStrategy,代码如下:
public interface DiscountStrategy {
/**
* 打折计算
*
* @param priceDetail 计价明细
* @param priceDiscountParams 打折参数
* @return 打折后的金额
*/
CalculatedPriceBo.PriceDetail doDiscount(CalculatedPriceBo.PriceDetail priceDetail, PriceDiscountParams priceDiscountParams);
}
优惠券和会员等级优惠的方式都包括对总价进行抵扣或打折。此时,也可以使用策略模式实现优惠方式的计算,如下:
- 抵扣方式-DeductionDiscountStrategy
- 打折方式-DiscountDiscountStrategy
- 打折策略上下文-DiscountStrategyContext
打折策略上下文的源码如下:
public class DiscountStrategyContext {
private final Map<DiscountStrategy.TypeEnum, DiscountStrategy> discountStrategyMap;
private DiscountStrategyContext() {
ImmutableMap.Builder<DiscountStrategy.TypeEnum, DiscountStrategy> builder = ImmutableMap.builder();
builder.put(DiscountStrategy.TypeEnum.deduction, new DeductionDiscountStrategy());
builder.put(DiscountStrategy.TypeEnum.discount, new DiscountDiscountStrategy());
this.discountStrategyMap = builder.build();
}
public static CalculatedPriceBo.PriceDetail doDiscount(DiscountStrategy.TypeEnum type,
CalculatedPriceBo.PriceDetail priceDetail, DiscountStrategy.PriceDiscountParams priceDiscountParams) {
DiscountStrategy discountStrategy;
return Objects.nonNull(discountStrategy = DiscountStrategyContextInstance.INSTANCE.discountStrategyMap.get(type))
? discountStrategy.doDiscount(priceDetail, priceDiscountParams) : null;
}
// 单例模式
private static final class DiscountStrategyContextInstance {
private static final DiscountStrategyContext INSTANCE = new DiscountStrategyContext();
}
}
- 优惠条件的设计
在优惠打折时,会根据使用到的优惠对象,例如vip用户,优惠券,节假日等等进行计算。这时,优惠计算的条件就会从不同的优惠对象中获取。定义优惠对象接口PriceDiscountParams,接口定义如下:
/**
* 价格打折条件
*/
interface PriceDiscountParams {
/**
* 获取抵扣的金额
*/
BigDecimal getDeductionAmount();
/**
* 获取满足抵扣条件的金额
*/
BigDecimal getMatchedDeductionAmount();
/**
* 获取打折的值(例如 七折就是0.7)
*/
BigDecimal getDiscountValue();
/**
* 获取最高抵扣金额
*/
BigDecimal getMaxDiscountAmount();
/**
* 获取使用打折的对象
*/
Map<String, Object> getUsedDiscount();
}
这时,使用工厂类模式根据不同的优惠对象生成优惠参数,定义优惠条件工厂类接口PriceDiscountParamsFactory
,代码如下:
public interface PriceDiscountParamsFactory {
/**
* @return 生成打折参数
*/
DiscountStrategy.PriceDiscountParams create();
}
其子类工厂实现类如下:
- CouponPriceDiscountParamsFactory,根据优惠券生成优惠条件
- UserPriceDiscountParamsFactory,根据用户生成游湖条件
public class CouponPriceDiscountParamsFactory implements PriceDiscountParamsFactory {
/**
* 打折使用的优惠券
*/
private final CouponListVo usedCoupon;
public CouponPriceDiscountParamsFactory(CouponListVo usedCoupon) {
this.usedCoupon = usedCoupon;
}
@Override
public DiscountStrategy.PriceDiscountParams create() {
// 根据优惠券创建参数
return new DiscountStrategy.PriceDiscountParams() ;
}
}
public class UserPriceDiscountParamsFactoryimplements PriceDiscountParamsFactory {
private final UserInfo user;
public UserPriceDiscountParamsFactory(UserInfo user) {
this.user= user;
}
@Override
public DiscountStrategy.PriceDiscountParams create() {
// 根据vip用户创建参数
return new DiscountStrategy.PriceDiscountParams() {
@Override
public BigDecimal getDeductionAmount();
}
}
- 优惠条件的匹配规则
在优惠券使用规则的筛选认证流程中,使用责任链模式,使以后可以动态扩展各类筛选条件。定义优惠券过滤接口CouponFilter,过滤器责任链接口CouponFilterChain,和组合链式接口CouponFilterPipeline。设计的过滤条件如下:
- 城市规则过滤-CityCouponFilter
- 车辆类型过滤-VehicleTypeCouponFilter
- 默认的责任链实现-DefaultCouponFilterPipeline,其代码如下:
public class DefaultCouponFilterPipeline implements CouponFilterPipeline {
private final List<CouponFilter> couponFilterList;
public DefaultCouponFilterPipeline() {
this.couponFilterList = Lists.newArrayList(new CityCouponFilter(), new VehicleTypeCouponFilter());
}
@Override
public boolean doFilter(CouponListVo couponBo, OrderPriceDto orderPriceDto) {
return new DefaultCouponFilterChain(couponFilterList).doFilter(couponBo, orderPriceDto);
}
private static class DefaultCouponFilterChain implements CouponFilterChain {
private final List<CouponFilter> couponFilterList;
private int currentIndex;
private DefaultCouponFilterChain(List<CouponFilter> couponFilterList) {
this.couponFilterList = couponFilterList;
this.currentIndex = 0;
}
@Override
public boolean doFilter(CouponListVo couponBo, OrderPriceDto orderPriceDto) {
CouponFilter couponFilter;
if (CollectionUtils.isEmpty(couponFilterList)
|| currentIndex >= couponFilterList.size()
|| Objects.isNull(couponFilter = couponFilterList.get(currentIndex++))) {
return true;
}
return couponFilter.doFilter(couponBo, orderPriceDto, this);
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13626.html