Version: Next

設計

業務分析#

業務邊界#

該系統僅包含車票的餘票管理部分。即查詢剩餘座位,下單買票減座。

而生成訂單資訊,付款,流量控制,請求風控等等都不包含在本次討論的範圍中。

業務用例#

  • 查詢余票,能夠查詢兩個車站間可用的車次以及剩餘座位數量。
  • 查詢車次對應的車票餘票,能夠查詢給定的車次,在各個車站之間還有多少剩餘座位。
  • 支援選座下單,客戶能夠選擇給定的車次及座位,並下單買票。

實現難點分析#

餘票管理#

火車票餘票管理的難點,其實就在於其餘票庫存的特殊性。

普通的電商商品,以 SKU 為最小單位,每個 SKU 之間相互獨立,互不影響。

火車票餘票,卻有所不同,因為余票會受到已賣票起終點而受到影響。下面結合一個簡單的邏輯模型,來詳細的瞭解一下這種特殊性。

現在,我們假設存在一個車次,分別經過 a,b,c,d 四個網站,同時,我們簡化場景,假設車次中只有一個座位。

那麼在沒有任何人購票之前,這個車次的餘票情況就如下所示:

起終點餘票量
a,b1
a,c1
a,d1
b,c1
b,d1
c,d1

如果現在有一位客戶購買了一張 a,c 的車票。那麼由於只有一個座位,所以除了 c,d 之外的餘票也就都沒有。餘票情況就變成了如下所示:

起終點餘票量
a,b0
a,c0
a,d0
b,c0
b,d0
c,d1

更直白一點,如果有一位客戶購買了全程車票 a,d,那麼所有的餘票都將全部變為 0。因為這個座位上始終都坐著這位乘客。

這也就是火車票的特殊性:同一個車次的同一個座位,其各個起終點的餘票數量,會受到已售出的車票的起終點的影響。

延伸一點,很容易得出,同一車次的不同座位之間是沒有這種影響的。

餘票查詢#

正如上一節所述,由於余票庫存的特殊性。對於同一個車次 a,b,c,d,其可能的購票選擇就有 6 種。

並且我們很容易就得出,選擇的種類數的計算方法實際上就是在 n 個網站中選取 2 個的組合數,即 c(n,2) 。

那麼如果有一輛經過 34 個網站的車次,其可能的組合就是 c(34,2) = 561 。

如何高效應對可能存在的多種查詢也是該系統需要解決的問題。

Claptrap 主體設計#

Train Ticketing System Design

將同一車次上的每個座位都設計為一個 Claptrap - SeatGrain#

該 Claptrap 的 State 包含有一個基本資訊

類型名稱说明
IList<int>Stations途徑車站的 id 清單,開頭為始發站,結尾為終點站。主要購票時進行驗證。
Dictionary<int, int>StationDic途徑車站 id 的索引反向字典。Stations 是 index-id 的清單,而該字典是對應的 id-index 的字典,為了加快查詢。
List<string>RequestIds關鍵屬性。每個區間上,已購票的購票 id。例如,index 為 0 ,即表示車站 0 到車站 1 的購票 id。如果為空則表示暫無認購票。

有了這數據結構的設計,那麼就可以來實現兩個業務了。

驗證是否可以購買#

透過傳入兩個車站 id,可以查詢到這個作為是否屬於這個 SeatGrain 。並且查詢到起終點對應的所有區間段。只要判斷這個從 RequestIds 中判斷是否所有的區間段都沒有購票 Id 即可。若都沒有,則說明可以購買。如果有任何一段上已有購票 Id,則說明已經無法購買了。

舉例來說,當前 Stations 的情況是 10,11,12,13. 而 RequestIds 是 0,1,0。

那麼,如果要購買 10->12 的車票,則不行,因為 RequestIds 第二個區間已經被購買。

但是,如果要購買 10->11 的車票,則可以,因為 RequestIds 第一個區間還無人購買。

購買#

將起終點對應在 RequestIds 中所有的區間段設置上購票 Id 即可。

將同一車次上的所有座位的餘票情況設計為一個 Claptrap - TrainGran#

該 Claptrap 的 State 包含有一些基本資訊

類型名稱说明
IReadOnlyList<int>Stations途徑車站的 id 清單,開頭為始發站,結尾為終點站。主查詢時進行驗證。
IDictionary<StationTuple, int>SeatCount關鍵屬性。StationTuple 表示一個起終點。集合包含了所有可能的起終點的餘票情況。例如,根據上文,如果該車次經過 34 個地點,則該字典包含有 561 個鍵值對

基於以上的數據結構,只需要在每次 SeatGrain 完成下單後,將對應的資訊同步到該 Grain 即可。

例如,假如 a,c 發生了一次購票,則將 a,c / a,b / b,c 的餘票都減一即可。

這裡可以藉助本框架內置的 Minion 機制來實現。

值得一提的是,這是一個比"最小競爭資源"大的設計。因為查詢場景在該業務場景中不需要絕對的快速。這樣設計可以減少系統的複雜度。

Id#

Train Ticketing System Id