原來賣票沒這麼簡單

前言

業務系統中技術攻關可能只是一小部分工作,更多的還是對於用戶需求和應用場景的深刻理解,而且這種理解需要隨著時間的推移不斷演進,否則就會出現「跟不上」的情況。如果能再有一些前瞻性的探索,始終使系統保持著一種不過度的超前設計,就可以平衡絲滑地迭代版本,而不是那種撕裂似的推倒重來。

業務背景

米攸(Wechat Mini Program)是一個專註於年輕人的活動社交平台,裡面有大量豐富有趣的活動,像桌游、劇本殺、電玩、聚餐、健身之類的活動都很受歡迎。用戶參與活動需要提交報名資訊、支付費用,俗稱 買票;活動主辦方需要統計報名資訊、收取費用,俗稱 賣票;因此,米攸需要提供一個服務於用戶和活動主辦方的票務系統。

票務系統 V1.0

系統設計階段,我們參考了不少資深活動領隊的意見,票務系統支援以下票種(票價):

  • 普通票
    普通用戶報名活動時的價格。

  • 會員票
    會員用戶報名活動時的價格,相較於普通票價,享有九折優惠。

  • 超級會員票
    超級會員用戶報名活動時的價格,相較於普通票價,享有七折優惠。

  • 早鳥票
    特定時間範圍內(簡稱:早鳥時間)報名活動時的價格,價格低於普通票價,主要針對活動推廣初期階段報名的用戶,即:報名越早,價格越低。

  • 團購票
    超過特定人數(簡稱:團購人數)報名活動時的價格,價格低於普通票價,主要針對同時多人報名的用戶,即:人數越多,價格越低。

用戶報名時,系統需要根據用戶會員、報名時間和報名人數,自動計算出用戶報名的票種票價,以及需要支付的總額。此外,會員用戶多人報名時,只有會員用戶自己可以享受會員價格優惠;活動早鳥票和團購票為可選項(活動可以不支援早鳥票或團購票),活動早鳥票價或團購票價可以低於會員票價或超級會員票價。

用戶單人報名時,票種僅需要考慮用戶票種(普通票、會員票、超級會員票或早鳥票),票價僅需要考慮用戶票種對應的票價,即:

票種 = 用戶票種
票價 = 用戶票價
總額 = 用戶票價 * 1

用戶多人報名時,票種需要考慮用戶票種(普通票、會員票、超級會員票、早鳥票或團購票)和他人票種(除用戶自己之外的其他人使用的票種,普通票、早鳥票或團購票),票價需要考慮用戶票價(用戶票種對應的票價)和他人票價(他人票種對應的票價),即:

票種 = 用戶票種 + 他人票種
票價 = 用戶票價 + 他人票價(系統僅記錄用戶票價)
總額 = 用戶票價 * 1 + 他人票價 * (報名人數 – 1)

票種和票價是對應關係,後文描述僅涉及票種。

用戶或他人票種的計算過程:

  1. 票種初始化為普通票種;
  2. 根據用戶會員、報名時間和報名人數,嘗試切換為價格更低的票種。

用戶單人報名

普通用戶

用戶票種初始為 普通票
如果活動支援早鳥票,用戶於早鳥時間報名,則切換為票種:早鳥票

會員

用戶票種初始為 普通票;用戶是會員,切換為 會員票
如果活動支援早鳥票,用戶於早鳥時間報名且早鳥票價低於用戶票價,則切換為 早鳥票

超級會員

用戶票種初始為 普通票;用戶是會員,切換為 超級會員票
如果活動支援早鳥票,用戶於早鳥時間報名且早鳥票價低於用戶票價,則切換為 早鳥票

綜上所述,用戶單人報名時,票種可能是以下四種之一:

  • 普通票
  • 會員票
  • 超級會員票
  • 早鳥票

用戶多人報名

普通用戶

用戶票種初始為 普通票,他人票種初始為 普通票
如果活動支援早鳥票,用戶於早鳥時間報名,則用戶票種切換為 早鳥票,他人票種切換為 早鳥票
如果活動支付團購票,用戶人數達到團購人數要求且團購票價低於用戶票價,則用戶票種切換為 團購票,他人票種切換為 團購票

會員

用戶票種初始為 普通票,他人票種初始為 普通票

用戶是會員,用戶票種切換為 會員票

如果活動支援早鳥票,用戶於早鳥時間報名且早鳥票價低於用戶票價,用戶票種切換為 早鳥票
如果活動支援早鳥票,用戶於早鳥時間報名,他人票種切換為 早鳥票

如果活動支援團購票,用戶人數達到團購人數要求且團購票價低於用戶票價,則用戶票種切換為 團購票
如果活動支援團購票,用戶人數達到團購人數要求且團購票價低於他人票價,則他人票種切換為 團購票

超級會員

用戶票種初始為 普通票,他人票種初始為 普通票;用戶是超級會員,用戶票種切換為 超級會員票

如果活動支援早鳥票,用戶於早鳥時間報名且早鳥票價低於用戶票價,用戶票種切換為 早鳥票
如果活動支援早鳥票,用戶於早鳥時間報名,他人票種切換為 早鳥票

如果活動支援團購票,用戶人數達到團購人數要求且團購票價低於用戶票價,則用戶票種切換為 團購票
如果活動支援團購票,用戶人數達到團購人數要求且團購票種低於他人票價,則他人票種切換為 團購票

