該如何設(shè)計(jì)一個(gè)購(gòu)物車(chē)系統(tǒng)呢?
隨著電子商務(wù)的崛起,網(wǎng)站的購(gòu)物車(chē)系統(tǒng)已經(jīng)成了普遍的功能,近幾年Rails快速竄紅,但是很多開(kāi)發(fā)者對(duì)于設(shè)計(jì)真正可用的購(gòu)物車(chē)系統(tǒng)仍然感到有很多的困難。
購(gòu)物車(chē)系統(tǒng)第一步
如何設(shè)計(jì)一個(gè)購(gòu)物車(chē)系統(tǒng)呢?在考慮這個(gè)問(wèn)題之前不妨先想想從挑選產(chǎn)品放入購(gòu)物車(chē),到最后結(jié)賬的環(huán)節(jié)中,可以切出哪些功能點(diǎn)方便規(guī)劃數(shù)據(jù)庫(kù),我們隨機(jī)可以察覺(jué)到有商品和購(gòu)物車(chē),而以下就是一個(gè)初步的案例分析圖:

保留金額信息
研判一個(gè)購(gòu)物車(chē)可以有多個(gè)產(chǎn)品,且一個(gè)產(chǎn)品放進(jìn)多個(gè)購(gòu)物車(chē),而這樣的設(shè)計(jì)圖乍看完美卻有個(gè)問(wèn)題:試想當(dāng)一個(gè)訪客挑選了一樣產(chǎn)品,并且放入購(gòu)物車(chē),這時(shí)如果該產(chǎn)品的價(jià)錢(qián)被后臺(tái)管理員異動(dòng),那么在訪客結(jié)賬之前購(gòu)物車(chē)中的商品價(jià)錢(qián)也需要異動(dòng)嗎?
如果這是被允許的,那么筆者可以在訪客結(jié)賬的前一秒,將價(jià)錢(qián)偷改為幾百萬(wàn)(之后幾年就是官司人生),于是我們需要新的設(shè)計(jì)避免這樣的悲劇:

這樣就好了許多,還需額外儲(chǔ)存數(shù)量與金額信息,這個(gè)金額必須是該產(chǎn)品加入購(gòu)物車(chē)當(dāng)下的數(shù)字,如此結(jié)賬時(shí)才不會(huì)有價(jià)錢(qián)異動(dòng)的爭(zhēng)議。
「訂單」與「購(gòu)物車(chē)」的差異?
「訂單」與「購(gòu)物車(chē)」在電子商務(wù)系統(tǒng)中扮演的角色相似卻也有些差異,相似是兩者都同樣會(huì)記錄訪客加入購(gòu)物車(chē)的商品的當(dāng)前價(jià)錢(qián)與數(shù)量,差異則是「購(gòu)物車(chē)」可編輯商品信息(例如數(shù)量或新增商品),但「訂單」除了付款之外,不可再異動(dòng)商品信息,且「訂單」會(huì)擁有更多如地址、姓名、統(tǒng)編等一類的通信數(shù)據(jù)。
加上「訂單」后的設(shè)計(jì)圖:

付款紀(jì)錄
「訂單」不能變動(dòng),唯一可以執(zhí)行的動(dòng)作就是「付款」。可是付款未必每次都會(huì)成功,且無(wú)論成功與否都應(yīng)該留個(gè)記錄,記錄除了方便管理員查詢、找出問(wèn)題之外,也能提供給使用者供參考與留存。
「交易紀(jì)錄」不需通信數(shù)據(jù),只需要記錄成功與否、交易編號(hào)即可,以下是新的設(shè)計(jì)圖:

你大概會(huì)注意到Trade有個(gè)params:text屬性,這是用來(lái)存放第三方支付服務(wù)所回傳的數(shù)據(jù),可能是JSON或是XML格式,搭配ActiveRecord中的#serialize,可以輕易存取這個(gè)字段以供日后的查閱由服務(wù)所回傳的原始交易數(shù)據(jù)。
其他人怎么做?
像購(gòu)物車(chē)系統(tǒng)如此常見(jiàn)的功能,想必也有許多開(kāi)源項(xiàng)目可以使用,其中最有名的三個(gè)項(xiàng)目分別是:
ror_ecommerce
Spree
Piggybak
ror_ecommerce
ror-ecommerce

是由David Henner在設(shè)計(jì)一個(gè)電子商務(wù)系統(tǒng)時(shí),順手開(kāi)源的項(xiàng)目,并且寫(xiě)了一個(gè)網(wǎng)站解釋他的設(shè)計(jì)哲學(xué),即便這個(gè)項(xiàng)目是三個(gè)之中更新最慢的,但對(duì)于想學(xué)習(xí)如何徒手制作電子商務(wù)網(wǎng)站的人來(lái)說(shuō),筆者認(rèn)為這是最值得參考的學(xué)習(xí)文件。
ror_ecommerce的數(shù)據(jù)庫(kù)設(shè)計(jì)與本篇文章的示意圖、以及「Agile Web Development with Rails 4」書(shū)中的示例相同。
Spree

Spree是Rails開(kāi)源電子商務(wù)圈最著名的項(xiàng)目,是由許多資深工程師組成的核心團(tuán)隊(duì)所開(kāi)發(fā),其中不乏Apache軟件協(xié)會(huì)的成員與公司的CTO、CEO,質(zhì)量保證,加上完整的API、文件以及活絡(luò)的社群,此外與ActiveMerchant兼容性高,對(duì)于單純只是想要快速架設(shè)電子商務(wù)網(wǎng)站的人來(lái)說(shuō),Spree堪稱首選。
數(shù)據(jù)結(jié)構(gòu)方面,有別于前述的構(gòu)架,Spree并沒(méi)有Cart entity,而是在Order之中新增一個(gè)state屬性將「訂單」偽裝成「購(gòu)物車(chē)」的行為,導(dǎo)致Order的行為邏輯比Cart復(fù)雜,但是可以實(shí)現(xiàn)的功能更多。
Piggybak

算是Rails開(kāi)源電子商務(wù)的后起之秀,沒(méi)有Spree那樣完備,但其模塊化的彈性使得許多開(kāi)發(fā)者也開(kāi)始投向Piggybak的懷抱,這層關(guān)系有點(diǎn)像Paperclip與CarrierWave之間一樣,后者也是以模塊化著稱。
你可以在Rails中任何一個(gè)ActiveRecord model中掛上acts_as_sellable使其成為可以買(mǎi)賣(mài)的商品,相當(dāng)簡(jiǎn)便,如果你想要的是在既有的網(wǎng)站上加上購(gòu)物功能,而非重新打造一個(gè)購(gòu)物網(wǎng)站,也許這是讓你選擇Piggybak而非Spree的誘因。
結(jié)語(yǔ)
購(gòu)物車(chē)系統(tǒng)沒(méi)有什么「必須」的設(shè)計(jì),不管是什么構(gòu)架的購(gòu)物車(chē),可以結(jié)賬的就是好的購(gòu)物車(chē)。如果對(duì)于設(shè)計(jì)購(gòu)物車(chē)仍有疑惑,也建議不妨多看看他人的開(kāi)源項(xiàng)目怎么設(shè)計(jì),其實(shí)許多書(shū)中找不到的答案,都放在代碼里面,而優(yōu)良的代碼更是可以閱讀的。
