需求和定義
??設(shè)計(jì)一個(gè)靈活的促銷架構(gòu),可以容納當(dāng)前可預(yù)見到的所有促銷活動類型。活動的配置方式要足夠自由,允許運(yùn)營管理員自由配置活動的參與人群、活動包含的商品范圍、特別是不同活動的疊加關(guān)系。
??配置時(shí),一個(gè)商品可以同時(shí)在同一時(shí)間參加多個(gè)活動。而在結(jié)算時(shí)互斥的多個(gè)活動中只能選擇其中一個(gè)參加,這個(gè)選擇可以是系統(tǒng)自動的,也可以是用戶自己選擇的。不互斥的活動可以則同時(shí)參加。
??這里的促銷活動是指用戶購買商品時(shí)會改變訂單的實(shí)付價(jià)格或者訂單商品數(shù)量的促銷類型,例如限時(shí)降價(jià)、滿100減10、買1贈1、會員打折等。不在下單過程中起作用的活動不包括在這個(gè)范圍里,例如新用戶注冊送券、下單后贈券、下單后返現(xiàn)等。
邏輯架構(gòu)
??這個(gè)架構(gòu)設(shè)計(jì)最關(guān)鍵的問題是要解決不同促銷活動疊加使用的問題,而且能否疊加是可以由運(yùn)營在配置的時(shí)候來指定的,而不是在代碼里固定不變。例如運(yùn)營設(shè)置了一個(gè)限時(shí)價(jià)活動為原價(jià)的2折,已經(jīng)突破了成本價(jià),這時(shí)候運(yùn)營可能不希望用戶再使用其它的促銷活動;而另一個(gè)限時(shí)價(jià)活動只是原價(jià)的9折,這時(shí)候運(yùn)營可能就允許用戶疊加使用其它活動。
??活動疊加存在先后關(guān)系,后面的促銷結(jié)果基于前面的促銷結(jié)果進(jìn)行計(jì)算。
??根據(jù)這個(gè)以上需求,設(shè)計(jì)一個(gè)分層的促銷活動架構(gòu),處在同一層的促銷表示互斥的、不能同時(shí)參加的活動類型,不同一層的活動則可以疊加使用。一個(gè)常見的分層方式是這樣的:
| 層級 | 促銷方式 | 舉例 |
|---|---|---|
| 1級 | 直接修改價(jià)格 | 直降價(jià)、秒殺、拼團(tuán)、預(yù)訂 |
| 2級 | 范圍促銷 | 指定商品買1送1;指定品牌買滿100減10;全場商品滿100減10; |
| 3級 | 加價(jià)換購 | 買滿100后加10元換購xx; |
| 4級 | 優(yōu)惠券 | 滿100減10券;滿3件8折券; |
| 5級 | 會員折上折 | 在實(shí)付金額上直接打95折; |
| 6級 | 包郵 | 單品包郵;滿99包郵;大促全場包郵; |
| 7級 | 運(yùn)費(fèi)券 | |
| 8級 | 積分抵扣 | 100積分抵1元 |
??分層的方式和層次之間的優(yōu)先級主要依據(jù)以下2點(diǎn):
??1.邏輯上是否允許一個(gè)商品能否同時(shí)參加兩個(gè)活動,例如用戶購買一個(gè)商品時(shí),顯然不能同時(shí)參加秒殺活動又同時(shí)參加拼團(tuán)活動。
??2.從運(yùn)營角度是否允許一個(gè)商品同時(shí)參加兩個(gè)活動,例如有些平臺會把范圍促銷分成類目促銷和全場促銷兩種,并且允許用戶同時(shí)參加這兩種活動,這時(shí)候就要把范圍促銷拆分成兩層。
??層次之間能否疊加由前面的活動來指定,后面的活動不能指定能否疊加前面層級的促銷,否則將需要不斷的逆向回滾。即,在配置1級活動的時(shí)候可以指定能否疊加后面的2到8級的促銷,而在配置7級活動時(shí)只能指定能否疊加8級促銷。
促銷計(jì)算過程
??計(jì)算過程參考管道設(shè)計(jì)模式(pipeline),把每一個(gè)促銷層級定義為一個(gè)主策略。同一層內(nèi)有多種促銷活動的,把每一種活動定義為一個(gè)子策略,在主策略里主要實(shí)現(xiàn)如果選擇子策略的邏輯。
??另外,不同位置和不同渠道可以使用的促銷類型可能會有所不同,例如購物車一般不需要計(jì)算運(yùn)費(fèi)這一級以后促銷、微信渠道不能使用會員折上折等。這種情況只要把主策略組合成不同的組合,計(jì)算時(shí)按需求指定要使用的策略組合就可以了。
??也就是說,從上到下依次有三個(gè)層次的策略:策略組合、主策略、子策略。這三種策略的輸入?yún)?shù)和輸出結(jié)果完全一樣。因此定義一個(gè)促銷計(jì)算結(jié)果對象,作為所有策略的輸入?yún)?shù)和輸出結(jié)果。

