电商促销后台设计,写得太好了!

作者:废物大师兄
来源: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/741573.html
http://www.woshipm.com/pd/594963.html
http://www.woshipm.com/pd/716781.html

(0)

相关推荐

  • 海南之十一

             去药店买了一瓶驱蚊花露水,没认真看说明书,回来才发现是婴儿专用型的,近视确实误事.使用了几次,根本不好使,它们一哄而上,扑了上来,来这里十一天了,我已千疮百孔,体无完肤.       ...

  • 广州2021年购房政策,都有那些区是重点限购的

    广州房产市场在2020年非常的火爆,最火的是黄埔和南沙这两个区.这归功于2019年的松绑限购政策,让有意向购房的外地人更加容易购房广州的房子. 目前广州有十一个区,不限购区分别是增城和从化区.轻限购区 ...

  • 一元包邮是怎么赚钱的?

    [黑话连篇] 该栏目更多的是揭露事件或对事件的看法,以达到让人精神升华的目的. 今年双十一,不出意料的我又没上车.这一次终于不是因为搞不懂规则,而是我实在顶不住大半夜的看直播抢货. 直播一搞搞几个小时 ...

  • ECSHOP团购活动,ECSHOP团购列表

    各位ECSHOP网店系统用户大家好,欢迎来到ECSHOP教程网图文教程,今天为大家详细解说一下ECSHOP团购活动,ECSHOP团购列表设置. ECSHOP教程网ECSHOP视频教程也再不断的完善与跟 ...

  • 说好的“线上线下同价”,却隐藏着这些电商促销套路

    线上的主导地位越来越明显. 过去,日化品牌会率先发力CS.KA渠道:如今,线上成就了诸多新锐品牌,它们通过布局电商渠道和刷屏式的内容营销,迅速崛起. 崛起于线上,与消费者之间的路径更短,同时" ...

  • 美工:电商详情页设计实用技巧讲解! : PS笔刷吧

    在电商设计工作中,详情页的设计一直都是衡量电商设计师能力的重要指标,正是因为它的重要程度,所以也就成了困扰很多设计师的难题.目前详情页基本上都采用了分屏式的设计,目的为了提升视觉流畅度以及减少阅读障碍 ...

  • 电商促销新方式 税务处理要合规

    电商促销新方式 税务处理要合规 作者:吴天如 "双11"购物节结束不久,又迎来"双12"购物节,随着电商行业的蓬勃发展,促销模式不断推陈出新.如何把握电商促销方 ...

  • 学霸在电商平台做“代写”生意:月收入竟然可以过万?

    这位13岁的重庆女孩儿,通过二手电商平台发布代写寒假作业的信息,一个月下来收入近万元.最终,因为书写太工整被"买家"的学校老师识破,同时账号也遭到了举报. 互联网时代,究竟是学生们 ...

  • 王坚和云计算:一场电商促销,是如何催生世界领先的云技术的?

    王坚和云计算:一场电商促销,是如何催生世界领先的云技术的?

  • 电商产品描述如何写?

    商品描述引导逻辑:信息 → 时间 → 信任 → 成交. 总结下来就是:给顾客所有想要的信息,换取顾客深入的浏览时间,使其对我们的产品产生信任,成交就水到渠成,从而提高转化率. 第一部分制作产品描述的工 ...

  • PS超强合成电商促销海报经验分享

    作者:尒白白 下面为大家分心PS超强合成电商促销海报经验,作者是尒白白,这篇教程很实用,作者把自己的经验都分享过来,对于没有接触过海报或是刚刚接触过海报的新人很值得学习,好了,下面我们一起来学习吧! ...

  • 【科技早报7点整】腾讯系电商围攻天猫 儿童看屏幕太久或永久改变大脑构造……

    早上好,科技圈 [一度蜜科技早报]第634期 2.腾讯系电商围攻天猫:京东起诉后,拼多多唯品会加入诉讼 拼多多.唯品会两大电商向北京高院提出申请,请求以第三人身份加入诉讼.此前,京东起诉天猫滥用市场支 ...

  • 电商促销、企业加购,蛋价迎来持续涨价?1个坏消息,利润飞了

    今年的鸡蛋行情可谓是一直低迷,有调查显示,80%左右的蛋农都迎来了亏损,且损失比较大,不少人今年已亏了几十万元.不过即便是这样,大家还在挺着,不愿意将蛋鸡淘汰出去,以至于蛋价持续低迷,上半年价格一直在 ...