关于java:电商促销后台设计写得太好了

52次阅读

共计 7277 个字符,预计需要花费 19 分钟才能阅读完成。

作者:废物大师兄 \
起源:www.cnblogs.com/cjsblog/p/9306637.html

电商所谓营销,归根结底都是订单金额的变动;如果咱们分明的晓得订单金额的计算流程是怎么的,那么咱们只须要顺着零碎的计算流程做促销,就不必放心各种促销类型之间产生重叠或者抵触的状况了。

当咱们晓得这个关系后,就能够将营销活动区分为三种类型:改商品价格、改商品小计价格、改订单价格,因为无论什么营销归根结底都是能够形容成改价格。

购物车中任何增删查改都要从新计算促销,所以促销的计算变得尤为重要,感觉京东曾经把促销做到了极致。

从模式上来讲,咱们公司的促销就相当于京东自营,所以很多也都是参考京东自营的,但咱们还没法做到像京东促销那样弱小。

这里,将咱们做的促销跟大家分享一下,只波及后盾接口逻辑局部。

接口的性能就是输出商品列表,返回加了促销分组后的商品列表。

首先要申明几点:

一、不是通用的促销设计,只是咱们公司目前反对的促销设计及逻辑;

二、作者程度无限,不会画图,所以图画得比拟丑,也很粗,心愿大家不要介意;

三、不谈性能

废话就不多说了,上面正式开始。。。

促销类型

后面说了,促销归根结底是改价格。在咱们这里其它单品促销就是改商品价格;而条件促销就相当于改小计的价格;至于赠品促销不设计改价格,能够认为是单品促销的一种类型。

主流程

同类型通过实体进行互斥、不同类型能够互相叠加。”这是他人总结的设计电商促销零碎的根本准则,我也比拟认同。

下面接口主流程就是先利用单品促销,再利用条件促销。略微再细化一点儿就是这样的:

先解决赠品促销,将赠品挂载到主商品(原先用户增加的购物车中的商品我称之为主商品)上,再利用单品促销。

在进行单品促销的时候,很有可能同一个商品命中多个单品促销。这个时候只能取一个促销,此处的计算逻辑是这样的:

  • 优惠力度最大的优先
  • 优惠力度雷同时,取最新创立的那个(创立工夫最新)

例如:

商品 A 命中四条促销,别离是:【促销 1】直降 2 元,【促销 2】折扣 8 折,【促销 3】直降 1 元。假如 A 的原价时 10 元,那么通过计算【促销 1】8 元,【促销 2】8 元,【促销 3】9 元。这个时候,【促销 3】应该被剔除,假如【促销 2】的创立工夫比【促销 1】要晚,那么应该取【促销 2】。即商品 A 最终命中【促销 2】。原价 10 元,促销价 8 元。

计算商品价格流程

略微解释一下:

  • 特价:商品 A 原价 12 元,今日特价 9.9 元。
  • 折扣:商品打几折。
  • 直降:商品 A 原价 12 元,今日直降 3 元,所以最终 9 元。且当促销价低于原价的 70% 时复原原价。

限购流程

这里有两点须要阐明:

  1. 限购的话须要查订单零碎,然而方才说了购物车中的任意增删查改都要从新计算促销,所以如果这里间接调订单的话可能订单的顶不住(技术实力还比拟单薄,无奈!!!),思考到这里咱们冗余了订单数据,每次从本地数据库去查。当然,这样必定不准,然而咱们只保障 90% 的状况就能够了,所以这里咱们采纳这种形式。
  2. 拆商品行。还是用下面的例子,商品 A 命中了【促销 2】,假如【促销 2】限购每人每单 1 件,而当初 A 的数量时 3,那么咱们会拆成 2 行,第一行商品 A 售价 8 元数量 1 件,第二行商品 A 售价 10 元数量 2 件。

条件促销分组

同一个商品可能会命中多个条件促销,而最终每个商品只能利用一个条件促销(即每个商品最终只能属于一个组)

咱们说,同种类型的促销不能叠加,不同类型的促销能够叠加。在咱们这里,单品促销和单品促销不能叠加,条件促销与条件促销不能叠加,单品与条件能够叠加。

程序走到这里,咱们曾经实现了单品促销的解决,接下来解决条件促销。在决定商品应该最终利用哪个条件促销时,咱们的准则是这样的:

1、优先思考满足条件的促销