?? 計(jì)算時(shí),把用戶購物車?yán)锏纳唐方M裝成促銷計(jì)算結(jié)果結(jié)構(gòu),依次通過全部策略,每個(gè)策略都會修改促銷計(jì)算結(jié)果里的價(jià)格,以及在商品信息上附加一個(gè)促銷計(jì)算結(jié)果。下面這個(gè)例子,用戶購買了goodsId=1的商品2件,商品原價(jià)是10元,這時(shí)候還沒有經(jīng)過促銷計(jì)算,實(shí)付價(jià)等于原價(jià)。注意promotion的isUserSelect字段,表示用戶指定要參加id=10的這個(gè)秒殺活動。
{
"goods":[{
"goodsId":1,
"buyCount":2,
"originalPrice":10,
"payPrice":10,
"promotions":[{
"type":"flashSale",
"id":10,
"isUserSelect":true
}]
}],
"originalPrice":20,
"payPrice":20,
"promotions":[]
}
??經(jīng)過全部促銷策略計(jì)算之后得到類似這樣的一個(gè)結(jié)構(gòu)。這個(gè)商品一共應(yīng)用了兩個(gè)促銷活動,實(shí)付價(jià)變成5元,其中參加秒殺活動減了4元,優(yōu)惠券減了1元。
?? levelStacking字段表示這個(gè)促銷可以跟哪些層級的促銷疊加下,例如新客秒殺這個(gè)活動的levelStacking=[4,5],表示這個(gè)活動可以疊加4級和5級促銷,但是不能同時(shí)參加其它層級的活動。
?? priceAdjustment字段表示這個(gè)商品在這個(gè)促銷活動上減了多少錢。
{
"goods":[{
"goodsId":1,
"goodsName":"示例商品",
"buyCount":2,
"originalPrice":10,
"payPrice":5,
"promotions":[{
"type":"flashSale",
"id":10,
"name":"新客秒殺",
"description":"僅限新客參加",
"isUserSelect":true,
"levelStacking":[4,5],
"priceAdjustment":4,
"extraInfo":[]
},{
"type":"discountCoupon",
"id":11,
"name":"新客無門檻減1券",
"description":"僅限新客參加",
"isUserSelect":false,
"levelStacking":[5],
"priceAdjustment":1,
"extraInfo":[]
}]
],
"originalPrice":20,
"payPrice":10,
"promotions":[{
"type":"flashSale",
"id":10,
"name":"新客秒殺",
"priceAdjustment":8,
"joinedGoodsId":[1],
"extraInfo":[]
},{
"type":"discountCoupon",
"id":11,
"levelStacking":[5],
"priceAdjustment":2,
"joinedGoodsId":[1],
"extraInfo":[]
}]
}
??計(jì)算結(jié)果還要獲取到商品和活動的其它詳情信息,把這個(gè)結(jié)果返回給購物車和訂單后,由調(diào)用方根據(jù)各自的需求解釋成所需的結(jié)構(gòu)。
??由于策略的輸入輸出都是一樣的,所以理論上所有策略是可以任意組合的。也就是說可以隨意往架構(gòu)里增加新的促銷方式或減少促銷方式。例如想是增加一個(gè)商品兌換券的促銷方式,只需要在3級和4級促銷之間增加一個(gè)主策略,再在這個(gè)主策略里增加兌換券的子策略就可以了。
數(shù)據(jù)庫設(shè)計(jì)
??首先把優(yōu)惠券跟其它活動分開來,因?yàn)閮?yōu)惠券是屬于用戶資產(chǎn)類型,用戶要先擁有了券才能參加活動。然后會員折上折和會員積分是由用戶屬性決定的,不需要在促銷里另外保存。
??除此之外的其它促銷都滿足“在某個(gè)時(shí)間內(nèi)購買了某個(gè)商品,就可以優(yōu)惠多少元或者獲得贈品”這樣一個(gè)結(jié)構(gòu),這些活動都可以放到一個(gè)表里保存。

??把促銷活動的公有信息放到主表里,各種不同活動的特有信息放到各自的主表里,子表的id就使用主表的id。如果使用Doctrine作為ORM可以很方便地實(shí)現(xiàn)這種架構(gòu)。
??優(yōu)惠券的數(shù)據(jù)表也使用相同的結(jié)構(gòu),在主表里保存券的通用配置,在子表里保存滿減券、滿折券、兌換券等不同券的信息。
其它問題
??活動的一般格式是買滿多少元/多少件,可以減少多少元/打多少折/獲得什么贈品,可以把這些抽象成活動條件和活動結(jié)果兩類策略,在各種不同的促銷里都可以調(diào)用。
??不同活動疊加時(shí),后面的活動可以按照原價(jià)來計(jì)算優(yōu)惠,也可以按照實(shí)付價(jià)來計(jì)算,也可以從某一層級開始用實(shí)付價(jià)。
??活動的可參與人群可以按用戶標(biāo)簽以及標(biāo)簽的交并補(bǔ)運(yùn)算進(jìn)行配置。參與活動的商品范圍一般按品牌、品類、商品id進(jìn)行配置,或者這些屬性的交并補(bǔ)運(yùn)算。這兩個(gè)配置的集合運(yùn)算的實(shí)現(xiàn)方式將在后面的文章詳細(xì)說明。
??以上只是促銷活動的大致架構(gòu),在實(shí)際開發(fā)中由于各種促銷之間有很大的差異,這個(gè)架構(gòu)還要做很多兼容處理。