作者:废物大师兄\
起源: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%时复原原价。
限购流程
这里有两点须要阐明:
- 限购的话须要查订单零碎,然而方才说了购物车中的任意增删查改都要从新计算促销,所以如果这里间接调订单的话可能订单的顶不住(技术实力还比拟单薄,无奈!!!),思考到这里咱们冗余了订单数据,每次从本地数据库去查。当然,这样必定不准,然而咱们只保障90%的状况就能够了,所以这里咱们采纳这种形式。
- 拆商品行。还是用下面的例子,商品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开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞+转发哦!