优惠叠加计算

一、概览

        目前活动有多种优惠方式,并且复杂的优惠规则很难让用户自行计算优惠叠加的顺序。需要系统推荐出一条最优惠的方式。

1、优惠形式

        目前的优惠形式有订单满减、组合满减、赠品、换购、单品、运费、打包费等活动。

2、优惠计算顺序     

         优惠的计算顺序可以分为平行式和渐进式。平行式优惠之间没有依赖关系。渐进式优惠存在依赖关系,下一步优惠取决于上一步优惠结果。如下图所示,不同的优惠计算顺序会影响最终的优惠金额。

        

优惠叠加计算_第1张图片

二、技术方案设计

1.优惠共享和互斥

       策略需要配置互斥组,最终需要根据配置挑选可用优惠,默认不在互斥组中的优惠共享。

    举个例子:

  DiscountconfluctGro

2.计价引擎

CalculateEngine -> 计价引擎,用于在购物车场景,试算提单组合优惠。

public class CalculateEngine{
    
    /**
    *  算价引擎算价
    */
    public DiscountResponse calculate(CalculateReq req, Promotion[] promotionArr){
        dfs(req, CartLocker.of(), promotionArr, new int[promotionArr.length]);
        return getCalculateResp(clList);
    }


    private List clList = new ArrayList<>();

    /**
    * 回溯算法计算优惠全排列组合
    */
    public void dfs(CalculateReq req, CartLocker cartLocker, Promotion[] promotionArr, int[] visited){
        
        boolean end = true;
        for (int i = 0; i < promotionArr.length; i++){
            if (visited[i] == 1){
                continue;
            }
            visited[i] = 1;
            end = false;
            CartLocker cpCL= cartLocker.copy();
            // 互斥组判断通过
            if (StrategyGroup.isNotConflict(cpCl.getUsedStrategySet(), 
                promotion[i].getStrategy().getType()
            )){
                // 策略计算,结果存在cartLocker中    
                promotion[i].getStrategy().apply(req, cpCl);
            }
            // 全排列
            dfs(req, cpCL, promotionArr, visited);
            visited[i] = 0;
        }
        if (end){
            clList.add(cartLocker);
        }
    }
    
    /**
    * 获取最佳的一个优惠组合
    */
    public DiscountRes getCalculateResp(List cartLockerList){
        DiscountRes res = null;
        if (CollectionUtil.isEmpty(cartLockerList)){
            return res;
        }
        CartLocker maxCartLocker = cartLockerList.get(0);
        for (int i = 1; i < cartLockerList.size(); i++){
            if (cartLockerList.get(i).getPromotionAmount() > maxCartLocker.getpromotionAmount()){
                maxCartLocker = cartLockerList.get(i);
            }
        }
        DiscountRes res = new DiscountRes();
        res.setPromotionAmount(maxCartLocker.getPromotionAmount());
        res.setCartLocker(maxCartLocker);
        res.setDiscountInfo(maxCartLocker.getDiscountInfo());
        return res;
    }
    
}

CartLocker -> 记录购物车所享有的优惠信息,包括每件商品对应优惠等信息。

class CartLocker{
    // 活动id列表
    private List promotionId = new ArrayList<>();
 
    // 策略列表
    private Set strategySet = new HashSet<>();
    
    // >
    private Map> productLockersMap;
    
    // >
    private Map> promotionLockersMap;

    @Data
    public static class Locker{
    

        private String promotionId;

        private String skuId;
        
        /**
        *   锁定数量
        */
        private Integer quantity;
        
        /**
        * 商品分摊金额
        */
        private Integer promotionAmount;
    }
}

3.算价策略

SkuDeduct -> 单品立减策略

class SkuDeduct{

    private String skuId;
    
    private Integer deductAmount;    

    public ApplyResult apply(CalculateRequest req, CartLocker cartLocker){
        List tmpLockQueue = new ArrayList<>();
        RowLockerManager rowLockerManager = new RowLockerManager(cartLocker);
        int promotionAmount = 0;
        for(Proudct p : req.getProducts()){
            if (Objects.equals(skuId, p.getSkuId())){
                for (int i = 0; i < p.getQuantity(); i++){
                    if (rowLockerManager.tryLock(p)){
                        tmpLockQueue.add(p);
                        promotionAmount += Math.min(deductAmount, p.getUnitPrice());
                    }
        
                }
                break;
            }
        }
        if (tmpLockqueue.size() == 0){
            return null;
        }

        // 商品锁定
        rowLockerManager.lockProducts(tmpLockQueue);
        // 优惠分摊
        rowLockerManager.getLocker(skuId).setPromotionAmount(promotionAmount);

        // 将锁定行更新到cartLocker中
        cartLocker.addLockers(rowLockerManager.getLockers());
        // 返回结果
        return ApplyResult.builder().lockers(rowlockerManager.getLockers()).promotionAmount(promotionAmount).build();
    }
}

RowLockManager

RowLockManager主要对商品件进行控制,防止使用多种优惠

class RowLockerManager{

    private List lockers;

    //  
    private Map lockerMap;
    
    // 
    private Map tryLockerMap;

    private CartLocker cartLocker;

    public boolean tryLock(Product product){
        var hasUsedCnt = cartLocker.getUsedCnt(product.getSkuId());
        
        var tmpUsedCnt = lockerMap.get(product.getSkuId()).getQuantity() + 
                        tryLockerMap.get(product.getSkuId()).getQuantity();
        // 没有商品可以使用
        if (hasUsedCnt + tmpUsedCnt == product.getQuantity()){
            return false;
        }
    
        // 更新tryLockerMap, 这里省略了些判断代码
        tryLockerMap.get(product.getSkuId()).setQuantity($ + 1);
        return true;
    }

    public void lock(Product product){
        // 只是简单流程代码
        tryLockerMap.get(product.getSkuId()).setQuantity($ - 1);
        lockerMap.get(product.getSkuId()).setQuantity($ + 1);
    }

    public void lockProducts(List products){
        for(Product product:products){
            lockProduct(product);
        }
    }
}

你可能感兴趣的:(营销活动,java)