約系統(tǒng):從設(shè)計(jì)到答辯全解析)
做畢設(shè)選題這件事真心建議你在“預(yù)約類系統(tǒng)”上多看兩眼。原因很簡單這類題目難度適中、需求清晰、前后端分工明確做出來以后不僅容易演示答辯時(shí)也有東西可講。尤其是我接下來要拆的這個(gè)基于SpringBoot微信小程序的書院預(yù)約系統(tǒng)屬于典型的“一套代碼解決選題、文檔、講解、答辯”的項(xiàng)目在每年畢業(yè)季的需求量一直很穩(wěn)定。這篇內(nèi)容我會從選題邏輯、技術(shù)選型、功能拆解、數(shù)據(jù)庫設(shè)計(jì)、核心代碼實(shí)現(xiàn)、部署流程、答辯避坑幾個(gè)維度把整個(gè)項(xiàng)目完整捋一遍。無論你是準(zhǔn)備直接拿這套源碼做底子還是想搞清楚預(yù)約系統(tǒng)到底該怎么寫這篇文章都值得耐心看完。1. 為什么預(yù)約類系統(tǒng)在畢設(shè)里是“安全牌”每年帶學(xué)生做畢設(shè)我最怕聽到的選題是“做一個(gè)智能推薦系統(tǒng)”或者“做一個(gè)基于深度學(xué)習(xí)的某平臺”。不是說這類題目做不出來而是對大多數(shù)本科生來說數(shù)據(jù)從哪來、效果怎么評估、工作量怎么量化這些問題很容易在答辯現(xiàn)場把自己繞進(jìn)去。預(yù)約系統(tǒng)則完全是另一個(gè)畫風(fēng)需求明確、流程清晰、每個(gè)模塊都有實(shí)打?qū)嵉臉I(yè)務(wù)邏輯評審老師一聽就知道你做了什么。書院預(yù)約系統(tǒng)的業(yè)務(wù)場景本身就挺有說服力。高校的書院制改革推行以來自習(xí)室、研討室、活動室、談話間這些物理空間都需要精細(xì)化管理。傳統(tǒng)的人工登記方式問題很多管理員沒法實(shí)時(shí)知道哪些時(shí)段被占了學(xué)生想預(yù)約也不知道找誰沖突全靠嘴說統(tǒng)計(jì)報(bào)表更是別想。用一個(gè)線上預(yù)約系統(tǒng)把“空間資源”數(shù)字化是真實(shí)存在且符合管理訴求的場景這比很多憑空想出來的業(yè)務(wù)需求好講得多。從工作量上看這個(gè)題目也踩得很準(zhǔn)。SpringBoot后端負(fù)責(zé)核心業(yè)務(wù)邏輯和數(shù)據(jù)處理小程序端負(fù)責(zé)用戶交互和預(yù)約操作中間通過HTTP接口通信。整個(gè)項(xiàng)目可以拆成用戶模塊、場地模塊、預(yù)約模塊、審批模塊、統(tǒng)計(jì)模塊無論是畫功能結(jié)構(gòu)圖、寫用例圖還是設(shè)計(jì)數(shù)據(jù)庫表每一塊都能落筆不會出現(xiàn)“寫了三千字文檔發(fā)現(xiàn)沒什么好寫”的尷尬。另外要提醒一點(diǎn)預(yù)約系統(tǒng)雖然看起來簡單但“預(yù)約”這兩個(gè)字背后藏著一個(gè)完整的狀態(tài)流轉(zhuǎn)邏輯。待審核、已通過、已取消、已完成、違約這些狀態(tài)之間的切換規(guī)則恰恰是答辯時(shí)評審老師最愛追問的點(diǎn)。能把狀態(tài)機(jī)講清楚基本就贏了一半。2. SpringBoot加小程序這套組合好在哪技術(shù)棧選型這件事很多學(xué)生一開始就想偏了。有人覺得前端用Vuewabpack顯得高級有人覺得后端非要微服務(wù)才夠分量。但在畢設(shè)這個(gè)場景下最合理的策略永遠(yuǎn)是“主流、穩(wěn)、資料多、自己真能跑通”。SpringBoot微信小程序恰好全部滿足。2.1 SpringBoot 2.x仍然是畢設(shè)主力雖然SpringBoot 3.x已經(jīng)發(fā)布但我給學(xué)生的建議一直是如果項(xiàng)目是從零開始用SpringBoot 2.7.x足夠不要盲目追新。原因很簡單2.7.x的教程、依賴版本對應(yīng)關(guān)系、以及網(wǎng)上能搜到的踩坑記錄實(shí)在太多了遇到問題五分鐘就能找到答案而3.x從JDK要求到Jakarta命名空間遷移任何一個(gè)小坑都可能卡住你半天。后端的技術(shù)組合可以直接照抄這份穩(wěn)定性和開發(fā)效率都能兼顧SpringBoot 2.7.x作為核心框架MyBatis Plus作為ORM層單表CRUD幾乎不用寫SQL連條件構(gòu)造器都能直接生成MySQL 8.0作為數(shù)據(jù)庫5.7也可以用兩者在這套系統(tǒng)里差異不大Lombok幫忙省掉getter/setter樣板代碼Hutool工具庫處理日期、字符串、隨機(jī)數(shù)這些小需求這套組合最大的優(yōu)點(diǎn)是什么是你寫業(yè)務(wù)代碼時(shí)真的在寫業(yè)務(wù)而不是在配置框架。對畢設(shè)而言時(shí)間就是分?jǐn)?shù)能省則省。2.2 微信小程序不需要解決安裝問題小程序端的原生開發(fā)是我反復(fù)推薦的另一個(gè)原因。很多同學(xué)糾結(jié)要不要用uni-app或者Taro來做一套代碼多端復(fù)用我的意見是沒必要。畢設(shè)評審看的是功能完整度和邏輯清晰度不是你的工程化能力。原生微信小程序語法結(jié)構(gòu)直觀頁面由四個(gè)文件組成js、json、wxml、wxss學(xué)起來門檻低改起來也方便。用原生小程序還有一層實(shí)際好處演示的時(shí)候老師很可能會掏出手機(jī)說“你掃碼我看看”。微信小程序天然的免安裝特性讓你在答辯現(xiàn)場不必折騰真機(jī)調(diào)試和APK安裝掃碼即用體驗(yàn)完成度立刻拉滿。2.3 前后端分離的分工邏輯這個(gè)項(xiàng)目的分工可以這樣理解小程序只負(fù)責(zé)“展示和收集”具體業(yè)務(wù)判斷全部交給后端接口。比如用戶提交預(yù)約申請小程序端只是把“場地ID、日期、時(shí)段、備注”打包成JSON發(fā)給后端至于這個(gè)時(shí)段還有沒有名額、該用戶有沒有違約記錄、能不能約全部由后端判斷并返回結(jié)果。這樣做的好處很多其中最關(guān)鍵的是答辯講架構(gòu)的時(shí)候你可以很清晰地說出“前后端通過RESTful API進(jìn)行數(shù)據(jù)交互前端負(fù)責(zé)視圖呈現(xiàn)后端負(fù)責(zé)業(yè)務(wù)邏輯與數(shù)據(jù)持久化”這就是分層架構(gòu)最標(biāo)準(zhǔn)的表達(dá)。后端接口設(shè)計(jì)盡量遵循REST風(fēng)格。比如GET /api/room/list 獲取場地列表GET /api/room/{id} 獲取場地詳情POST /api/reservation 提交預(yù)約PUT /api/reservation/{id}/cancel 取消預(yù)約GET /api/reservation/my 獲取我的預(yù)約列表GET /api/admin/reservation/page 管理端分頁查詢預(yù)約這樣設(shè)計(jì)的好處是接口數(shù)量不多語義清晰寫接口文檔也方便。小程序端只需要統(tǒng)一封裝一個(gè)request方法的工具類設(shè)置好baseUrl和token所有請求都能復(fù)用。3. 功能拆解書院預(yù)約系統(tǒng)到底做了什么事很多同學(xué)拿到一套源碼第一反應(yīng)是“把代碼跑起來”然后就開始一個(gè)頁面一個(gè)頁面點(diǎn)。這個(gè)習(xí)慣得改。拿到任何項(xiàng)目第一件事是把功能清單理清楚。功能清單不僅是開發(fā)的地圖也是寫文檔時(shí)候的大綱。這套書院預(yù)約系統(tǒng)從角色上劃分涉及三類用戶學(xué)生用戶、書院管理員、系統(tǒng)超級管理員。3.1 用戶端功能小程序里的那部分小程序端是學(xué)生用得最多的入口功能設(shè)計(jì)要圍繞“快速完成預(yù)約”這個(gè)核心目標(biāo)。微信授權(quán)登錄是第一道門檻。用戶在進(jìn)入小程序后前端通過wx.login拿到code傳給后端后端再調(diào)用微信的code2Session接口換取openid第一次登錄時(shí)自動為用戶創(chuàng)建賬號后續(xù)登錄直接識別身份。整個(gè)流程用戶是無感的不需要手動輸入用戶名密碼。這個(gè)設(shè)計(jì)在體驗(yàn)上很加分也是答辯的時(shí)候可以重點(diǎn)講的技術(shù)細(xì)節(jié)。場地瀏覽與檢索是第二個(gè)模塊。書院里通常有多個(gè)場地比如研討室、自習(xí)區(qū)、多功能廳每個(gè)場地都有名稱、位置、可容納人數(shù)、開放時(shí)間、設(shè)備信息、場地照片和狀態(tài)。小程序首頁默認(rèn)展示所有可用場地用戶可以按照場地類型篩選也可以直接搜索場地名稱。列表頁的每個(gè)卡片展示場地縮略圖、名稱、可預(yù)約時(shí)段點(diǎn)進(jìn)詳情頁可以看到更完整的場地信息和設(shè)施說明。預(yù)約操作是整條業(yè)務(wù)鏈的核心。用戶選定場地后選擇日期、選擇時(shí)段填寫預(yù)約用途比如“小組討論”“自習(xí)”提交后生成一條待審核預(yù)約單。這里有一個(gè)功能設(shè)計(jì)的細(xì)節(jié)用戶選擇日期時(shí)小程序端的日期選擇器要限制只能在“今天到今天之后的7天內(nèi)”選擇超過范圍的日期置灰。這個(gè)限制不是拍腦袋想的它對應(yīng)后臺的預(yù)約提前期配置也避免用戶提前太久占住資源。我的預(yù)約列表承擔(dān)了后續(xù)所有狀態(tài)管理。用戶可以查看自己所有預(yù)約記錄每條記錄都帶有一個(gè)狀態(tài)標(biāo)簽待審核、已預(yù)約、已取消、已完成、違約。在“待審核”和“已預(yù)約”狀態(tài)下用戶可以主動取消預(yù)約取消后的名額會釋放回可用名額池里。個(gè)人中心則展示用戶的基礎(chǔ)信息、所屬書院、累計(jì)預(yù)約次數(shù)、違約次數(shù)以及綁定學(xué)號信息。如果用戶存在未處理違約個(gè)人中心會彈出紅色提示條這也是答辯時(shí)體現(xiàn)系統(tǒng)“管理閉環(huán)”的典型設(shè)計(jì)。3.2 管理端功能誰來審批誰來管數(shù)據(jù)管理端是運(yùn)行在瀏覽器里的后臺頁面用戶角色是書院管理員。這個(gè)角色承擔(dān)的事情比想象中多。預(yù)約審核是管理員最高頻的操作之一。學(xué)生提交預(yù)約申請后管理員在待審核列表里查看預(yù)約人信息、預(yù)約場地、時(shí)間、用途依據(jù)實(shí)際情況選擇通過或駁回。駁回時(shí)必須填寫駁回原因這個(gè)原因會推送到小程序端展示給用戶讓用戶知道為什么被拒絕而不是簡單粗暴地改狀態(tài)。場地管理是管理員配置“有什么資源可以約”的入口。管理員可以新增場地設(shè)置場地名稱、所屬位置、容納人數(shù)、設(shè)備標(biāo)簽、開放時(shí)間范圍上傳場地圖片設(shè)置該場地默認(rèn)的花費(fèi)標(biāo)準(zhǔn)如果書院是收費(fèi)制的話。也可以對已有場地進(jìn)行上下架操作下架后小程序端不再展示該場地但歷史預(yù)約記錄全部保留。預(yù)約規(guī)則配置是一個(gè)容易被忽略但實(shí)際很關(guān)鍵的功能。比如管理員可以設(shè)置“一個(gè)學(xué)生同時(shí)最多持有多少個(gè)預(yù)約”“預(yù)約最晚提前多久取消”“距離預(yù)約開始前幾小時(shí)關(guān)閉簽到”這些參數(shù)。這些規(guī)則會影響代碼里的校驗(yàn)邏輯屬于典型的業(yè)務(wù)規(guī)則配置化設(shè)計(jì)在文檔里體現(xiàn)出“靈活配置”這一點(diǎn)很容易讓評審加分。數(shù)據(jù)統(tǒng)計(jì)模塊給管理員提供可視化的預(yù)約情況概覽。比如近7天預(yù)約總量、各場地預(yù)約熱度排名、違約趨勢、預(yù)約通過率。統(tǒng)計(jì)頁的數(shù)據(jù)接口一般返回聚合后的結(jié)果前端用柱狀圖或折線圖展示即可。這部分是“錦上添花”的功能但有了它整個(gè)系統(tǒng)的完整性立刻上了一個(gè)檔次。3.3 超管與系統(tǒng)基礎(chǔ)功能超級管理員和書院管理員的區(qū)別在于管理范圍和賬號權(quán)限。超管可以創(chuàng)建多個(gè)書院管理員賬號并給每個(gè)賬號綁定對應(yīng)的書院。也就是說這套系統(tǒng)天然支持多書院接入每個(gè)管理員只能看自己書院的數(shù)據(jù)。這個(gè)設(shè)計(jì)值得留意因?yàn)槎鄶?shù)學(xué)生做的預(yù)約系統(tǒng)都是單點(diǎn)管理能想到用“書院ID”做數(shù)據(jù)隔離說明你對多租戶概念有基本認(rèn)知。系統(tǒng)基礎(chǔ)功能還包括操作日志、字典管理、輪播圖配置。操作日志記錄關(guān)鍵操作的執(zhí)行人、時(shí)間、動作避免出了事找不著人。字典管理用來維護(hù)場地類型、預(yù)約狀態(tài)這類可擴(kuò)展的枚舉值避免每次改選項(xiàng)都要改代碼。輪播圖配置用于管理小程序首頁的橫幅圖片這一點(diǎn)看起來是個(gè)小功能但在實(shí)際答辯中經(jīng)常被老師指出來問“這個(gè)圖片是在哪里配置的”能答上來又能加一點(diǎn)印象分。4. 數(shù)據(jù)庫設(shè)計(jì)的幾個(gè)關(guān)鍵決策數(shù)據(jù)庫表結(jié)構(gòu)是整個(gè)項(xiàng)目的根基。預(yù)約系統(tǒng)雖然業(yè)務(wù)不算復(fù)雜但表與表之間的關(guān)聯(lián)關(guān)系、狀態(tài)字段的設(shè)計(jì)、并發(fā)場景下的約束都需要提前想清楚。我建議你拿到源碼先不看代碼而是打開數(shù)據(jù)庫的建表SQL把每張表看一遍字段逐個(gè)理解清楚再回過頭看代碼理解的效率會高很多。4.1 核心表結(jié)構(gòu)概覽這套系統(tǒng)里的表可以歸為四類用戶類、資源類、業(yè)務(wù)類、配置類。用戶類表主要是一張用戶主表核心字段包括openid、昵稱、頭像URL、學(xué)號、所屬書院ID、手機(jī)號、角色類型、狀態(tài)。角色字段區(qū)分學(xué)生和管理員也可以用獨(dú)立的角色表來設(shè)計(jì)但對于預(yù)約系統(tǒng)來說一個(gè)字段就夠了。資源類表是場地表包含場地名稱、所屬書院、位置描述、容納人數(shù)、設(shè)備標(biāo)簽、圖片URL、開放開始時(shí)間、開放結(jié)束時(shí)間、狀態(tài)。另外配一張場地分類表分類和場地是一對多的關(guān)系。業(yè)務(wù)類表最核心的是預(yù)約表。我列一下關(guān)鍵字段id主鍵user_id預(yù)約用戶IDroom_id被預(yù)約場地IDreserve_date預(yù)約日期time_slot_id時(shí)間段IDstatus狀態(tài)0待審核、1已通過、2已取消、3已完成、4違約purpose預(yù)約用途說明reject_reason駁回原因create_time、update_time創(chuàng)建和更新時(shí)間時(shí)間段表單獨(dú)拆出來是值得的。每個(gè)場地每天設(shè)置多個(gè)時(shí)段比如8:00-10:00、10:00-12:00、14:00-16:00、16:00-18:00時(shí)段表的結(jié)構(gòu)是場地ID、開始時(shí)間、結(jié)束時(shí)間、該時(shí)段可預(yù)約人數(shù)上限。為什么拆出來因?yàn)椴煌瑘龅氐臅r(shí)段可能不同如果硬編碼到代碼里管理端想調(diào)整時(shí)段就得改代碼重新發(fā)布非常不靈活。配置類表包括輪播圖表、字典表、操作日志表。這幾張表的結(jié)構(gòu)都比較標(biāo)準(zhǔn)按常規(guī)設(shè)計(jì)即可。4.2 唯一約束防止重復(fù)預(yù)約數(shù)據(jù)庫層面最容易忽略的點(diǎn)是防止同一個(gè)用戶在同一個(gè)時(shí)間點(diǎn)預(yù)約同一個(gè)場地。代碼里可以加判斷但最穩(wěn)妥的做法是在預(yù)約表上加一個(gè)聯(lián)合唯一索引。比如對user_id、reserve_date、time_slot_id建聯(lián)合唯一索引這樣用戶在同一個(gè)時(shí)段只能有一條預(yù)約記錄數(shù)據(jù)庫層面就把重復(fù)預(yù)約攔死了。這個(gè)設(shè)計(jì)在答辯時(shí)值得單獨(dú)拿出來講。很多學(xué)生只會用程序判斷而你能說出“通過數(shù)據(jù)庫唯一索引做最終兜底保證數(shù)據(jù)一致性”這就是有工程意識的體現(xiàn)。4.3 并發(fā)場景下的防超賣處理預(yù)約系統(tǒng)的“超賣”問題是個(gè)經(jīng)典考點(diǎn)。所謂超賣就是一個(gè)時(shí)段的可預(yù)約名額只有10個(gè)但11個(gè)人同時(shí)提交最后全預(yù)約成功了。解決思路和秒殺系統(tǒng)的庫存扣減類似最常用也最有效的方法是利用SQL原子更新。來看一段核心代碼的寫法Override Transactional(rollbackFor Exception.class) public boolean reduceAvailableCount(Integer timeSlotId) { int updated timeSlotMapper.reduceAvailableCount(timeSlotId); return updated 0; }對應(yīng)的Mapper寫法是UPDATE time_slot SET available_count available_count - 1 WHERE id #{timeSlotId} AND available_count 0這個(gè)SQL的精髓在于把“檢查剩余名額”和“扣減名額”合并成一條原子SQL。數(shù)據(jù)庫在同一時(shí)刻只能有一個(gè)事務(wù)執(zhí)行這條UPDATE當(dāng)多個(gè)請求同時(shí)進(jìn)來時(shí)只有一個(gè)請求能成功把a(bǔ)vailable_count從1更新為0其余請求因?yàn)椴粷M足available_count 0的條件影響行數(shù)為0接口返回“名額已滿”。這樣就在源頭避免超賣問題而不需要依賴復(fù)雜的分布式鎖。這里還有個(gè)容易被忽略的細(xì)節(jié)整個(gè)扣減操作必須在事務(wù)內(nèi)執(zhí)行并且事務(wù)在提交之前要檢查扣減是否成功如果失敗就直接拋出異?;貪L保證不會出現(xiàn)“名額扣了但預(yù)約記錄沒生成”的臟數(shù)據(jù)。5. 核心業(yè)務(wù)流程與狀態(tài)機(jī)設(shè)計(jì)預(yù)約系統(tǒng)的業(yè)務(wù)邏輯看起來散實(shí)際都圍繞著一張預(yù)約單的狀態(tài)轉(zhuǎn)變展開。把狀態(tài)機(jī)畫清楚代碼寫起來就會非常順。這里我不推薦用任何復(fù)雜的設(shè)計(jì)模式直接用一個(gè)普通的狀態(tài)字段加條件判斷就夠清晰了關(guān)鍵是狀態(tài)與狀態(tài)之間的轉(zhuǎn)換條件要說清楚。5.1 預(yù)約狀態(tài)的完整流轉(zhuǎn)路徑一張預(yù)約單從創(chuàng)建到最終結(jié)束可能經(jīng)歷的路徑有以下幾條正常流程提交預(yù)約申請待審核→ 管理員通過已預(yù)約→ 到場地掃碼/簽到已完成被動取消提交預(yù)約申請待審核→ 管理員駁回已取消帶原因→ 流程結(jié)束用戶主動取消提交預(yù)約申請待審核或已預(yù)約→ 用戶點(diǎn)擊取消已取消→ 流程結(jié)束違約流程提交預(yù)約申請待審核→ 管理員通過已預(yù)約→ 用戶未簽到違約→ 流程結(jié)束記錄違約次數(shù)這個(gè)狀態(tài)機(jī)看起來不難但它決定了代碼里每個(gè)方法的邊界。比如用戶取消預(yù)約代碼里必須判斷當(dāng)前狀態(tài)是“待審核”或“已預(yù)約”如果是“已完成”或者“違約”取消接口要直接拒絕。所有狀態(tài)轉(zhuǎn)換都統(tǒng)一在一個(gè)Service類里處理不要散落在各個(gè)Controller里不然改一處漏一處后面調(diào)試會非常痛苦。5.2 后端校驗(yàn)的設(shè)計(jì)層次后端校驗(yàn)是我反復(fù)提醒學(xué)生不要偷懶的部分。小程序前端可以做好看的交互提示但真正的安全判斷必須以后端為準(zhǔn)。校驗(yàn)分三個(gè)層次第一層是參數(shù)基礎(chǔ)校驗(yàn)。比如預(yù)約日期不能為空、場地ID是否存在、時(shí)段是否屬于該場地這類校驗(yàn)用Spring Validation注解就能完成。第二層是業(yè)務(wù)規(guī)則校驗(yàn)。比如用戶是否被拉黑、該用戶當(dāng)前是否有未完成預(yù)約、所選時(shí)段是否還有名額、場地是否處于正常開放狀態(tài)。這一層校驗(yàn)是核心需要寫在Service里。第三層是防并發(fā)校驗(yàn)。即使第二層校驗(yàn)通過了也可能因?yàn)椴l(fā)導(dǎo)致數(shù)據(jù)不一致這時(shí)就要靠前面提到的那條原子更新SQL兜底。我這里給一段典型的預(yù)約提交邏輯偽代碼思路public Result submitReservation(ReservationDTO dto) { // 1. 校驗(yàn)場地存在且正常開放 Room room roomMapper.selectById(dto.getRoomId()); if (room null || room.getStatus() ! 1) { return Result.error(場地不存在或已下架); } // 2. 校驗(yàn)用戶身份與狀態(tài) User user userMapper.selectById(dto.getUserId()); if (user null || user.getStatus() ! 1) { return Result.error(用戶狀態(tài)異常); } // 3. 校驗(yàn)時(shí)段是否可預(yù)約 TimeSlot slot timeSlotMapper.selectById(dto.getTimeSlotId()); if (slot null) { return Result.error(預(yù)約時(shí)段不存在); } // 4. 原子扣減名額防止超賣 boolean success reduceAvailableCount(dto.getTimeSlotId()); if (!success) { return Result.error(該時(shí)段名額已滿); } // 5. 創(chuàng)建預(yù)約單 Reservation reservation new Reservation(); reservation.setUserId(user.getId()); reservation.setRoomId(room.getId()); reservation.setReserveDate(dto.getReserveDate()); reservation.setTimeSlotId(slot.getId()); reservation.setStatus(0); // 待審核 reservationMapper.insert(reservation); return Result.success(預(yù)約申請?zhí)峤怀晒Φ却芾韱T審核); }注意扣減名額這一步發(fā)生在創(chuàng)建預(yù)約單之前因?yàn)槲覀円_認(rèn)扣減成功后才落預(yù)約單數(shù)據(jù)。如果有任何一個(gè)步驟拋異常事務(wù)整體回滾名額不會憑空消失。5.3 簽到與違約系統(tǒng)的管理閉環(huán)一套預(yù)約系統(tǒng)做到“能預(yù)約”只是完成了基本需求真正體現(xiàn)完整度的是“預(yù)約之后怎么辦”。簽到與違約機(jī)制就是負(fù)責(zé)閉環(huán)的。簽到的邏輯可以設(shè)計(jì)成兩種方案。第一種是用戶到達(dá)場地后在小程序端點(diǎn)擊“簽到”按鈕系統(tǒng)校驗(yàn)當(dāng)前時(shí)間和預(yù)約時(shí)間差若在預(yù)約開始時(shí)間前30分鐘到開始時(shí)間后30分鐘之間允許簽到成功超過這個(gè)窗口則標(biāo)記為違約。第二種是管理員在后臺代為簽到適用于用戶到達(dá)后忘記簽到的情況管理員確認(rèn)用戶到場后手動完成簽到。違約認(rèn)定規(guī)則一般這樣設(shè)置預(yù)約通過審核后如果用戶在預(yù)約開始時(shí)間后30分鐘內(nèi)未簽到系統(tǒng)自動更新該條預(yù)約狀態(tài)為“違約”同時(shí)該用戶的違約次數(shù)加一。用戶違約滿3次后系統(tǒng)限制其7天內(nèi)不可預(yù)約。這個(gè)規(guī)則在代碼里是一個(gè)定時(shí)任務(wù)實(shí)現(xiàn)的用Spring的Scheduled注解每隔一分鐘掃描一次預(yù)約表找出所有“預(yù)約日期等于今天、狀態(tài)為已預(yù)約、當(dāng)前時(shí)間已超過開始時(shí)間30分鐘”的記錄批量更新狀態(tài)。定時(shí)任務(wù)的另外兩個(gè)實(shí)用場景是學(xué)生主動取消預(yù)約后自動釋放名額以及過期未審核的預(yù)約單自動駁回。這三個(gè)定時(shí)任務(wù)加起來代碼量不大但是把系統(tǒng)的“自動化”程度拉高不少答辯講到這塊時(shí)老師會認(rèn)為你有完整的產(chǎn)品思維。6. 項(xiàng)目部署從拿到源碼到能演示源碼拿到手最核心的目標(biāo)是先跑起來。部署過程我會按步驟拆解每一步都告訴你為什么這么做避免你卡在某個(gè)莫名其妙的細(xì)節(jié)上。6.1 環(huán)境準(zhǔn)備清單在開始之前先把環(huán)境確認(rèn)一遍。我這里列一個(gè)最小清單JDK 1.8或以上版本推薦1.8穩(wěn)定Maven 3.6用來管理后端依賴MySQL 5.7或8.0本機(jī)安裝即可Redis可選不是必須。預(yù)約系統(tǒng)用MySQL就能撐住如果源碼里有Redis相關(guān)配置通常只用于緩存首頁場地列表和存儲token沒裝的話可以跳過微信開發(fā)者工具最新穩(wěn)定版即可一個(gè)微信小程序測試號注冊個(gè)人類型即可不需要企業(yè)資質(zhì)你會發(fā)現(xiàn)這里沒有提Nacos、沒有提Docker也沒有提Spring Cloud全家桶。對畢設(shè)來說這些都屬于過度設(shè)計(jì)裝一堆東西反而給自己增加排查問題的負(fù)擔(dān)。6.2 后端啟動步驟后端啟動分四步走每一步都不要跳過第一步導(dǎo)入數(shù)據(jù)庫。用Navicat或者命令行執(zhí)行項(xiàng)目里的數(shù)據(jù)庫腳本文件通常叫book.sql或者reservation.sql。執(zhí)行之后檢查一下表是否都建出來了尤其是預(yù)約表、時(shí)段表、用戶表這幾張核心表。第二步修改配置文件。打開application.yml文件重點(diǎn)改三處數(shù)據(jù)庫連接地址、數(shù)據(jù)庫用戶名、數(shù)據(jù)庫密碼。還有一處容易被忽略的是文件上傳路徑如果你的系統(tǒng)支持上傳場地圖片本地會有一個(gè)存儲路徑建議改成絕對路徑。第三步啟動SpringBoot主類。右鍵運(yùn)行Application類的main方法看到“Started Application in xx seconds”字樣說明后端啟動成功。此時(shí)在瀏覽器訪問localhost:8080能看到一些基礎(chǔ)接口的返回信息。第四步接口自測。建議使用Apifox或者Postman先測最簡單的一個(gè)接口比如登錄接口或者場地列表接口確認(rèn)數(shù)據(jù)庫連接和接口路由都沒問題再測預(yù)約提交這類核心流程接口。后端啟動過程最常見的報(bào)錯(cuò)有兩個(gè)一是數(shù)據(jù)庫連接失敗檢查yml里的庫名和密碼是否寫對二是端口被占用把8080改成8081或者隨便一個(gè)空閑端口即可。6.3 小程序端配置與調(diào)試后端起好了下一步就是小程序端。打開微信開發(fā)者工具導(dǎo)入項(xiàng)目文件夾然后在app.js或者config.js里找到baseUrl配置// config.js module.exports { // 本地調(diào)試用指向你本機(jī)的后端地址 baseUrl: http://localhost:8080, // 真機(jī)預(yù)覽時(shí)需要改成局域網(wǎng)IP如 http://192.168.1.101:8080 }這里有個(gè)關(guān)鍵坑需要提醒你小程序開發(fā)時(shí)默認(rèn)不允許訪問http協(xié)議的接口只允許https。但本地后端是http怎么辦兩步操作第一步在微信開發(fā)者工具右上角點(diǎn)擊“詳情”勾選“不校驗(yàn)合法域名、web-view業(yè)務(wù)域名、TLS版本以及HTTPS證書”。這一步是本地聯(lián)調(diào)的必要條件正式上線前當(dāng)然需要配置合法域名但畢設(shè)演示階段用本地地址就夠了。第二步如果要在真機(jī)上預(yù)覽小程序和電腦必須處于同一局域網(wǎng)而且你要在小程序后臺把電腦的局域網(wǎng)IP加到request合法域名里。嫌麻煩的話答辯現(xiàn)場用開發(fā)者工具的模擬器演示完全足夠不需要真機(jī)。6.4 管理端啟動管理端是一個(gè)獨(dú)立的Web項(xiàng)目技術(shù)棧一般是Vue或者簡單的Bootstrap頁面。啟動方式和后端類似如果是Vue項(xiàng)目需要先執(zhí)行npm install安裝依賴然后npm run serve啟動開發(fā)服務(wù)器瀏覽器訪問localhost的對應(yīng)端口即可。管理端的地址配置要注意它也要知道后端的服務(wù)地址在哪里通常也是在配置環(huán)境變量或者請求封裝文件里改baseURL。前端項(xiàng)目有個(gè)通病改完配置不重啟不生效。我建議每次改動后都重新啟動一遍不要偷懶熱更新避免出現(xiàn)“改了但沒生效”的玄學(xué)問題。7. 答辯實(shí)錄評審老師最愛追問的幾個(gè)地方代碼跑通只是畢設(shè)的第一步答辯表現(xiàn)才是決定成績的關(guān)鍵。我把這些年學(xué)生被問到最多的問題整理出來你在準(zhǔn)備答辯時(shí)對著這些問題過一遍基本穩(wěn)了。7.1 “為什么選擇SpringBoot而不是SSH/SSM”被問到這個(gè)問題很多學(xué)生的回答是“大家都用SpringBoot”。這個(gè)回答不是錯(cuò)但太弱了。更好的回答思路是從維護(hù)成本和開發(fā)效率切入??梢赃@樣回答傳統(tǒng)的SSH或SSM項(xiàng)目需要手動配置大量XML文件開發(fā)環(huán)境搭建復(fù)雜調(diào)試周期長。SpringBoot提供自動化配置把常用框架的默認(rèn)配置都內(nèi)置了配合內(nèi)嵌Tomcat解決了環(huán)境一致性問題。對于預(yù)約系統(tǒng)這種典型的CRUD加業(yè)務(wù)邏輯項(xiàng)目來說SpringBoot能讓我把更多精力放在業(yè)務(wù)實(shí)現(xiàn)本身而不是陷入框架配置的泥潭。7.2 “預(yù)約場景下如何處理并發(fā)沖突”這是最容易被問到的高質(zhì)量問題。參考答案分兩層講第一層數(shù)據(jù)庫層面的原子操作兜底。通過UPDATE time_slot SET available_count available_count - 1 WHERE id ? AND available_count 0來扣減名額由于UPDATE語句本身是行級鎖同一時(shí)刻只有一個(gè)事務(wù)能成功扣減從根上避免超賣。第二層業(yè)務(wù)層的唯一索引兜底。在預(yù)約表上建立聯(lián)合唯一索引防止同一用戶同一時(shí)段重復(fù)預(yù)約即使業(yè)務(wù)邏輯代碼出了bug數(shù)據(jù)庫也不會寫入臟數(shù)據(jù)。把這兩層說完基本可以把大多數(shù)老師的胃口填滿。7.3 “你覺得這個(gè)系統(tǒng)還有什么可以改進(jìn)的地方”這個(gè)問題其實(shí)是送分題但很多學(xué)生回答“沒有”就白白丟分了。你應(yīng)該在答辯前準(zhǔn)備好兩三個(gè)合理的擴(kuò)展方向。我的建議是消息通知擴(kuò)展當(dāng)前系統(tǒng)只在預(yù)約狀態(tài)變化時(shí)返回結(jié)果用戶體驗(yàn)不夠主動??梢砸胛⑿庞嗛喯⒃陬A(yù)約審核通過、預(yù)約時(shí)間即將開始、違約提醒這三個(gè)節(jié)點(diǎn)主動推送通知。信用積分體系當(dāng)前違約滿三次禁約7天是硬編碼規(guī)則可以抽象成信用積分模型。預(yù)約行為、違約行為、簽到行為都影響分?jǐn)?shù)分?jǐn)?shù)決定用戶可預(yù)約場地的優(yōu)先級。把規(guī)則引擎化系統(tǒng)就更像一個(gè)產(chǎn)品了。多端擴(kuò)展小程序端之外可以增加企業(yè)微信端管理入口讓管理員在手機(jī)上就能完成審批??臻g預(yù)約這類輕管理需求移動審批是真實(shí)場景中高頻剛需。寫在最后的一點(diǎn)經(jīng)驗(yàn)預(yù)約系統(tǒng)這類項(xiàng)目真正難的不是“寫出來”而是“講清楚”。很多同學(xué)在開發(fā)階段悶頭看代碼功能做完了但整個(gè)項(xiàng)目的設(shè)計(jì)思路、模塊劃分、關(guān)鍵決策腦子里還是一團(tuán)漿糊。等到答辯答辯時(shí)被老師連續(xù)追問幾句“為什么”就卡殼了。所以我給你的建議是拿到源碼之后先花一個(gè)晚上把數(shù)據(jù)庫表和核心業(yè)務(wù)流程梳理透再花一個(gè)晚上把SpringBoot里幾個(gè)關(guān)鍵Service類的調(diào)用鏈走一遍然后親手把項(xiàng)目從零啟動起來把每個(gè)功能點(diǎn)都實(shí)際操作一遍。能做到這三件事不管是源碼分享還是定制改造你都已經(jīng)具備了獨(dú)立講解的能力。如果你打算拿這個(gè)題目做二次開發(fā)優(yōu)先改的方向我建議是預(yù)約維度從“場地”擴(kuò)展到“場地內(nèi)的座位”或者“空間設(shè)備”的組合預(yù)約這會直接提升系統(tǒng)的數(shù)據(jù)模型復(fù)雜度工作量增加不多但答辯的深度會有質(zhì)的變化。要是你手頭正好也在做著類似的預(yù)約類項(xiàng)目遇到具體的坑歡迎一起交流。