这句话的意思是,假如商品 A,商品 B 满足【促销 1】满 100 减 20 这个阶梯,同时 A 和 B 又都命中了【促销 2】然而不满足【促销 2】的条件,因为假如【促销 2】的最小阶梯是满 150 减 30。那么这个时候,尽管 A 和 B 都同时命中【促销 1】和【促销 2】,但 A 和 B 一起正好合乎【促销 1】满 100 减 20 的条件,所以这个时候促销 A 和 B 应该最终取【促销 2】

2、同时满足多个条件促销时,取后创立的那个(创立工夫最近)

还是下面的例子,假如 A 和 B 的总金额加起来是 160 元,那么它们都满足【促销 1】和【促销 2】,假如【促销 2】是后创立的,所以此时它们最终命中的条件促销应该取【促销 2】。并且,之后应该讲它们从【促销 1】的商品组中剔除(PS:因为一个商品只能属于一个组,即只能利用一个条件促销)。京东在这里对每种促销做了计算,把最终用哪个促销的决定权交给用户去选,咱们这里不搞这么简单。

说了这么多,可能有点晕,上面举个例子

假如有 A,B,C,D 四个商品,促销 1234 是四个促销

如图,【促销 1】是所有商品,所有 A,B,C,D 四个都命中【促销 1】,换句话说【促销 1】的商品组中有 A,B,C,D

【促销 2】的商品组中有 A,C

【促销 3】的商品组中有 A,B

【促销 4】的商品组中有 A,B,C

假如促销 1,2,3,4 是顺次创立的,也就是说 4 是最晚创立的,1 是最早创立的

再假如,A+B+ C 合乎【促销 4】的其中一个阶梯条件,A+ B 合乎【促销 3】中的其中一个阶梯条件,A+B+C+ D 合乎【促销 1】的其中最低一级的阶梯条件

那么,最终的促销分组应该是这样的:

【促销 4】的商品组有:A,B,C

【促销 3】的商品组为空

【促销 2】的商品组为空

【促销 1】的商品组中有:D,而且不满足最低的阶梯,因为原来 A +B+C+ D 满足最低一级的阶梯,当初只剩下 D 了当然不满足最低一个的阶梯

条件促销分组计算

在代码实现上,这里是两层循环:

  • 第一层是条件促销列表
  • 第二层是某个条件促销中的商品组

局部代码实现

代码可能是这样的,上面贴出条件促销局部的代码片段:

//  解决条件促销
//  算小计
for (PromotionProductDTO promotionProductDTO : promotionProductDTOList) {promotionProductDTO.setSubtotal(promotionProductDTO.getPromotionPrice().multiply(new BigDecimal(promotionProductDTO.getQuantity())));
}
List<PromotionInfoDTO> conditionPromotionInfoDTOList = promotionInfoMap.get(PromotionTypeEnum.TIAOJIAN.getType());
//  限购
List<PromotionInfoDTO> validConditionPromotionInfoDTOList = new ArrayList<>();
for (PromotionInfoDTO promotionInfoDTO : conditionPromotionInfoDTOList) {if (isMaxConditionPromotionLimit(promotionInfoDTO, userId)) {continue;}
    validConditionPromotionInfoDTOList.add(promotionInfoDTO);
}
conditionPromotionInfoDTOList = validConditionPromotionInfoDTOList;

//  按范畴初步将商品归到各个条件促销下(撒网)for (PromotionInfoDTO promotionInfoDTO : conditionPromotionInfoDTOList) {List<PromotionProductDTO> matchedPromotionProductDTOList = new ArrayList<>();

    List<PromotionProductEntity> promotionProductEntityList = promotionInfoDTO.getDefinitiveProductEntityList();
    for (PromotionProductDTO promotionProductDTO : promotionProductDTOList) {
        //  商品匹配到的促销
        if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.ALL.getValue()) {matchedPromotionProductDTOList.add(promotionProductDTO);
        }else if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.CATEGORY.getValue()) {Set<String> secondCategorySet = promotionProductEntityList.stream().map(PromotionProductEntity::getProCategorySecond).collect(Collectors.toSet());
            if (secondCategorySet.contains(promotionProductDTO.getCategoryCode())) {matchedPromotionProductDTOList.add(promotionProductDTO);
            }
        }else if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.SPECIFIED.getValue()) {Set<Long> specialProductIdSet = promotionProductEntityList.stream().map(PromotionProductEntity::getProductId).collect(Collectors.toSet());
            if (specialProductIdSet.contains(promotionProductDTO.getId())) {matchedPromotionProductDTOList.add(promotionProductDTO);
            }
        }
    }

    //  促销匹配到的商品
    promotionInfoDTO.setMatchedProductDTOList(matchedPromotionProductDTOList);

    //  判断促销匹配的这些商品是否满足条件
    BigDecimal totalAmount = BigDecimal.ZERO;
    for (PromotionProductDTO promotionProductDTO : matchedPromotionProductDTOList) {totalAmount = totalAmount.add(promotionProductDTO.getSubtotal());
    }
    PromotionStairEntity promotionStairEntity = matchStair(promotionInfoDTO.getDefinitiveStairEntityList(), totalAmount);
    if (null != promotionStairEntity) {promotionInfoDTO.setPromotionStairEntity(promotionStairEntity);
    }
}

