业务总结001:优惠券与礼包活动
優(yōu)惠券相信大家在各大電商網(wǎng)站購(gòu)物的時(shí)候都用過(guò),有的時(shí)候領(lǐng)了一張券會(huì)想法設(shè)法的挑選商品來(lái)達(dá)到優(yōu)惠券的使用金額,哪怕買了預(yù)期之外的商品也要把這張券用掉,褥羊毛的心態(tài)大部分人都有。
從產(chǎn)品的角度思考類似優(yōu)惠券這種營(yíng)銷活動(dòng)正是抓住了用戶褥羊毛的心態(tài),促進(jìn)用戶消費(fèi)來(lái)增加商品的銷量,從而賺取更多的利益。現(xiàn)在很多類電商產(chǎn)品在你注冊(cè)之后會(huì)時(shí)不時(shí)的給你發(fā)一張券,當(dāng)你有需求時(shí)還是很樂(lè)意去 app 上面買東西的。
最近一段時(shí)間也是接觸了營(yíng)銷優(yōu)惠券相關(guān)的業(yè)務(wù),我所處的情況比較復(fù)雜 ,公司正經(jīng)歷 C 端業(yè)務(wù)由 PHP 切到 Java 的過(guò)程,優(yōu)惠券相關(guān)業(yè)務(wù)以前是 PHP 做的,需要全部切成 Java,除了需要保證功能可用還要完成數(shù)據(jù)轉(zhuǎn)化,當(dāng)用戶由老版本升級(jí)到新版本,整個(gè)過(guò)程是無(wú)感知的。接下來(lái)我主要從優(yōu)惠券需求分析、優(yōu)惠券業(yè)務(wù)現(xiàn)狀、需求遷移設(shè)計(jì)、數(shù)據(jù)遷移與需求優(yōu)化幾個(gè)方面談?wù)勛罱母惺芘c收獲。
一、優(yōu)惠券需求分析
1.1 優(yōu)惠券模型
一個(gè)完整的優(yōu)惠券信息可以理解為一套規(guī)則的組合,主要包括以下幾點(diǎn):基本信息、優(yōu)惠券類型、優(yōu)惠券業(yè)務(wù)類型、有效期、使用范圍等。可以結(jié)合自己的業(yè)務(wù)場(chǎng)景調(diào)整。
- 基本信息:優(yōu)惠券的基本信息主要有優(yōu)惠券的名字,發(fā)放數(shù)量,每人限制領(lǐng)取數(shù)量,規(guī)則描述等
- 優(yōu)惠券類型:滿減券、折扣券等
- 優(yōu)惠券業(yè)務(wù)類型:這個(gè)業(yè)務(wù)類型可以根據(jù)產(chǎn)品的定位考慮是否需要,比如我們是醫(yī)療相關(guān)的產(chǎn)品,會(huì)售賣一些服務(wù)與商城商品,因此我們就有商城與服務(wù)兩種業(yè)務(wù)類型的券
- 有效期:有效期一般有兩種,固定時(shí)間段與設(shè)置固定有效天數(shù)
- 適用范圍:全部商品可用、指定商品分類可用、指定商品可用等
1.2 優(yōu)惠券活動(dòng)
上面梳理的優(yōu)惠券模型只是一個(gè)模板,并不具備活動(dòng)信息,因此一個(gè)優(yōu)惠券模板并不具備發(fā)放資格,一般我們會(huì)搞一個(gè)專題活動(dòng)頁(yè),點(diǎn)進(jìn)去是一張或者多張優(yōu)惠券,活動(dòng)一般有開(kāi)始與結(jié)束時(shí)間、活動(dòng)狀態(tài)、圖片、鏈接等信息。活動(dòng)創(chuàng)建完成后對(duì)優(yōu)惠券與活動(dòng)進(jìn)行關(guān)聯(lián),優(yōu)惠券發(fā)放更靈活,無(wú)論從產(chǎn)品還是開(kāi)發(fā)角度都更容易理解。
1.3 發(fā)券方式
一個(gè)用戶獲取優(yōu)惠券的方式有很多種:
- 專題活動(dòng)頁(yè)面手動(dòng)領(lǐng)取
- 注冊(cè)或者進(jìn)入 APP 主頁(yè)系統(tǒng)以彈窗形式自動(dòng)發(fā)放
- 精準(zhǔn)營(yíng)銷,以任務(wù)的形式給某些用戶群體發(fā)放,通過(guò)短信或者 push 推送告知用戶
- 分享領(lǐng)券,比如我們常用的外賣軟件,分享后自己與他人皆可領(lǐng)券
- 后臺(tái)手動(dòng)給指定用戶發(fā)放(后門)
1.4 下單流程
下面主要從開(kāi)發(fā)的角度來(lái)分析,用戶選購(gòu)商品在下單時(shí)需要獲取所有可用優(yōu)惠券(達(dá)到滿減金額、指定商品等條件),默認(rèn)選中對(duì)用戶最優(yōu)惠的,如果業(yè)務(wù)復(fù)雜這里還需要考慮多優(yōu)惠券互斥情況。
用戶領(lǐng)取的優(yōu)惠券一般有未使用,已使用,已過(guò)期幾個(gè)狀態(tài),當(dāng)下單使用了一張優(yōu)惠券時(shí),這張優(yōu)惠券的狀態(tài)應(yīng)該怎么設(shè)置,因?yàn)橛脩粝聠尾⒉灰馕吨鴷?huì)支付,訂單可能會(huì)被用戶取消,也可能是因?yàn)槌瑫r(shí)未支付取消,也可能被核銷。
這個(gè)可以根據(jù)產(chǎn)品需求處理,比如用戶下單的優(yōu)惠券,在取消訂單時(shí)不退回,那可以直接修改成已使用狀態(tài)。如果取消訂單需要退還用戶優(yōu)惠券,那就不能改成已使用狀態(tài),可以考慮用一個(gè)中間態(tài)來(lái)表示下單未支付的情況-凍結(jié),這個(gè)凍結(jié)只是中間隨著訂單狀態(tài)轉(zhuǎn)化而變化的狀態(tài),靈活程度更高。
二、優(yōu)惠券業(yè)務(wù)現(xiàn)狀
遷移前發(fā)券的方式主要有三種
- 新用戶注冊(cè)發(fā)放一個(gè)新人禮包,這個(gè)禮包里有多個(gè)券,由系統(tǒng)統(tǒng)一發(fā)放
- 商城領(lǐng)券中心,有多張券,需要用戶手動(dòng)領(lǐng)取
- 后臺(tái)給指定用戶發(fā)券
老的設(shè)計(jì)一個(gè)優(yōu)惠券并不是一個(gè)模板,而是具備了活動(dòng)屬性,比如狀態(tài)、開(kāi)始與結(jié)束領(lǐng)取時(shí)間等,商城里的領(lǐng)券中心也不是一個(gè)可以專門配置的專題活動(dòng),而是獲取指定商城里正在進(jìn)行中的優(yōu)惠券列表,導(dǎo)致優(yōu)惠券需求直接被做死了,我覺(jué)得現(xiàn)在有的運(yùn)營(yíng)可能都不清楚這個(gè)券是怎么配置的。
優(yōu)惠券禮包是一個(gè)活動(dòng),活動(dòng)本身具備了發(fā)放數(shù)量、狀態(tài)、活動(dòng)開(kāi)始與結(jié)束時(shí)間等數(shù)據(jù),而優(yōu)惠券本身也具有這些數(shù)據(jù),在校驗(yàn)上要以哪種數(shù)據(jù)為準(zhǔn),是個(gè)很難抉擇的問(wèn)題,設(shè)計(jì)的有點(diǎn)糟糕,而且禮包只支持添加優(yōu)惠券,比如以后會(huì)在禮包里添加紅包等其他優(yōu)惠,會(huì)很難適配。
要保證新老版本數(shù)據(jù)遷移對(duì)用戶無(wú)影響,在重新設(shè)計(jì)前,我們通過(guò)抓包,定位 PHP 接口的方式來(lái)梳理需求,因?yàn)楹芏嘈枨蠖际沁\(yùn)營(yíng)直接找開(kāi)發(fā)做的,導(dǎo)致沒(méi)有需求文檔,這也是為什么優(yōu)惠券會(huì)設(shè)計(jì)的如此糟糕的原因,找產(chǎn)品梳理需求,他們也是一臉懵逼,后面只能通過(guò)看 PHP 代碼整理邏輯。
線下產(chǎn)品推廣非常給力,每天新用戶增量大概在 1w 左右,一個(gè)新人禮包里配了 7 張券,這樣計(jì)算起來(lái),優(yōu)惠券領(lǐng)取記錄每天會(huì)有 7w 左右的增量。查 DB count 的時(shí)候有 220w+ 的數(shù)據(jù),而且按照這個(gè)增量,突破 500w 也只是時(shí)間問(wèn)題,后來(lái)一個(gè)多月的時(shí)間就從 220w 漲到了 550w,遷移數(shù)據(jù)的時(shí)候需要把分庫(kù)分表方案也規(guī)劃進(jìn)去。
后臺(tái)運(yùn)營(yíng)中心有優(yōu)惠券統(tǒng)計(jì)需求,比如發(fā)放數(shù)量、使用數(shù)量這些簡(jiǎn)單的統(tǒng)計(jì),因?yàn)闆](méi)有數(shù)據(jù)部門,這些統(tǒng)計(jì)都是以 SQL 的方式做的,因?yàn)閿?shù)據(jù)量比較大,統(tǒng)計(jì)的維度又比較多,很多統(tǒng)計(jì)都不會(huì)命中數(shù)據(jù)庫(kù)索引,就導(dǎo)致有的統(tǒng)計(jì)接口非常慢,最慢的一個(gè)甚至需要 4s 左右。
三、需求遷移設(shè)計(jì)
3.1 遷移目標(biāo)
優(yōu)惠券及禮包活動(dòng)由 PHP 邏輯到 Java 的重寫切換,數(shù)據(jù)從 PHP 表到 Java 表的轉(zhuǎn)化遷移,新老版本用戶無(wú)感知,數(shù)據(jù)完全統(tǒng)一。
3.2 遷移前準(zhǔn)備
剛開(kāi)始接觸的時(shí)候,自己的想法很簡(jiǎn)單,想簡(jiǎn)單熟悉下業(yè)務(wù)邏輯后直接擼代碼,完全沒(méi)有往設(shè)計(jì)方面上想,后來(lái)被石南開(kāi)導(dǎo)了一番后,意識(shí)到了架構(gòu)設(shè)計(jì)與業(yè)務(wù)整理的重要性。充分考慮每個(gè)問(wèn)題,按照計(jì)劃推進(jìn)每個(gè)階段該做的事。
接下來(lái)的兩周一直在看 PHP 代碼,整理業(yè)務(wù)邏輯,梳理表結(jié)構(gòu),對(duì)這個(gè)過(guò)程進(jìn)行歸檔,這個(gè)過(guò)程很漫長(zhǎng),也很枯燥,但是卻是很必要的。兩周時(shí)間下來(lái)幾乎所有的邏輯都梳理清楚了,接下來(lái)也是最難的一部分,根據(jù)原有的邏輯,重新設(shè)計(jì)表結(jié)構(gòu)。
自己剛做營(yíng)銷業(yè)務(wù)不久,對(duì)營(yíng)銷活動(dòng)的概念理解還不是很透徹,前期總認(rèn)為優(yōu)惠券、禮包與活動(dòng)概念不搭邊,因此設(shè)計(jì)出來(lái)的表結(jié)構(gòu)也很直白,也不能適配我們底層的表結(jié)構(gòu)與代碼架構(gòu),對(duì)于底層優(yōu)惠券模型的設(shè)計(jì)也有點(diǎn)糟糕,后來(lái)參考了其他知名電商網(wǎng)站的優(yōu)惠券設(shè)計(jì)后,和石南重新設(shè)計(jì)了表結(jié)構(gòu)。
我們?cè)谝郧皹I(yè)務(wù)的基礎(chǔ)上把優(yōu)惠券模型單獨(dú)抽成優(yōu)惠券模型表(模型表在預(yù)留出查詢字段后,可以按照業(yè)務(wù)字段分組以 JSON 的形式存儲(chǔ),這樣擴(kuò)展性會(huì)非常高),為了適配以前優(yōu)惠券的活動(dòng)屬性,在優(yōu)惠券的基礎(chǔ)上又套用了一層活動(dòng)信息,這只是我們?yōu)榱诉m配原有業(yè)務(wù)設(shè)計(jì)的,不建議大家也這樣做,因?yàn)闀r(shí)間緊張,后期優(yōu)化我們會(huì)考慮把活動(dòng)屬性剝離出來(lái)。
和產(chǎn)品確認(rèn)基本需求后,優(yōu)惠券統(tǒng)計(jì)需求直接干掉了,因?yàn)檫@些需求都是以前運(yùn)營(yíng)找開(kāi)發(fā)加的。具體的表設(shè)計(jì)這里就不進(jìn)行細(xì)節(jié)性的介紹了,參考上面的兩張圖,相信也能給大家一些思路。
3.3 開(kāi)發(fā)細(xì)節(jié)
我們營(yíng)銷底層有一套規(guī)則校驗(yàn)引擎,比如像用戶領(lǐng)取優(yōu)惠券,下單獲取可用優(yōu)惠券,這中間一系列的規(guī)則條件校驗(yàn)都可以走規(guī)則引擎。關(guān)于規(guī)則引擎我簡(jiǎn)單的介紹一下,大家可以根據(jù)實(shí)際情況設(shè)計(jì)。
- 首先要有規(guī)則,規(guī)則字段(規(guī)則集合)由前端拼接完成后存儲(chǔ)到 DB 里
- 從 DB 里取出規(guī)則字符串轉(zhuǎn)成對(duì)象集合
- 傳入規(guī)則對(duì)象集合,封裝用戶規(guī)則條件,走規(guī)則校驗(yàn)器統(tǒng)一校驗(yàn)
大致的流程就是上面幾個(gè)步驟,沒(méi)有思路的話大家可以參考營(yíng)銷平臺(tái)架構(gòu)。核心類圖如下
很多營(yíng)銷活動(dòng)一般都會(huì)有庫(kù)存概念,不同類型的獎(jiǎng)勵(lì)庫(kù)存扣減方式可能是不同的,對(duì)于庫(kù)存扣減也可以進(jìn)行抽象,根據(jù)活動(dòng)類型進(jìn)行路由分派走不同的處理流程,這塊設(shè)計(jì)比較簡(jiǎn)單就不進(jìn)行細(xì)節(jié)介紹了。
四、數(shù)據(jù)遷移
業(yè)務(wù)邏輯梳理清晰后,編寫代碼并不是一件難事。后期比較頭大的是數(shù)據(jù)遷移,因?yàn)橹?PHP 與我們的表結(jié)構(gòu)不一樣,甚至差別很大,優(yōu)惠券領(lǐng)取記錄表已經(jīng)由之前的 220w 增長(zhǎng)到了 550w+,下面來(lái)總結(jié)下整個(gè)過(guò)程我們是如何做的。
4.1 遷移方案
因?yàn)楸斫Y(jié)構(gòu)差別大,有些業(yè)務(wù)邏輯我們進(jìn)行了優(yōu)化,并不能完美兼容之前的數(shù)據(jù),或者說(shuō)要兼容以前的數(shù)據(jù)需要復(fù)雜的邏輯轉(zhuǎn)化,因?yàn)閿?shù)據(jù)量比較大,轉(zhuǎn)化過(guò)程過(guò)于復(fù)雜會(huì)影響遷移的效率,一些優(yōu)先級(jí)較低平常不怎么用到的需求我們和產(chǎn)品及運(yùn)營(yíng)確認(rèn)后直接干掉了。
遷移開(kāi)發(fā)方案采取雙數(shù)據(jù)源的方式,通過(guò)讀取 PHP 表數(shù)據(jù),進(jìn)行字段與邏輯轉(zhuǎn)化后批量插入 Java 數(shù)據(jù)表。
4.2 遷移代碼開(kāi)發(fā)
完成數(shù)據(jù)遷移主要考慮兩個(gè)方面:全量數(shù)據(jù)與增量數(shù)據(jù)。
- 全量數(shù)據(jù)遷移:全量數(shù)據(jù)遷移并不是一下讀取數(shù)據(jù)庫(kù)里所有數(shù)據(jù),試想一下如果一次讀取 550w+ 的數(shù)據(jù),可能會(huì)造成什么樣的后果。全量數(shù)據(jù)同步我們進(jìn)行了分段,比如 550w+ 的數(shù)據(jù),我們可以分成 6 次進(jìn)行,每次同步 100w,下一次同步時(shí)跳過(guò)上次同步的 100w,這樣對(duì)數(shù)據(jù)庫(kù)與服務(wù)器的壓力會(huì)小很多
- 增量數(shù)據(jù)遷移:增量數(shù)據(jù)也是考慮兩個(gè)方面:增量修改與增量添加,怎么判斷是修改了數(shù)據(jù)還是新增了數(shù)據(jù),可以通過(guò)主鍵 ID 與上次同步時(shí)間戳來(lái)判斷。根據(jù)時(shí)間戳去數(shù)據(jù)庫(kù)拉取增量,然后根據(jù)傳入的主鍵 ID(全量同步最大的主鍵 ID) 判斷是增量更新還是增量添加,然后走不同的邏輯,這個(gè)過(guò)程也可以參考數(shù)據(jù)增量考慮是否需要分段同步
有的文章分析 mybatis 的批量操作并不是批量操作的數(shù)據(jù)越多效率就越高,如果數(shù)據(jù)過(guò)多會(huì)導(dǎo)致 mybatis SQL 字符串很大,當(dāng)批量插入的時(shí)候效率反而會(huì)下降。我們選擇每次從數(shù)據(jù)庫(kù)讀取 50 條數(shù)據(jù),效率還可以,希望能給大家一個(gè)參考。
4.3 數(shù)據(jù)同步效率預(yù)估
除了優(yōu)惠券領(lǐng)取數(shù)量數(shù)據(jù)比較多,其他相關(guān)的表數(shù)據(jù)大概也有 100w 左右,因?yàn)橹皼](méi)有搞過(guò)數(shù)據(jù)同步,對(duì)同步效率沒(méi)有概念,不知道一分鐘能跑多少數(shù)據(jù)。
在遷移代碼開(kāi)發(fā)好后,導(dǎo)入了 100w 的測(cè)試數(shù)據(jù),在本地測(cè)試了幾次,如果過(guò)程沒(méi)有復(fù)雜的查詢,1 - 2s 左右就能跑完 1w 的數(shù)據(jù),這還是在測(cè)試服務(wù)器上跑出來(lái)的結(jié)果,正式環(huán)境會(huì)比這還要快。后來(lái)我們接了分庫(kù)分表,正式環(huán)境 6 - 7s 能跑完 1w 的數(shù)據(jù),550w+ 的數(shù)據(jù),一個(gè)小時(shí)不到全量就跑完了。
在沒(méi)測(cè)試之前還以為很慢,項(xiàng)目環(huán)境跑了一遍看結(jié)果嚇了一跳,采用雙數(shù)據(jù)源的方式同步在數(shù)據(jù)量不是特別大的情況下還是可行的。
4.4 分庫(kù)分表
我們接的是 sharding-jdbc,只需要做一些簡(jiǎn)單的配置,就可以實(shí)現(xiàn)分庫(kù)分表,用起來(lái)很方便,侵入型也比較小。因?yàn)橹皼](méi)有用過(guò)這個(gè)框架,在同步數(shù)據(jù)的時(shí)候發(fā)現(xiàn)有的版本不支持批量插入,這也是當(dāng)時(shí)沒(méi)有想到的問(wèn)題,后來(lái)升級(jí)版本就可以了。
雖然用了 sharding-jdbc 但是內(nèi)部原理并不清楚,后續(xù)把分庫(kù)分表也規(guī)劃到學(xué)習(xí)計(jì)劃中去。
五、需求優(yōu)化
5.1 線上慢查詢優(yōu)化
項(xiàng)目上線后發(fā)現(xiàn)有的接口的 RT 很大,遠(yuǎn)遠(yuǎn)超出了能夠接受的范圍。主要通過(guò)下面三個(gè)步驟來(lái)解決問(wèn)題
- 添加緩存
- 刪除不必要的業(yè)務(wù)代碼
- 檢查索引
一般接口 RT 比較大的接口都比較復(fù)雜,要查詢的數(shù)據(jù)也比較多,對(duì)于這些數(shù)據(jù)考慮是否可以添加緩存,減少查詢 DB 的次數(shù),另外一個(gè)方面就是重新梳理業(yè)務(wù),檢查是否有更簡(jiǎn)單的方案來(lái)實(shí)現(xiàn),一個(gè)表添加一個(gè)索引的開(kāi)銷是比較大的,因此最后一步才是考慮添加索引。
5.2 后續(xù)需求優(yōu)化
優(yōu)惠券現(xiàn)在有一個(gè)嚴(yán)重的問(wèn)題:承載了活動(dòng)屬性,因?yàn)闀r(shí)間緊迫,我們?cè)谶w移過(guò)程中還保留了原先的設(shè)置,這樣就導(dǎo)致有的優(yōu)惠券需求被做死了,想要擴(kuò)展非常難,關(guān)于這點(diǎn)已經(jīng)和產(chǎn)品溝通過(guò),后面優(yōu)化的時(shí)候希望去掉活動(dòng)屬性,走統(tǒng)一的優(yōu)惠券模版,優(yōu)惠券活動(dòng)的形式進(jìn)行配置。
六、總結(jié)
- 遇到的問(wèn)題總會(huì)有解決辦法,切勿急躁
- 需求分析,架構(gòu)設(shè)計(jì)非常重要,寫代碼前花半天時(shí)間寫下文檔,畫畫流程圖,先把思路理清楚再寫代碼
- 項(xiàng)目規(guī)劃,按計(jì)劃推進(jìn)每個(gè)過(guò)程中要做的事
- 項(xiàng)目總結(jié),做完一個(gè)項(xiàng)目經(jīng)歷了什么,留下了什么
感謝項(xiàng)目組的信任,在一開(kāi)始接觸這個(gè)業(yè)務(wù)的時(shí)候還有些擔(dān)心不能完成,最后還是一步步挺過(guò)來(lái)了,最終順利上線!有的時(shí)候你不逼逼自己,就不知道原來(lái)自己也能做這么多事。
總結(jié)
以上是生活随笔為你收集整理的业务总结001:优惠券与礼包活动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信号被盗怎么办卡里还有钱(微信号被盗怎
- 下一篇: 业务总结002:秒杀活动架构设计