可裝配的優惠券系統設計與實踐
1. 背景簡介
優惠券系統是互聯網電商時代最為常用運營手段,拉新,促活,留存,在運營的過程中,能快速的、靈活的支持運營頻繁多變的營銷需求。傳統的實現方案,不僅開發周期長、成本大,并且不能及時影響運營多變的需求,運營活動復用的效率較低。
車服務優惠券系統提供了一種可裝配且通用優惠券核心框架的實現,快速搭建滿足業務需要的優惠券模板功能。僅需開發一次自定義業務組件,可靈活裝配多優惠券模板,發券用券驗券接口無需二次開發,平行無縫對接第三方商家券,節省開發資源,減少開發成本,提高了運營效率。
2. 系統設計
2.1優惠券的生命周期
組件創建:定義表單元素,生成自定義組件,存入組件庫中。
裝配模板:運營按業務需求,選擇組件,組合裝配在一起,生成優惠券模板。
生成批次:選擇模板,關聯配置業務券,比如:加油券,充電券,洗車券,代步券等。模板組合裝配在一起,生成優惠券批次。
投放活動:配置運營活動,關聯批次券信息。可組合裝配成券包,或單張券,投放客戶端或H5頁面。
兌換領取:發放形式,可分為3種,兌換碼兌換,運營活動用戶登錄領券,運營后臺定向發券,滿足業務規則,比如新注冊用戶,業務新老用戶,首單用戶發券進行間接發券。
核銷使用:用戶購買商品下單,拉取可用券,下單鎖定券,支付核銷券。
取消訂單還券:訂單取消,返還用戶券,券可再次使用。
券快過期提醒:推送提醒用戶使用。
結算:生成優惠券,平臺與商家承擔比例,執行結算補貼。
2.2優惠券系統設計思維導圖
圖 2-3
2.3優惠券系統基本流程圖示
組件創建 >> 裝配模板 >> 裝配批次 >> 裝配活動 >> 發券 >> 核銷券 >> 結算
圖 2-4
2.4優惠券系統架構圖示
圖 2-5
3. 核心功能
3.1創建自定義組件
自定義組件:創建優惠券組件,生成優惠券模板必要條件,因為模板生成時,需要加載裝配相關的表單組件。
創建組件所需元素信息為:組件表單名稱,Placeholder文本信息,JS驗證功能,數據源,組件類型(包含文本框,數值框,下拉列表框,多選列表框控件,聯選日期控件,滿減金額控件,數據列表模態選擇框等常用組件),表達式關鍵詞等。其中配置Js驗證功能,需要預先開發JS驗證插件,集成常用的驗證插件庫中,用于模板生成時自動檢查表單中元素。其中配置表達式關鍵詞,用于生成規則條件表達式。其中設置數據源,用于初始化組件下拉列表,數據可選模態框等。組件生成后,保存到組件庫表中,提供給模板創建使用。
解決的問題:
自定義組件的實現,存入組件庫中,提高了代碼復用性,常用的組件,任何業務券都可以復用,避免多次開發,增加了業務的靈活性,對系統進行解耦,也可以分組開發,部署。后臺優惠券模板可以自由組合,根據運營需求,快速自行裝配。提高運營工作效率,支持多業務券場景配置。
優惠券自定義組件生成,方便快速構建業務優惠券模板,需求靈活實現。不管是優惠類型,還是業務需求方可變的條件,我們都能從設計層面去實現它。來節約開發運營成本。并且生成可配置的多規則條件表達式。下單使用時,一次驗證,所有的條件是否可達,滿足優惠券下單使用。使優惠券系統結構簡潔清晰,同時將可變的部分,完全開放給使用者,提供了非常大的靈活性。
創建組件頁面圖示如下:
圖:3-1-1
組件創建流程圖示如下:
圖 3-1-2
3.2創建自定義模板
自定義模板創建:由3.1中創建的組件,按業務需求,進行動態裝配。優惠券模板可設置3種類型,分別為商家券模板,平臺券模板,公共模板。用于批次生成時,對運營進行不同角色權限隔離使用。其中配置業務類型,用于客戶端拉券時按業務隔離展示使用。配置滿減券,立減券,折扣券,用于區分券的使用條件。模板可選組件分為兩部分,固定區域,可變區域。固定區域為優惠券中的常用組件區域,系統自動加載固定類型組件信息,見圖3-2-1所示,固定類型組件是每張優惠券共有的屬性。比如:優惠券標題,面額,庫存數量,使用時間,平臺與商家承擔比例,折扣,最高金額,使用跳轉Url等。可變區域為運營活動用券時需要驗證的組件區域,比如限制的商家,門店,滿減條件等組件。若組件庫中組件元素為非固定類型,系統會區分當前組件為可變組件,展示在選擇組件區域,提供運營人員按業務券的需求,可從組件庫中進行拖拉組件到指定可變區域中。最后模板生成,系統會保存模板庫中,以供批次創建生成使用。至此,模板再次加載或編輯時,其中的業務部分,基礎屬性部分,可變部分,系統會根據所存的模板信息,關聯到批次中,同時相應的加載出元素組件。
解決的問題:
運營人員可根據業務券需求,自定義裝配優惠券模板。通過界面,進行拖拉組件,支持組件元素拖拉排序,快速生成優惠券模板,簡潔快捷,動態裝配。存入模板庫中。提供運營創建批次時,多次復用,減少開發成本,提高工作效率。
創建模板頁面圖示如下:
圖 3-2-1
模板流程圖示:
圖 3-2-2
3.3創建自定義批次券包
- 自定義批次:設定批次的通用屬性,動態加載優惠券模板,自動校驗表單控件三部分組成。
- 通用屬性配置:發券的形式,包含直接領取券與兌換券碼形式。限制領取次數,可限當天每人領取次數,或者批次總領取次數。領取人群的配置限制,包含業務新老用戶限制,注冊新老用戶的限制。短信釘釘提醒功能配置,過期券預提配置,運營庫存提醒配置。商家單位配置,財務PR單號配置等。預提醒功能技術實現,通過延遲隊列,定時任務執行實現。
- 動態優惠券模板:
- 優惠券批次創建,加載可用的優惠券模板。運營可根據需求,在下拉列表中選擇優惠券模板。此處優惠券模板,是根據可視化組件,分布式控件動態加載渲染呈現,去除了冗余的模板組件。可以無限制的加載需要配置的優惠券。比如:加油,充電,洗車。
- 自動校驗證表單:
- 由于模板是動態加載的組件,多個優惠券模板,會有很多組件,組件類型不同,一個個寫表單校驗,會顯得很繁瑣,代碼臃腫,維護復雜 。現對所有的元素組件,進行動態提取組件配置的JS,然后根據JS校驗類庫,自動加載表單驗證,維護簡單,代碼簡潔。
解決問題:
批次可以多張券模板組合,最終生成一個券包。支持不同業務券組合,相同業務券組合。支持批次復制,模板復用。發券可以按券包形式發,也可以按單張券針對性發放。批次創建,是由模板動態加載自定義組件完成。多個券模板,如果按傳統模式開發,會很多組件元素要書寫,js功能也要逐個書寫,每當有新券需求,或者券限制條件升級變動,都會需要大量的控件開發,費時費力。本批次的設置,代碼簡潔,界面明了,所見即所得,無須過度開發,一勞永逸。
批次創建圖示如下:
圖 3-3-1
模板加載圖示如下:
圖 3-3-2
批次創建流程圖:
圖 3-3-3
3.4創建自定義活動
創建活動,可以配置整個業務的領券中心。聚合所有待領取的券,券包展示,同時提供對外輸出領券接口關鍵配置,需要設置活動名稱,活動的起始時間,活動KEY,互斥KEY,活動限制數量,活動的跳轉鏈接,活動所投放平臺渠道。可選擇關聯不同的券批次模板,通過活動KEY的領取維度,鎖定不同的批次券包,鎖定整個活動領取量,進行發券。通過互斥KEY的維度,鎖定不同的活動,用戶在活動A中可以領取哪些券,在活動B可以領取哪些券。通過活動中啟動,停止進行領取功能的控制,將活動中配置信息進行保存,存入活動領取中心庫中。
解決的問題:
適應于一個或多個活動場景,比如領券中心,領單張券,券包,一鍵批量領券,新老用戶領券。通過活動Key,關聯出整個活動配置的券包。通過活動控制券上線下線功能。對外輸出領券接口,提供領券的活動標識隨機碼Key,保證業務的保密性、可控性、復用性。
活動創建圖示3-4如下:
圖 3-4-1
活動配置流程圖示:
圖 3-4-2
4. 技術難點
4.1技術如何實現自定義組件
圖 4-1-1
創建模板時,是由多個組件裝配完成。新創建模板時,如何動態渲染加載多個組件?編輯模板如何對組件進行賦值?模板組件從技術層面是由多個Freemark模板視圖文件組合而成,比如圖 4-1-1 中propType_text.ftl 文件,propType_List.ftl文件,propType_Select.ftl文件等,在創建模板中頁面中,會根據組件池中,根據組件類型,通過<#include>進行文件加載傳遞數據完成。如下圖 4-1-2所示。
圖 4-1-2
模板視圖文件需要Html+Freemark+Jquery 預先開發創建好。其中表單控件中name為組件生成時配置的name屬性,文件中所有的${}語法屬性,均對應組件生成時配置屬性。比如4-1-3中:${prop.placeholder} 文本框描述提示信息,${prop.flagword} 是使用條件占位符,${prop.jsfunction} js自定義驗證功能方法名,${prop.id} 組件Id,${prop.type} 組件類型,${prop.formnameExt} 模板Id+組件名稱,${prop.datasource}等不再一一列舉。
動態組件插件示例:舉個文本框組件propType_Text.ftl 示例:后端讀取模板中文本框屬性值傳遞賦值。
圖4-1-3
模板視圖文件中 ${prop}是整個頁面渲染控件主要引擎對象。又是后端模板通用對象模型,對應的后端開發類名為:CouponTempPropView ,如下面代碼所示。后端接口通過模板Id,關聯所有模板中的組件屬性信息。然后封裝成CouponTempPropView數據對象,進行初始化值賦值。若模板頁面首次新頁面加載,后端接口HandlAdapter處理適配器響應CouponTempPropView對象數據,通過ViewResolver視圖解析器初始化組件頁面表單,進行渲染呈現。若模板頁面編輯時,需要對組件表單input元素進行賦值。由${prop}對象取CouponTempPropView對象數據中的${prop.formvalue},然后jquery查找當前prop.formvalue的input隱藏域控件的value值,該value是由多個屬性值組合而成,根據當前組件,需要進行拆解,然后將值傳遞給name=${prop.formnameExt}的input控件,進行傳遞賦值。到此組件技術裝配屬性完成。創建批次時,選擇優惠券模板,動態加載標簽自定義組件呈現后臺UI,如何加載模板的相關的組件信息呢,同樣是引用模板視圖文件完成的。技術運用了動靜分離技術手段及組件化模板插件響應,運營人員選擇優惠券業務模板,后端數據根據運營的選擇模板,程序進行準備模板及控件所需數據。通過模板數據裝載,初始化需要加載的模板中關聯組件,前端頁面不需要堆積實現未知的組件,僅根據后端數據,注入到模板關聯組件的插件中,傳遞數據,需要呈現哪一塊插件,就會渲染所關聯的組件,完全數據來驅動實現組件的呈現,實現方式和現在的Vue想法一致。完全數據初始化控件,數據傳遞控件的實現。
編輯批次時,關聯多個模板信息,后端如何進行動態傳值:由批次信息,找到批次所關聯的一個或多個模板信息,再由模板查找,加載出模板對應的組件信息,組件信息包含表單元素,通過批次信息表中的存儲的字段值,轉化成Key-value,key為表單元素,value為對應值。對模板組件屬性結構類型進行一一映射,通過Action傳遞前端頁面進行渲染優惠券模板組件視圖。最終實現和創建模板時的加載視圖文件一致,不再詳細贅述。
4.2技術如何動態生成條件表達式
由于優惠券批次包含多個優惠券模板,每個優惠券模板的使用條件是隨業務需求變化,所以我們如果存儲這樣動態可變的值,數據表中每次需升級擴展字段,同時又要升級系統的數據讀取Mybatis文件,以及優惠券模板文件中限制條件規則等。顯然這種方式比較笨拙,粗曠,又不利于優惠券系統的維護。我們自定義的規則條件表達式,應運而生。
? 什么是規則條件表達式:
它是多個模板組件使用限制內容組合一起,生成一種可變的自定義表達式。參考借鑒逆波蘭表達式生成規則而設置。
? 如何生成:
創建優惠券批次時,通過模板Id關聯出多個組件信息,封裝到數據模型中ListpropDtoList,然后根據條件表達式方法,篩選出組件prop.getType()類型,比如數值類型,獲取prop.getFlagword條件表達式關鍵字為coupon_cityid,prop.getFlagdesc=10010。拼裝生成條件表達式為:(coupon_cityid=10010)。生成使用描述為:限城市為北京使用。其他類型視條件生成表達式。示例如下:
? 舉車品券生成示例:
在創建優惠券時,業務限定多條件表達式隨批次動態生成,舉例:車品券表達式:[coupon_city]_in(111010,120000)&&(coupon_[order]>10&&coupon_[order]<100)&&[coupon_tag]in(123),表達式條件翻譯為:優惠券滿足城市北京或天津,訂單金額滿10元小于1000元,且優惠券品類標簽為123的商品使用,提交保存,生成優惠券批次相關信息,生成驗證優惠券表達式,存儲數據表中,及緩存中。
驗證券表達式條件,解析表達式代碼如下:
用戶下單使用券,系統校驗券的使用條件,由條件表達式多條件驗證,從用券接口取出訂單號,訂單金額,訂單商品,商家信息,運費服務費等作為檢驗條件,將請求參數存入Map字典中,驗證用戶優惠券時,通過Map中對應名稱,正則匹配組件中標簽名、標簽參數、操作符、操作數值進行標簽名的替換,執行commonBLL.EvalExpr表達條件運算,結果滿足條件,當前券可進行下單結算使用。否則,將不能使用。
舉例,車品券表達式:
([coupon_city]in(10110)&&(coupon_[order]>10&&coupon_[order]<100)&&[coupon_tag]in(123)),[coupon_city]:10110, coupon_[order]:20 , [coupon_tag]:123,最終替換為(10110=10110)&& 20>10 && 20<100 && 123 =123)。
表達式執行完成后,生成訂單,省去傳統多個字段,循環迭代的邏輯驗證,有些字段關聯的屬性可能比較多,驗證的時候加載的時間會加長。有條件表達式,我們不用再做上面的多次驗證。只需要根據訂單參數,替換可變參數,執行表達式規則計算,完成所需要的條件驗證。
4.3技術如何實現發券,限制超發并發
運營活動配置券包,客戶端進行活動發券。用戶登錄領券,領券接口驗證當前請求參數,首先請求攔截進行簽名校驗,網關授權認證,并發管控請求限流桶,分布式Redis鎖。然后進行登錄用戶及風控校驗,校驗通過,讀取活動關聯的批次券包,讀取用戶領券信息存入Redis中HashTable,通過領券信息HashMap集合進行批次限制校驗,包含新老用戶,批次是否停止使用,批次每天領取量,總的領取限制量,活動是否停止校驗,校驗通過后,組合領券基本信息,存入jetcache+redis 二級緩存中,然后異步發送MQ。MQ消費者讀取信息入庫,同時創建多線程進行同步通知第三方券入庫,領券完成。
解決的問題:
領取接口整個流程,增加分布式Redis鎖+請求限流桶,比如秒殺券活動,短時間會增加百萬+的流量,由于服務器數據承載響應能力,帶寬流量有限,加入切面攔截請求,做容錯處理,同時加入分布式Redis原子鎖,進行并發控制,防止券因并發而超發的現象發生。同時驗證領取限制條件,解決了并發領取,權限領取,異步領取,攔截了一些風險用戶,實時反饋給用戶領取結果信息。整個過程,從1級,2級緩存中讀取更新數據,根據后臺配置分層進行校驗,極大提高了請求的響應速度,代碼更加簡潔,安全性、可擴展性、可維護性增強。
領券流程圖示如下:
圖 4-3-1
? 如何互斥?
一般在秒殺商品,直降商品,下單用券時,會進行拉取可用券列表時,對優惠券列表先進行按金額排序 ,輪循驗證當前券金額,與秒殺金額,直降金額進行比較,取最優惠的價格給用戶展示使用。節省運營投入成本。
有時平臺券與商家券,如果都是平臺采購券的運營場景,也需要用互斥規則。分別拉取平臺券,與商家券可用券列表,再進行取最大的券進行比對。擇取優惠力度最大券提供給用戶使用。
? 如何疊加?
平臺券與商家券,通常下單時,可以共同疊加使用。下單時,需要同時關聯兩張券的權益碼。兩張券同時變成核銷狀態。需要同時驗證訂單的總金額是否滿足券使用條件,一般用在滿減或湊單時使用。
? 如何平臺券轉發多家商家券?
車服務加油業務,油站屬于不同供應商,運營采購多家供應商的券,由于采購庫存有限。如何讓用戶在選擇油站用券時,僅能給用戶發一張商家券使用,不浪費采購庫存,又能促進業務拉新。我們在系統引用了平臺券關聯多商家券的模式,最終落地使用商家券。
比如:加油優惠券模板,新增供應商選擇組件,在運營后臺創建加油券時候,選擇關聯三家供應商。用戶通過領券活動得到一張平臺券,這張平臺券使用條件限制了三家供應商,用戶下單用這張券時,根據油站所屬供應商,平臺券校驗當前供應商可用條件,校驗通過調用商家發券接口發對應供應商商家券,商家券核銷完成后,更新當前平臺券。到此完成平臺券轉發商家券的邏輯。配置如圖所示:
圖 4-3-2
4.4技術如何實現用券驗券
用戶請求下單,拉取用戶可用券,從緩存中取出用戶所有券,根據業務規則動態加載請求參數,比如加油券,驗證訂單金額,油品類型,供應商,油站等條件,利用鍵值對key-value的形式,存儲請求參數,然后讀取每一張券的規則條件表達式。比如(coupon_seller in(12,23)) &&([order]>=50)&&(oil_product=3),通過正則表達式,取表達式中標簽占位符,由鍵值對中獲取key值替換占位符,然后執行表達式檢驗,完成驗券拉券過程。
用戶選擇券,進行創建訂單時,再次請求驗證當前券,判斷表達式條件是否滿足,券是否過期,是否被停用,檢驗領券賬戶,防止被篡改。然后訂單表中鎖定當前券碼。
用戶支付時,支付成功回調,再次驗證當前券碼是否可用,核銷當前券碼,券碼表中同時更新訂單號,使用時間,用券金額,券變更為核銷狀態。當支付失敗,視訂單未完成,訂單將定時自動取消,券被歸還。
解決的問題:
接口中根據業務不同,用券的場景不同,傳遞的參數通過鍵值對形式動態傳遞。用于替代券條件表達式占位符,快速驗券。不用關心哪個業務,哪個條件進行固定判斷。完全依賴Key-value中請求參數,動態驗證,實現接口裝配。核銷的時候,訂單與券進行雙向綁定,增加安全性,可靠性。
用券流程圖示如下:
圖 4-4-1
5. 總結
本系統將優惠券業務進行了高度的提煉和抽象,將系統中的變與不變進行了完全隔離。
通過不斷的開發積累組件化控件,使得運營可以更靈活的設計營銷方案,極大的提高了代碼復用和系統靈活性和效率,優惠券模板只需拖拽即可完成人機交互功能。
通過對優惠券系統模板的高度提煉抽象,使其與業務系統完全解耦,從上面的介紹中我們可以看到,業務系統只有一個動作,整理配置屬性參數,獲取結果,除此之外,業務系統不需要知道優惠券系統的任何其他細節,這就保證了兩個系統完全分離,分開開發、獨立部署,互不影響,提高開發效率和系統穩定性。
本系統具有明顯的降低開發成本、提高開發效率、提升系統穩定性的優點。更重要的是提供完全開放的優惠券可定義的控件,自由可變優惠券模板生成,即使后期業務需求變更,也不會重新回歸測試開發。只需要修改模板條件。即可0開發成本新增優惠券玩法,極大的滿足了運營頻繁多變的優惠券營銷需求。
作者簡介
吳彥斌
■ 曾任職于服務端研發部-服務端買用技術團隊-用車組。
■ 高級研發工程師,2015年加入公司,主要從事優惠券系統的研發,訂單系統的研發,物流管理系統的研發。
張東生
■ 服務端研發部-服務端買用技術團隊-用車組。
■ 服務端買用技術團隊用車組組長,負責之家用戶用車場景的各用車服務權益產品及用車工具產品的研發和架構工作。