綜上所述,用戶多人報名時,票種可能是以下之一:

  • 普通票 + 普通票
  • 早鳥票 + 早鳥票
  • 團購票 + 團購票
  • 會員票 + 普通票
  • 會員票 + 早鳥票
  • 會員票 + 團購票
  • 超級會員票 + 普通票
  • 超級會員票 + 早鳥票
  • 超級會員票 + 團購票

經過這樣嚴密邏輯的設計,我們認為自己考慮的已經足夠全面,應該能夠很好地覆蓋絕大多數應用場景,系統運行初期確表現的也很好,直到我們開始上線 自駕露營 之類的活動,系統的弊端開始突顯。

自駕活動,有的用戶是自己開車,有的用戶需要拼車,我們需要為開車的用戶減免一定的票價抵扣油費;也說是說,開車和拼車的用戶需要支付的票價是不一樣的,開車用戶需要 自駕票,拼車用戶需要 拼車票

露營活動,有的用戶是自帶裝備,有的用戶需要租用裝備,用戶裝備的需求差異可以還比較大;也說是說,不同裝備需求的用戶需要支付的票價也是不一樣的,我們應該把不同裝備組合設計為不同的套餐,每一個套餐對應著一個 套餐票,如:套餐1票、套餐2票等。

很遺憾,現在的票務系統完全不支援這種自定義票種的需求!

臨時的解決方案是線上用戶仍按現有票種報名支付,線下活動現場領隊再根據每位用戶的具體情況額外收取一次費用。用戶具體情況需要依賴於活動報名備註或線上溝通交流,匯總統計不好做,領隊工作難度大,效率很低,影響活動時間,用戶體驗也不好。

必須升級改造!

票務系統 V2.0

升級改造的方向很明確:我們需要為每一個活動設定不同的票種(簡稱:自定義票種);同時,每一種票種仍然需要支援會員、早鳥、團購場景

前文中的票種稱之為默認票種。

自定義票種

  • 票種名稱
    票種名稱,如:會員票、早鳥票、團購票、自駕票、拼車票、套餐1票,…。

  • 票種價格
    票種默認價格,類似於前文中的 普通票

  • 會員價格標識
    標識票種是否支援使用會員價格。

  • 會員價格
    票種支援使用會員價格時,設置票種會員價格,類似於前文中的 會員票

  • 超級會員標識
    標識票種是否支援使用超級會員價格。

  • 超級會員價格
    票種支援使用超級會員價格時,設置票種超級會員價格,類似於前文中的 超級會員票

  • 報名時間標識
    標識票種是否限制用戶報名時間,即:用戶只能在指定的時間範圍內使用該票種報名,類似於前文中的 早鳥票

  • 報名開始時間
    票種限制用戶報名時間時,設置報名開始時間。

  • 報名截止時間
    票種限制用戶報名時間時,設置報名截止時間。

  • 報名人數開關
    標識票種是否限制用戶報名人數,即:用戶只能在指定的人數範圍內使用該票種報名,類似於前文中的 團購票

  • 報名最少人數
    票種限制用戶報名人數時,設置報名最少人數。

  • 報名最多人數
    票種限制用戶報名人數時,設置報名最多人數。

  • 票種說明
    票種詳情。

每一個活動可以添加多個自定義票種,每一個票種都可以根據該票種的具體情況:

  • 設置名稱和價格,實現【普通票】功能
  • 選擇是否使用會員價格或超級會員價格,實現【會員票】或【超級會員票】功能
  • 選擇是否限制報名時間,實現【早鳥票】功能
  • 選擇是否限制報名人數,實現【團購票】功能

用戶開始報名活動時,系統會假設用戶報名人數為1人(僅為自己報名),自動根據用戶會員、報名時間從活動的多個自定義票種中選擇一個價格最低的票種供用戶參考;用戶實際報名活動時,可以根據自己實際的報名人數和活動具體需求選擇合適自己的那一個票種。

用戶多人報名時,自定義票種的會員優惠權益也僅限會員本人使用。

請注意,目前為止,自定義票種還只是一個想法,難的是想法的具體實現:如何在已有系統中同時兼容默認票種和自定義票種,實現系統升級的平滑遷移,這裡僅談幾個關鍵點:

  1. 默認票種(組合)有 N 種,默認票種ID 使用 1 ~ N 之間的數值分別表示;
  2. 自定義票種使用單獨的數據表進行存儲,票種ID使用起始於 N + 1 的自增整數表示,使用活動ID進行關聯,
  3. 載入活動時,使用活動ID關聯查詢活動自定義票種列表;如果票種列表不為空,判斷為自定義票種活動;否則,判斷為默認票種活動;
  4. 客戶端根據活動類型(自定義票種活動或默認票種活動)的不同渲染不同的頁面模組;
  5. 用戶報名時,活動訂單中的票種ID使用默認票種ID或自定義票種活動ID;
  6. 載入活動訂單時,如果訂單票種ID小於或等於 N,判斷為默認票種訂單;否則,判斷為自定義票種訂單,根據票種ID在自定義票種數據表中查詢票種資訊。

實現自定義票種之後,活動主辦方可以靈活設置活動票種和價格,更好地為用戶提供服務;用戶可以根據自己的情況選擇合適的票種,支付相應的費用;平台可以按票種統計每個活動的報名資訊,一舉三得

結語

看著簡單的問題可能是因為我們想的不夠複雜,看著複雜的方案一定是因為我們想的不夠簡單,Keep Simple Thing Simple。