//  按满足条件与否以及促销创立的先后顺序进一步归档商品(即分组)//  挑选出满足条件的促销,并依照创立工夫降序排序
List<PromotionInfoDTO> matchedConditionPromotionInfoDTOList = conditionPromotionInfoDTOList.stream()
        .filter(x->null != x.getPromotionStairEntity())
        .sorted(Comparator.comparing(PromotionInfoDTO::getCreateTime).reversed())
        .collect(Collectors.toList());

//  去重,以保障每个组中的商品之间无交加
int len = matchedConditionPromotionInfoDTOList.size();
for (int i = 0; i < len - 1; i++) {PromotionInfoDTO majorPromotionInfoDTO = matchedConditionPromotionInfoDTOList.get(i);
    for (int j = i + 1; j < len; j++) {PromotionInfoDTO minorPromotionInfoDTO = matchedConditionPromotionInfoDTOList.get(j);
        for (PromotionProductDTO majorMatchedPromotionProductDTO : majorPromotionInfoDTO.getMatchedProductDTOList()) {minorPromotionInfoDTO.setMatchedProductDTOList(minorPromotionInfoDTO.getMatchedProductDTOList()
                    .stream()
                    .filter(x -> !x.getId().equals(majorMatchedPromotionProductDTO.getId()))
                    .collect(Collectors.toList()));
        }
    }
}

//  最终命中的促销
List<PromotionInfoDTO> ultimatePromotionInfoDTOList = new ArrayList<>();
//  从新计算各组匹配的阶梯规定
for (PromotionInfoDTO promotionInfoDTO : matchedConditionPromotionInfoDTOList) {List<PromotionProductDTO> promotionProductDTOS = promotionInfoDTO.getMatchedProductDTOList();
    //  过滤掉空的促销
    if (null == promotionProductDTOS || promotionProductDTOS.size() < 1) {continue;}
    ultimatePromotionInfoDTOList.add(promotionInfoDTO);
    BigDecimal totalAmount = BigDecimal.ZERO;
    for (PromotionProductDTO promotionProductDTO : promotionProductDTOS) {totalAmount = totalAmount.add(promotionProductDTO.getSubtotal());
    }

    //  查问该组商品满足的最高阶梯
    PromotionStairEntity promotionStairEntity = matchStair(promotionInfoDTO.getDefinitiveStairEntityList(), totalAmount);
    if (null != promotionStairEntity) {
        //  设置这组商品命中的促销的哪一个阶梯
        promotionInfoDTO.setPromotionStairEntity(promotionStairEntity);
        //  设置每个商品最终命中的惟一的条件促销
        for (PromotionProductDTO promotionProductDTO : promotionProductDTOS) {promotionProductDTO.setConditionpromotionInfoDTO(promotionInfoDTO);
        }
    }else {
        //  计算还差多少钱满足最低阶梯
        List<PromotionStairEntity> promotionStairList = promotionInfoDTO.getDefinitiveStairEntityList().stream().sorted(Comparator.comparing(PromotionStairEntity::getMinimumCharge)).collect(Collectors.toList());
        PromotionStairEntity promotionStairEntity2 = promotionStairList.get(0);
        BigDecimal minimumCharge = promotionStairEntity2.getMinimumCharge();
        BigDecimal balance = minimumCharge.subtract(totalAmount);
        promotionInfoDTO.setBalance(balance);
    }
} 

返回的数据接口

最终返回的应该是一个列表,列表中的每一个元素代表一个条件促销(即分组)

接口看起来可能是这样的:

参考:

http://www.woshipm.com/pd/741…\
http://www.woshipm.com/pd/594…\
http://www.woshipm.com/pd/716…

近期热文举荐:

1.Java 15 正式公布,14 个新个性,刷新你的认知!!

2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3. 我用 Java 8 写了一段逻辑,共事直呼看不懂,你试试看。。

4. 吊打 Tomcat,Undertow 性能很炸!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0