約系統(tǒng):從數(shù)據(jù)庫設(shè)計到并發(fā)控制實戰(zhàn))
1. 為什么每年都有人做預(yù)約系統(tǒng)但這個題其實并不簡單Spring Boot配體育場館設(shè)施預(yù)約系統(tǒng)這個搭配我在畢業(yè)設(shè)計的選題名單里看了好幾年可以說年年有但絕大多數(shù)人第一反應(yīng)都是“不就是增刪改查嗎”。真到開題答辯、中期檢查、最后演示的時候同一個題能分出明顯差距有人只演示了頁面上點擊預(yù)約成功評委一問“兩個人同時約同一塊場地怎么辦”就卡殼有人卻能把并發(fā)控制、狀態(tài)機、超時釋放、支付冪等全部講清楚答辯變成加分現(xiàn)場。先說結(jié)論預(yù)約類系統(tǒng)非常適合做畢設(shè)因為復(fù)雜度剛好卡在“入門之上、工程之下”的位置。你說它難它沒有高深的算法你說它容易它又牽扯并發(fā)、狀態(tài)流轉(zhuǎn)、資源競爭和業(yè)務(wù)規(guī)則稍微處理不好就是臟數(shù)據(jù)。這篇文章我打算把做這類系統(tǒng)的完整思路寫一遍從選題定調(diào)、功能邊界、數(shù)據(jù)庫設(shè)計、訂單狀態(tài)機到真實開發(fā)中反復(fù)踩過的坑再到答辯如何講出含金量逐個拆開講透。正在準(zhǔn)備畢設(shè)的同學(xué)應(yīng)該知道預(yù)約系統(tǒng)的套路一旦吃透圖書館座位預(yù)約、會議室預(yù)約、健身房課程預(yù)約本質(zhì)都是同一套邏輯換個業(yè)務(wù)名詞就能復(fù)用。1.1 畢設(shè)選型的“性價比”判斷復(fù)雜度適中、就業(yè)導(dǎo)向明確為什么推薦這個題目三個原因。第一技術(shù)棧主流且成熟。Spring Boot是這兩年企業(yè)開發(fā)的事實標(biāo)準(zhǔn)配上MySQL、MyBatis或Spring Data JPA網(wǎng)上資料一抓一大把遇到問題搜得到答案不至于卡死在某個冷門技術(shù)上。第二業(yè)務(wù)模型清晰但又不失深度。場館、場地、時間片、訂單、支付、狀態(tài)流轉(zhuǎn)一層層拆下來既有常規(guī)CRUD也有真正需要動腦的并發(fā)與狀態(tài)設(shè)計工作量適合一個學(xué)期的周期。第三演示效果好。做出來的東西能看、能操作、能講用戶端加管理端界面和功能都直觀比某些純算法題更容易讓答辯老師快速理解你做了什么。但這里要提醒一句正因為題目經(jīng)典答辯老師也見多了這個題所以“能跑”只是及格線。如果你只做到了“用戶能預(yù)約、管理員能看訂單”那大概率就是及格分。拉開差距的點在于你有沒有想清楚資源沖突怎么防止、訂單狀態(tài)怎么流轉(zhuǎn)、超時訂單怎么處理。這些內(nèi)容不是頁面堆砌出來的而是設(shè)計層面的東西必須在動手寫代碼之前就定下來。1.2 先把三個設(shè)計抉擇定下來后面才不會返工我做這個類型的系統(tǒng)有一個強烈的體會返工九成不是代碼寫錯而是業(yè)務(wù)模型沒定清楚就開始建表。下面三個問題建議在動手前先回答。第一個問題預(yù)約的粒度是什么按“場地”還是按“場次”場地維度指的是用戶選擇某塊場地比如羽毛球3號場的某個時間段進行預(yù)約適合散客自由約球場次維度指的是管理員預(yù)先排好某些固定時段比如19:00-20:00羽毛球小班課用戶搶的是這個場次的名額。體育場館實際業(yè)務(wù)中兩種都存在但畢設(shè)沒必要一上來就做混合模式。我建議以“場地時間段”作為核心模型時間片表作為連接場地和訂單的樞紐這樣最貼近真實場館運營也最容易把并發(fā)的點設(shè)計清楚。第二個問題時間片是固定排好的還是下單時動態(tài)計算有一個省事的做法是不建時間片表用戶下單時傳“開始時間結(jié)束時間”服務(wù)端去訂單表里掃一遍有沒有沖突。這種方案在小數(shù)據(jù)量下確實能跑但問題很明顯一是熱門時段用戶同時搶的時候沖突檢測會非常頻繁二是“判斷是否沖突”的邏輯散落在多個接口里很容易寫漏。我的建議是引入時間片表讓管理員按場館開放時間和場地類型批量生成未來N天的時間片用戶只能從空閑時間片里選。這樣做的一個隱藏好處是時間沖突校驗被前置到了排期生成階段下單接口只需要關(guān)心“時間片是否空閑”復(fù)雜度一下就降下來了。第三個問題單場館還是多場館從表結(jié)構(gòu)設(shè)計角度我建議從一開始就支持“多場館—多場地”的層級關(guān)系。哪怕你演示的時候只放一個體育館的數(shù)據(jù)表結(jié)構(gòu)上也把場館表、場地表分開。這樣寫論文時可以說“系統(tǒng)支持多場館擴展”答辯時也能應(yīng)對“如果學(xué)校有好幾個館怎么處理”的追問。2. 功能邊界與業(yè)務(wù)規(guī)則先想清楚“預(yù)約什么”再動手功能列表看似簡單但很多同學(xué)列出十幾個模塊最后做不完就是因為沒有分清“核心鏈路”和“包裝功能”。我通常把功能分成用戶端、管理端和三條業(yè)務(wù)規(guī)則先定邊界再寫代碼。2.1 用戶端和管理端到底要做哪些事用戶端核心功能可以控制在六項左右注冊登錄、瀏覽場館和場地、按日期查詢可約時間片、提交預(yù)約訂單、模擬支付、取消訂單。再加一個歷史訂單列表用于查看預(yù)約狀態(tài)。至于收藏、評論、積分、會員等級這些如果時間和精力允許可以錦上添花但不建議在核心鏈路還沒跑通之前碰它們。管理端包括場館管理、場地管理、時間片排期管理、訂單管理、用戶管理。時間片排期是管理端最重要也最容易忽略的功能它決定了系統(tǒng)能不能真正運轉(zhuǎn)起來。如果時間片都是手動一條條插入數(shù)據(jù)庫演示時既難看又容易出錯所以一定要做一個“按日期范圍場地批量生成時間片”的功能這是管理端的一個小亮點也是后面很多設(shè)計的基礎(chǔ)。2.2 三條業(yè)務(wù)規(guī)則提前量、訂單數(shù)上限、最晚取消時間功能定了之后要寫明白的是業(yè)務(wù)規(guī)則這些規(guī)則會直接影響接口校驗邏輯。我見過不少同學(xué)建完表直接寫接口寫到一半發(fā)現(xiàn)“這個狀態(tài)什么時候能取消”“一個人能不能同時占幾塊場地”這些問題根本沒定義代碼越寫越別扭。建議先定三條規(guī)則。第一可提前預(yù)約的天數(shù)。比如只能預(yù)約未來7天內(nèi)的場地避免排期過遠導(dǎo)致運營變動后數(shù)據(jù)對不上。第二單個用戶同時有效的預(yù)約單數(shù)量。比如最多同時有3個“待支付已支付”狀態(tài)下的訂單防止有人把黃金時段全部占住。這個規(guī)則看起來簡單但它是防止惡意占位的第一道防線。第三最晚取消時間。比如開球時間前2小時可以免費取消超過這個時間就不能在用戶端取消了避免場地資源被臨時放鴿子。這三條規(guī)則在寫代碼前用文字列出來數(shù)據(jù)庫校驗、接口邏輯、前端提示都有了依據(jù)。還有一條容易被忽略的邊界場館的開放時間。比如體育館營業(yè)時間是8:00到22:00那時間片生成、訂單校驗、前端可選項都必須遵循這個范圍否則就會出現(xiàn)凌晨三點有人預(yù)約羽毛球場的詭異數(shù)據(jù)。3. 數(shù)據(jù)庫表結(jié)構(gòu)七張核心表和一條并發(fā)“鐵律”數(shù)據(jù)庫設(shè)計是這種系統(tǒng)最關(guān)鍵的部分沒有之一。因為預(yù)約系統(tǒng)的根問題是“數(shù)據(jù)別打架”而數(shù)據(jù)打架的問題最好在設(shè)計表的時候就徹底封死而不是靠代碼小心翼翼地繞。3.1 七張核心表的職責(zé)劃分拋開擴展表不談核心表我建議按下面這個清單設(shè)計每張表解決一類問題。表名核心職責(zé)關(guān)鍵字段說明user用戶與管理員身份username唯一、password_hash、role(USER/ADMIN)、phonevenue場館name、location、open_time、close_time、statuscourt場地掛在場館下venue_id、name、type(羽毛球/籃球/游泳等)、base_price、statusschedule時間片連接場地與訂單court_id、date、start_time、end_time、price、status(OPEN/BOOKED/LOCKED)booking_order預(yù)約訂單order_no唯一、user_id、schedule_id、court_id、date、start_time、end_time、total_price、statuspayment_record支付流水order_id、pay_no、amount、status、paid_atadmin_config業(yè)務(wù)規(guī)則配置rule_key、rule_value用于存可預(yù)約天數(shù)、訂單上限等這些表之間的關(guān)系并不復(fù)雜場館1對多場地場地1對多時間片時間片1對1訂單訂單1對1支付流水。真正需要用心設(shè)計的不是關(guān)系而是字段層面的約束。3.2 并發(fā)鐵律把“同一時間片只可被預(yù)約一次”壓進數(shù)據(jù)庫這是整篇內(nèi)容里我認為最重要的一段。做預(yù)約系統(tǒng)最核心的技術(shù)問題就是同一塊場地、同一個時間段絕不能出現(xiàn)兩個有效訂單。很多同學(xué)的做法是下單時先查詢一下這個時間段有沒有訂單沒有就插入。這在單用戶操作時沒有任何問題但一遇到并發(fā)就露餡兩個請求同時查到“空閑”然后同時插入訂單超賣就發(fā)生了。正確的做法是在數(shù)據(jù)庫層面把它“焊死”。具體有兩道防線。第一道防線是唯一索引。schedule表上應(yīng)該加上(court_id, date, start_time)的唯一約束意思是同一塊場地同一天同一個開始時間只能存在一個時間片記錄。這是從源頭上保證時間片本身不重復(fù)。第二道防線是下單時的原子占位。用一條帶狀態(tài)條件的UPDATE語句搶占時間片UPDATE schedule SET status BOOKED WHERE id #{scheduleId} AND status OPEN;如果影響行數(shù)為1說明這塊場地被你占住了可以繼續(xù)創(chuàng)建訂單如果影響行數(shù)為0說明別人已經(jīng)搶先一步直接提示“該時段已被預(yù)約”。這個方案依賴數(shù)據(jù)庫行鎖避免了先查詢再插入的時間窗口是最直接也最好向評委解釋的方案。如果你用的是JPA等ORM框架也可以走樂觀鎖路線在schedule表上加一個version字段更新時帶上版本號條件更新失敗就重試或提示。兩種方案選一種即可但建議在論文中把兩種都寫清楚這本身就是很好的答辯素材。3.3 字段細節(jié)與三個隱藏約定除了核心字段還有幾個必須注意的細節(jié)。第一訂單表要冗余場地名、場館名、時間段、價格等信息而不是只存外鍵。為什么因為如果管理員后續(xù)調(diào)整了場地名稱或者價格歷史訂單不能被影響而且列表頁展示時不需要反復(fù)聯(lián)表查詢。第二業(yè)務(wù)訂單號order_no要認真生成比如用日期加隨機序列并加上唯一索引。它不只是為了好看也是后續(xù)模擬支付、對賬時的標(biāo)識。第三所有時間字段建議統(tǒng)一用datetime類型Java側(cè)用LocalDateTime避免各種時區(qū)換算帶來的詭異問題這一點后面第五部分會詳細展開。還有一個容易被忽視的點schedule表的price雖然是冗余字段但它的值應(yīng)該從court表的base_price復(fù)制過來而不是下單時臨時算。因為場館調(diào)價后已經(jīng)生成的時間片價格不應(yīng)該跟著飄。這種“快照”思想在訂單系統(tǒng)里很常見寫論文時值得單獨講一講。4. 核心流程與六個狀態(tài)一個預(yù)約訂單的一生下單不是簡單insert一條記錄而是一系列狀態(tài)流轉(zhuǎn)。我建議把訂單的生命周期單獨作為一個設(shè)計重點因為在答辯中狀態(tài)機是比CRUD高級得多的表達。4.1 從查詢到入場的五個步驟一次完整預(yù)約流程可以分為五步查詢可約時間片、創(chuàng)建預(yù)約訂單、支付、核銷入場、完成。查詢階段只展示狀態(tài)為OPEN的時間片創(chuàng)建訂單時通過上面說的原子占位把schedule改成BOOKED同時生成狀態(tài)為“待支付”的訂單支付成功后把訂單改為“已支付”用戶到場后由管理員核銷或掃碼確認訂單變?yōu)椤耙淹瓿伞?。這里有一個設(shè)計上的關(guān)鍵點創(chuàng)建訂單和鎖定時間片必須在同一個事務(wù)里完成否則會出現(xiàn)訂單創(chuàng)建失敗但時間片被鎖住的情況。實現(xiàn)上可以這樣組織先執(zhí)行UPDATE占位再insert訂單如果訂單插入失敗則拋異?;貪L讓UPDATE也回滾時間片自然恢復(fù)為OPEN。4.2 訂單狀態(tài)機六個狀態(tài)怎么流轉(zhuǎn)我建議訂單表用整數(shù)來表示狀態(tài)配合一張狀態(tài)說明表方便查詢和展示。六個狀態(tài)如下狀態(tài)值狀態(tài)名含義可流轉(zhuǎn)去向0待支付已鎖定時間片但未付款已支付、已取消、超時關(guān)閉1已支付用戶已付款等待入場已完成、已取消2已完成已被核銷行程結(jié)束終態(tài)3已取消用戶主動取消終態(tài)4超時關(guān)閉系統(tǒng)自動關(guān)閉未支付訂單終態(tài)5已退款已支付訂單取消后退款終態(tài)狀態(tài)機的核心原則是任何狀態(tài)之間的跳轉(zhuǎn)都必須有明確的觸發(fā)條件和操作入口不能讓一條訂單同時處于兩個狀態(tài)。比如用戶取消時只有“待支付”和“已支付”狀態(tài)能走取消流程取消成功后如果之前已支付就再進入“已退款”。超時關(guān)閉只針對“待支付”狀態(tài)已支付的訂單不能被定時任務(wù)直接關(guān)閉否則會引發(fā)支付和訂單狀態(tài)不一致。4.3 三個核心接口與超時釋放預(yù)約系統(tǒng)的接口不必很多但三個核心接口的邏輯要足夠健壯。創(chuàng)建預(yù)約接口的核心是原子占位加事務(wù)支付接口的核心是冪等無論用戶點擊多少次支付按鈕或者模擬支付回調(diào)重復(fù)通知都不能產(chǎn)生多筆有效流水取消接口的核心是釋放資源不管訂單狀態(tài)改成了什么最終必須把對應(yīng)schedule的狀態(tài)改回OPEN讓其他用戶能繼續(xù)約。還有一個業(yè)務(wù)上很現(xiàn)實的問題用戶下單后不付款場地被一直鎖著怎么辦。我在實際設(shè)計里用了“定時任務(wù)掃描用戶主動取消”雙通道。定時任務(wù)每隔幾分鐘掃一次把超過15分鐘仍未支付的待支付訂單改成超時關(guān)閉并釋放對應(yīng)時間片。同時在用戶端允許用戶主動取消待支付訂單取消時同樣釋放時間片。兩個通道都做是因為任何單一機制都可能因為用戶忘了關(guān)頁面而導(dǎo)致座位被無效占用。5. 我在開發(fā)中反復(fù)踩過的六個坑這一部分是我最想寫的。網(wǎng)上教程只會告訴你“應(yīng)該怎么做”但實際開發(fā)中真正讓人崩潰的往往是那些看起來不起眼、卻能把整個邏輯帶偏的細節(jié)。下面六個問題都是我在做類似系統(tǒng)時真實遇到過、也幫人排查過的。5.1 并發(fā)下單時場地“超賣”了前面提到了countinsert方式的隱患這里用更實際的場景描述一遍。我最初做第一版時創(chuàng)建預(yù)約的代碼是先查schedule狀態(tài)再判斷是否為空然后插入訂單。用Postman模擬兩個請求同時打同一個時間片接口時兩個請求都打印了“當(dāng)前狀態(tài)為空閑”然后都成功創(chuàng)建了訂單。那一刻我才意識到應(yīng)用層的“先查后寫”永遠存在競態(tài)窗口解決問題必須靠數(shù)據(jù)庫的原子操作。后來改成UPDATE ... WHERE statusOPEN之后再并發(fā)測試就穩(wěn)定了。這里給一個排查建議凡是涉及資源搶占的邏輯不要相信“我在代碼里加了if判斷”直接用數(shù)據(jù)庫的原子更新或唯一的數(shù)據(jù)庫約束驗證然后用并發(fā)請求實測別只靠點頁面測。5.2 內(nèi)部自調(diào)用讓Transactional形同虛設(shè)這是一個很經(jīng)典的Spring Boot陷阱。我在取消訂單時寫了這樣一個結(jié)構(gòu)訂單Service里有一個cancel方法內(nèi)部調(diào)用了同類里的releaseSchedule方法而releaseSchedule上加了Transactional注解。測試時發(fā)現(xiàn)如果cancel方法本身在事務(wù)里調(diào)用releaseSchedule的事務(wù)還能沾點光但當(dāng)我單獨調(diào)用releaseSchedule時事務(wù)根本不生效。原因很簡單同類內(nèi)部方法調(diào)用不會經(jīng)過Spring的代理對象注解自然就不起作用。解決辦法也很直接把釋放場地的邏輯放到另一個Service比如ScheduleService里讓訂單Service調(diào)用ScheduleService的方法。這樣事務(wù)邊界既清晰又有效。這個坑在寫論文時其實可以當(dāng)作一個“踩坑記錄”寫進去反而顯得真實。5.3 時間重疊判斷的邊界條件如果哪天你決定不按時間片結(jié)構(gòu)做而是下單時動態(tài)校驗時間段那么“判斷時間段是否重疊”就是一個躲不開的邏輯。很多人在這里栽過跟頭。正確判斷兩組時間區(qū)間[aStart, aEnd)和[bStart, bEnd)是否重疊的條件是aStart bEnd bStart aEnd。用Java代碼寫就是public boolean isOverlap(LocalDateTime aStart, LocalDateTime aEnd, LocalDateTime bStart, LocalDateTime bEnd) { return aStart.isBefore(bEnd) bStart.isBefore(aEnd); }重點在于使用半開區(qū)間也就是開始時間包含、結(jié)束時間不包含。這樣14:00-16:00和16:00-18:00兩個相鄰時間片會返回false不會誤判成沖突。很多同學(xué)用閉區(qū)間思維把條件寫成aStart bEnd bStart aEnd結(jié)果兩個緊挨著的預(yù)約被當(dāng)成沖突調(diào)試半天不知道原因。5.4 待支付訂單把黃金時段“焊死”這個問題我在聯(lián)調(diào)時才意識到有多惡心。當(dāng)時為了測試我連續(xù)創(chuàng)建了好幾個待支付訂單把晚高峰的幾個時間片全鎖住了然后關(guān)閉頁面不再操作這些時間片就永久處于BOOKED狀態(tài)其他用戶根本約不了。如果不做超時釋放整個場館的可約資源會慢慢被垃圾訂單占滿。解決方案就是前面說的定時任務(wù)加主動取消雙通道。實現(xiàn)上我有一個心得定時任務(wù)不要掃描所有訂單而是只查“狀態(tài)為待支付且創(chuàng)建時間早于當(dāng)前時間15分鐘”的訂單這樣即使訂單量大了也不會太慢。釋放時間片和關(guān)閉訂單這兩個操作同樣要在事務(wù)里完成。5.5 懶加載異常在聯(lián)調(diào)時才暴露如果用Spring Data JPA做關(guān)聯(lián)查詢訂單表里的court、venue這些關(guān)聯(lián)對象默認是懶加載的。在Service層開啟事務(wù)時沒問題一旦把實體直接返回給ControllerJackson序列化時訪問關(guān)聯(lián)屬性就會拋LazyInitializationException。這個問題屬于典型的“單測測不出來、聯(lián)調(diào)必現(xiàn)”的坑。解決方式有幾個我個人推薦在Service層就把實體轉(zhuǎn)換成DTO或VO不要在Controller里直接返回實體。這樣既避免懶加載問題也防止實體內(nèi)部結(jié)構(gòu)暴露給前端還方便在DTO里組裝展示字段。養(yǎng)成這個習(xí)慣之后答辯時也能順帶聊聊分層設(shè)計比單純講CRUD強得多。5.6 前后相差8小時的詭異預(yù)約時間這是一個看著玄學(xué)、查了半小時才發(fā)現(xiàn)的坑。用戶在頁面上選的18:00存進數(shù)據(jù)庫變成了10:00或者反過來查詢時全部偏了8小時。問題根源在時區(qū)MySQL連接串沒有指定serverTimezoneJVM時區(qū)和數(shù)據(jù)庫時區(qū)不一致Timestamp類型在轉(zhuǎn)換時被自動調(diào)整了。我的解決方式是統(tǒng)一三處約定數(shù)據(jù)庫連接串顯式帶上serverTimezoneAsia/ShanghaiJava側(cè)全部使用LocalDateTime而不是Date數(shù)據(jù)庫表字段統(tǒng)一用datetime而不是timestamp。這里不建議用timestamp是因為它內(nèi)部轉(zhuǎn)換邏輯太容易受時區(qū)影響對預(yù)約系統(tǒng)這種需要精確時間段的場景不太友好。6. 答辯怎么講從“系統(tǒng)”到“并發(fā)場景下的資源預(yù)定引擎”最后這部分很實在因為代碼寫得再好答辯講不出來也白搭。我見過不少同學(xué)功能都做完了但匯報時只會打開頁面一個個點評委看到的只是“這個按鈕能跳轉(zhuǎn)”自然覺得是普通管理系統(tǒng)。你得換一種講法。6.1 答辯展示的三層敘事建議按三層來講。第一層講數(shù)據(jù)庫設(shè)計重點講為什么引入時間片表、為什么把沖突校驗前置到排期生成階段而不是下單時去掃訂單表。第二層講并發(fā)控制把“兩人同時約同一塊場地”這個問題拋出來然后展示唯一索引加原子占位更新的解決方案。第三層講訂單狀態(tài)機從待支付到超時關(guān)閉、已支付到已完成把六個狀態(tài)的流轉(zhuǎn)圖畫清楚同時說明超時釋放的雙通道設(shè)計。演示時也可以準(zhǔn)備兩個對比場景第一次演示正常下單第二次用并發(fā)工具模擬兩個請求同時搶同一個時間片展示只有一個能成功。這個演示比任何PPT文字都更有說服力。切記在論文里寫了Redis、消息隊列、分布式鎖這類技術(shù)點就一定要自己動手實現(xiàn)過否則評委追問一個細節(jié)就可能露餡。如果只在數(shù)據(jù)庫層面做了并發(fā)控制那就誠實地講數(shù)據(jù)庫方案這已經(jīng)很扎實了。6.2 七周開發(fā)節(jié)奏與優(yōu)先級建議我建議拿七周左右的完整周期來做這個題每周有明確產(chǎn)出避免前松后緊。大致節(jié)奏如下周次任務(wù)關(guān)鍵產(chǎn)出第1周需求分析 數(shù)據(jù)庫設(shè)計 頁面草圖文檔、ER圖、表結(jié)構(gòu)第2周Spring Boot工程搭建 用戶場館基礎(chǔ)CRUD可用工程、完成登錄第3周時間片生成 預(yù)約下單主鏈路從選時間到生成訂單第4周訂單狀態(tài)機 模擬支付 超時任務(wù)狀態(tài)流轉(zhuǎn)完整第5周管理端頁面 前端聯(lián)調(diào)前后端跑通第6周并發(fā)測試 邊界修復(fù)并發(fā)過測、修復(fù)八小時時區(qū)等問題第7周寫論文 準(zhǔn)備演示 模擬答辯管理端展示不花哨核心鏈路流暢優(yōu)先級上先打通“選時間片—下單—支付—核銷”這條主干道再做取消、超時、退款這些分支邏輯最后還有時間才做統(tǒng)計報表和頁面美化。很多同學(xué)一開始就折騰前端動畫、UI特效最后發(fā)現(xiàn)核心流程還沒走通這是最可惜的。最后說一點個人體會這種系統(tǒng)最怕的不是不會寫代碼而是拿到題目就悶頭建表寫Controller。我見過不少同學(xué)把所有表建完才開始想業(yè)務(wù)規(guī)則結(jié)果狀態(tài)沒定義清楚改了三遍代碼。如果你看完這篇還是不知道從哪里下手我的建議是先打開一個文檔把“用戶能做什么、管理員能做什么、訂單有幾個狀態(tài)、同一個時間片被搶怎么辦”這四個問題各寫一段話寫明白了再動手。抓住并發(fā)和狀態(tài)這兩條主線這套系統(tǒng)就會走得穩(wěn)答辯時也有話可講。