電商所謂營銷,歸根結底都是訂單金額的變化;如果我們清楚的知道訂單金額的計算流程是怎樣的,那麼我們只需要順著系統的計算流程做促銷,就不用擔心各種促銷類型之間產生重疊或者衝
2021-07-24 03:08:38
電商所謂營銷,歸根結底都是訂單金額的變化;如果我們清楚的知道訂單金額的計算流程是怎樣的,那麼我們只需要順著系統的計算流程做促銷,就不用擔心各種促銷類型之間產生重疊或者衝突的情況了。當我們知道這個關係後,就可以將營銷活動區分為三種類型:改商品價格、改商品小計價格、改訂單價格,因為無論什麼營銷歸根結底都是可以描述成改價格。
購物車中任何增刪查改都要重新計算促銷,所以促銷的計算變得尤為重要,感覺京東已經把促銷做到了極致。
從模式上來講,我們公司的促銷就相當於京東自營,所以很多也都是參考京東自營的,但我們還沒法做到像京東促銷那樣強大。
這裡,將我們做的促銷跟大家分享一下,只涉及後臺介面邏輯部分。
介面的功能就是輸入商品列表,返回加了促銷分組後的商品列表。
首先要聲明兩點:
一、不是通用的促銷設計,只是我們公司目前支援的促銷設計及邏輯;
二、作者水平有限,不會畫圖,所以圖畫得比較醜,也很粗,希望大家不要介意;
三、不談效能
廢話就不多說了,下面正式開始。。。
前面說了,促銷歸根結底是改價格。在我們這裡其它單品促銷就是改商品價格;而條件促銷就相當於改小計的價格;至於贈品促銷不設計改價格,可以認為是單品促銷的一種類型。
「同類型通過實體進行互斥、不同類型可以相互疊加。」這是別人總結的設計電商促銷系統的基本原則,我也比較認同。
上面介面主流程就是先應用單品促銷,再應用條件促銷。稍微再細化一點兒就是這樣的:
先處理贈品促銷,將贈品掛載到主商品(原先使用者新增的購物車中的商品我稱之為主商品)上,再應用單品促銷。
在進行單品促銷的時候,很有可能同一個商品命中多個單品促銷。這個時候只能取一個促銷,此處的計算邏輯是這樣的:
例如:
商品A命中四條促銷,分別是:【促銷1】直降2元,【促銷2】折扣8折,【促銷3】直降1元。假設A的原價時10元,那麼經過計算【促銷1】8元,【促銷2】8元,【促銷3】9元。這個時候,【促銷3】應該被剔除,假設【促銷2】的創建時間比【促銷1】要晚,那麼應該取【促銷2】。即商品A最終命中【促銷2】。原價10元,促銷價8元。
稍微解釋一下:
這裡有兩點需要說明:
同一個商品可能會命中多個條件促銷,而最終每個商品只能應用一個條件促銷(即每個商品最終只能屬於一個組)
我們說,同種類型的促銷不能疊加,不同類型的促銷可以疊加。在我們這裡,單品促銷和單品促銷不能疊加,條件促銷與條件促銷不能疊加,單品與條件可以疊加。
程式走到這裡,我們已經完成了單品促銷的處理,接下來處理條件促銷。在決定商品應該最終應用哪個條件促銷時,我們的原則是這樣的:
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了當然不滿足最低一個的階梯
在程式碼實現上,這裡是兩層迴圈:
程式碼可能是這樣的,下面貼出條件促銷部分的程式碼片段
1 // 處理條件促銷 2 // 算小計 3 for (PromotionProductDTO promotionProductDTO : promotionProductDTOList) { 4 promotionProductDTO.setSubtotal(promotionProductDTO.getPromotionPrice().multiply(new BigDecimal(promotionProductDTO.getQuantity()))); 5 } 6 List<PromotionInfoDTO> conditionPromotionInfoDTOList = promotionInfoMap.get(PromotionTypeEnum.TIAOJIAN.getType()); 7 // 限購 8 List<PromotionInfoDTO> validConditionPromotionInfoDTOList = new ArrayList<>(); 9 for (PromotionInfoDTO promotionInfoDTO : conditionPromotionInfoDTOList) { 10 if (isMaxConditionPromotionLimit(promotionInfoDTO, userId)) { 11 continue; 12 } 13 validConditionPromotionInfoDTOList.add(promotionInfoDTO); 14 } 15 conditionPromotionInfoDTOList = validConditionPromotionInfoDTOList; 16 17 // 按範圍初步將商品歸到各個條件促銷下(撒網) 18 for (PromotionInfoDTO promotionInfoDTO : conditionPromotionInfoDTOList) { 19 List<PromotionProductDTO> matchedPromotionProductDTOList = new ArrayList<>(); 20 21 List<PromotionProductEntity> promotionProductEntityList = promotionInfoDTO.getDefinitiveProductEntityList(); 22 for (PromotionProductDTO promotionProductDTO : promotionProductDTOList) { 23 // 商品匹配到的促銷 24 if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.ALL.getValue()) { 25 matchedPromotionProductDTOList.add(promotionProductDTO); 26 }else if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.CATEGORY.getValue()) { 27 Set<String> secondCategorySet = promotionProductEntityList.stream().map(PromotionProductEntity::getProCategorySecond).collect(Collectors.toSet()); 28 if (secondCategorySet.contains(promotionProductDTO.getCategoryCode())) { 29 matchedPromotionProductDTOList.add(promotionProductDTO); 30 } 31 }else if (promotionInfoDTO.getProductRange() == PromotionPruductRangeEnum.SPECIFIED.getValue()) { 32 Set<Long> specialProductIdSet = promotionProductEntityList.stream().map(PromotionProductEntity::getProductId).collect(Collectors.toSet()); 33 if (specialProductIdSet.contains(promotionProductDTO.getId())) { 34 matchedPromotionProductDTOList.add(promotionProductDTO); 35 } 36 } 37 } 38 39 // 促銷匹配到的商品 40 promotionInfoDTO.setMatchedProductDTOList(matchedPromotionProductDTOList); 41 42 // 判斷促銷匹配的這些商品是否滿足條件 43 BigDecimal totalAmount = BigDecimal.ZERO; 44 for (PromotionProductDTO promotionProductDTO : matchedPromotionProductDTOList) { 45 totalAmount = totalAmount.add(promotionProductDTO.getSubtotal()); 46 } 47 PromotionStairEntity promotionStairEntity = matchStair(promotionInfoDTO.getDefinitiveStairEntityList(), totalAmount); 48 if (null != promotionStairEntity) { 49 promotionInfoDTO.setPromotionStairEntity(promotionStairEntity); 50 } 51 } 52 53 // 按滿足條件與否以及促銷創建的先後順序進一步歸檔商品(即分組) 54 // 挑選出滿足條件的促銷,並按照創建時間降序排序 55 List<PromotionInfoDTO> matchedConditionPromotionInfoDTOList = conditionPromotionInfoDTOList.stream() 56 .filter(x->null != x.getPromotionStairEntity()) 57 .sorted(Comparator.comparing(PromotionInfoDTO::getCreateTime).reversed()) 58 .collect(Collectors.toList()); 59 60 // 去重,以保證每個組中的商品之間無交集 61 int len = matchedConditionPromotionInfoDTOList.size(); 62 for (int i = 0; i < len - 1; i++) { 63 PromotionInfoDTO majorPromotionInfoDTO = matchedConditionPromotionInfoDTOList.get(i); 64 for (int j = i + 1; j < len; j++) { 65 PromotionInfoDTO minorPromotionInfoDTO = matchedConditionPromotionInfoDTOList.get(j); 66 for (PromotionProductDTO majorMatchedPromotionProductDTO : majorPromotionInfoDTO.getMatchedProductDTOList()) { 67 minorPromotionInfoDTO.setMatchedProductDTOList(minorPromotionInfoDTO.getMatchedProductDTOList() 68 .stream() 69 .filter(x -> !x.getId().equals(majorMatchedPromotionProductDTO.getId())) 70 .collect(Collectors.toList())); 71 } 72 } 73 } 74 75 // 最終命中的促銷 76 List<PromotionInfoDTO> ultimatePromotionInfoDTOList = new ArrayList<>(); 77 // 重新計算各組匹配的階梯規則 78 for (PromotionInfoDTO promotionInfoDTO : matchedConditionPromotionInfoDTOList) { 79 List<PromotionProductDTO> promotionProductDTOS = promotionInfoDTO.getMatchedProductDTOList(); 80 // 過濾掉空的促銷 81 if (null == promotionProductDTOS || promotionProductDTOS.size() < 1) { 82 continue; 83 } 84 ultimatePromotionInfoDTOList.add(promotionInfoDTO); 85 BigDecimal totalAmount = BigDecimal.ZERO; 86 for (PromotionProductDTO promotionProductDTO : promotionProductDTOS) { 87 totalAmount = totalAmount.add(promotionProductDTO.getSubtotal()); 88 } 89 90 // 查詢該組商品滿足的最高階梯 91 PromotionStairEntity promotionStairEntity = matchStair(promotionInfoDTO.getDefinitiveStairEntityList(), totalAmount); 92 if (null != promotionStairEntity) { 93 // 設定這組商品命中的促銷的哪一個階梯 94 promotionInfoDTO.setPromotionStairEntity(promotionStairEntity); 95 // 設定每個商品最終命中的唯一的條件促銷 96 for (PromotionProductDTO promotionProductDTO : promotionProductDTOS) { 97 promotionProductDTO.setConditionpromotionInfoDTO(promotionInfoDTO); 98 } 99 }else {100 // 計算還差多少錢滿足最低階梯101 List<PromotionStairEntity> promotionStairList = promotionInfoDTO.getDefinitiveStairEntityList().stream().sorted(Comparator.comparing(PromotionStairEntity::getMinimumCharge)).collect(Collectors.toList());102 PromotionStairEntity promotionStairEntity2 = promotionStairList.get(0);103 BigDecimal minimumCharge = promotionStairEntity2.getMinimumCharge();104 BigDecimal balance = minimumCharge.subtract(totalAmount);105 promotionInfoDTO.setBalance(balance);106 }107 }
最終返回的應該是一個列表,列表中的每一個元素代表一個條件促銷(即分組)
介面看起來可能是這樣的:
作者:廢物大師兄
連結:https://www.cnblogs.com/cjsblog/p/9306637.html
相關文章
電商所謂營銷,歸根結底都是訂單金額的變化;如果我們清楚的知道訂單金額的計算流程是怎樣的,那麼我們只需要順著系統的計算流程做促銷,就不用擔心各種促銷類型之間產生重疊或者衝
2021-07-24 03:08:38
近日,知名資料機構每日互動公佈了2021年第二季度5G手機銷量資料,從榜單資料來看,在5G手機銷量前十名中,小米手機竟然全軍覆沒。要知道,在今年上半年,小米釋出了多款高性價比的5G手
2021-07-24 03:08:24
眾所周知,目前的主流晶片都是矽基晶片,佔所有晶片的90%以上。不過科學家們也在研究採用其它非矽基材料的晶片,比如碳基晶片等。還有第三代半導體材料,也都不是矽基晶片,比如GaN、
2021-07-24 03:08:18
7月23日晚七點(北京時間),2020年東京奧運會開幕式即將舉行,這場全球矚目的國際盛事牽動著不少國人的心。作為官方媒體,新華網將對東京奧運會進行持續報道。而真我GT大師探索版也
2021-07-24 03:08:11
「山西能有什麼便利店?」這可能是很多人在聽說「山西便利店走紅」之後的第一反應,畢竟便利店這個關鍵詞,似乎總和一線城市、經濟發達掛鉤。但根據中國連鎖經營協會發布的「2020
2021-07-24 03:08:06
大資料文摘出品今年的東京奧運會是一場幾乎沒有現場觀眾的奧運。由於疫情的影響,東京自12日起進入緊急狀態,在嚴格的防疫控制下,東京地區、北海道和福島縣的所有比賽將空場舉行
2021-07-24 03:07:59