作者:廢物大師兄
來源: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/741573.html
http://www.woshipm.com/pd/594963.html
http://www.woshipm.com/pd/716781.html
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258316.html
標籤:其他
上一篇:二進制相加
