約系統(tǒng)開發(fā)全攻略:從需求拆分到Spring Boot+Vue實(shí)現(xiàn))
簡介智能會議室預(yù)約管理系統(tǒng)源碼包面向計(jì)算機(jī)、電子信息工程、數(shù)學(xué)等專業(yè)學(xué)生的課程設(shè)計(jì)、期末大作業(yè)與畢業(yè)設(shè)計(jì)實(shí)踐。系統(tǒng)圍繞會議室預(yù)約管理全流程涵蓋用戶身份認(rèn)證、會議室狀態(tài)查詢、預(yù)約沖突檢測、自動提醒及后臺數(shù)據(jù)統(tǒng)計(jì)分析等核心模塊能幫助學(xué)生理解實(shí)際業(yè)務(wù)需求與智能系統(tǒng)設(shè)計(jì)方法。壓縮包共2000個(gè)文件約9.77MB以C/C源碼769個(gè)c、767個(gè)h為主輔以匯編啟動文件、Python腳本、構(gòu)建配置腳本、Markdown文檔及圖表素材等便于從源碼到構(gòu)建、運(yùn)行、說明全方位研讀。已有57人學(xué)習(xí)瀏覽。通過研讀該項(xiàng)目可掌握用戶登錄驗(yàn)證、預(yù)約算法、沖突處理、消息提醒等功能的落地實(shí)現(xiàn)并學(xué)習(xí)makefile、configure等工程構(gòu)建配置思路是提升編程能力與項(xiàng)目管理能力的實(shí)用參考資料。1. 智能會議室預(yù)約管理系統(tǒng)畢設(shè)課設(shè)都繞不開的經(jīng)典選題每到周五下午技術(shù)部的劉工都得在群里喊三遍下午三點(diǎn)大會議室誰用了沒人回答到了三點(diǎn)門口站了四個(gè)人。公司如此學(xué)校也一樣。智能會議室預(yù)約管理系統(tǒng)把整個(gè)流程搬上線會議室信息維護(hù)、預(yù)約申請、沖突檢測、審批流轉(zhuǎn)、歷史記錄一套下來正好覆蓋管理系統(tǒng)類課設(shè)的全部經(jīng)典考點(diǎn)。這份畢設(shè)課設(shè)資源是一套完整可運(yùn)行的工程前后端都在適合拿來做選題交付也適合剛?cè)胄械娜水?dāng)源碼精讀的第一份完整項(xiàng)目。2. 需求拆解與技術(shù)選型四個(gè)核心模塊和三張核心表2.1 需求邊界這個(gè)系統(tǒng)到底要管哪幾件事先別急著解壓看代碼動手之前把需求邊界畫清楚。這類管理系統(tǒng)最容易翻車的點(diǎn)就是邊界沒定就開始建表做著做著發(fā)現(xiàn)會議室狀態(tài)取消預(yù)約統(tǒng)計(jì)報(bào)表這些需求全擠在一起代碼越寫越亂。我一般會把題目拆成四個(gè)模塊這也是答辯時(shí)老師對需求分析最認(rèn)可的拆法。第一個(gè)是會議室資源管理維護(hù)會議室的基礎(chǔ)信息名稱、位置、容量、設(shè)備、是否啟用。第二個(gè)是預(yù)約申請用戶選日期和時(shí)間區(qū)間填會議主題提交后進(jìn)入審批。第三個(gè)是審批流轉(zhuǎn)管理員通過或駁回通過后該時(shí)間段被鎖定駁回則釋放。第四個(gè)是歷史記錄與統(tǒng)計(jì)按時(shí)間、會議室、申請人的維度查記錄配合簡單圖表說明系統(tǒng)使用情況。課設(shè)和畢設(shè)的差異就在第四點(diǎn)上。課設(shè)做到前三步頁面能跑通就算交付但畢設(shè)答辯老師幾乎一定會追問系統(tǒng)好不好用、怎么體現(xiàn)價(jià)值這時(shí)候統(tǒng)計(jì)報(bào)表和導(dǎo)出功能就是你的護(hù)城河。所以建表階段就要把create_time、audit_time、status這類字段留好后面做統(tǒng)計(jì)才有數(shù)據(jù)可查不用返工。2.2 技術(shù)選型前后端分離是主流別盲目上微服務(wù)這份資源的技術(shù)棧以標(biāo)題信息為準(zhǔn)我不替它吹。但如果拿到的代碼不符合你自己熟悉的路子也別慌直接說這個(gè)場景下被驗(yàn)證最多的組合后端 Spring Boot MyBatis-Plus MySQL前端 Vue 3 Element Plus身份認(rèn)證用 JWT 或 Session。為什么不是 SSH 老架構(gòu)不是不好而是這套老技術(shù)?,F(xiàn)在出問題你能搜到的有效答案已經(jīng)很少課設(shè)周期兩三個(gè)月卡一個(gè)詭異異常就是一周。為什么不上微服務(wù)因?yàn)闀h室預(yù)約這個(gè)體量單機(jī)跑幾千人學(xué)校一周的預(yù)約量毫無壓力微服務(wù)只會把答辯變成你講講服務(wù)間怎么調(diào)用的然后自己圓不回來。也有課程用 Python 做Flask 或 Django 配 Bootstrap 同樣能交。Python 方案的優(yōu)點(diǎn)是你自己改代碼的負(fù)擔(dān)小路由和 Model 寫得直觀缺點(diǎn)是部署到老師電腦時(shí)依賴版本容易打架pip install裝錯(cuò)版本比 Java 的 Maven 報(bào)錯(cuò)更難懂。我的建議是后端基礎(chǔ)一般的直接選 Spring Boot網(wǎng)上文檔密度最高報(bào)錯(cuò)粘貼進(jìn)搜索引擎基本都有同名案例。前端部分能選 Vue 3 就不要用 jQuery 拼頁面。預(yù)約系統(tǒng)的核心交互是選日期、看占用、提申請Vue 的雙向綁定做表單和數(shù)據(jù)回顯非常順手日歷格子上的占用狀態(tài)用v-for渲染也清晰。Element Plus 提供現(xiàn)成的表格、日期選擇器、彈窗課設(shè)階段不用自己寫復(fù)雜 CSS省下的時(shí)間全花在業(yè)務(wù)邏輯上性價(jià)比最高。2.3 數(shù)據(jù)模型預(yù)約記錄表是三張核心表的重心數(shù)據(jù)模型是整套代碼的地基。我見過太多預(yù)約系統(tǒng)翻車都是表設(shè)計(jì)階段漏字段后期補(bǔ)字段補(bǔ)到懷疑人生。核心表就三張用戶表、會議室表、預(yù)約記錄表。其中預(yù)約記錄表是全項(xiàng)目的邏輯重心走廊里的爭吵、答辯時(shí)的亮點(diǎn)、隱藏的坑全在這張表上。CREATE TABLE booking_record ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵, room_id bigint NOT NULL COMMENT 會議室ID關(guān)聯(lián) meeting_room.id, booker_id bigint NOT NULL COMMENT 申請人ID關(guān)聯(lián) user.id, book_date date NOT NULL COMMENT 預(yù)約日期只存日期, start_time time NOT NULL COMMENT 開始時(shí)間如 09:00:00, end_time time NOT NULL COMMENT 結(jié)束時(shí)間如 10:00:00, title varchar(100) DEFAULT NULL COMMENT 會議主題, status tinyint NOT NULL DEFAULT 0 COMMENT 0待審核 1已通過 2已駁回 3已取消 4已結(jié)束, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 提交時(shí)間, audit_time datetime DEFAULT NULL COMMENT 審批時(shí)間, auditor_id bigint DEFAULT NULL COMMENT 審批人ID, PRIMARY KEY (id), KEY idx_room_date (room_id, book_date), KEY idx_booker (booker_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT預(yù)約記錄表;這段建表語句有三個(gè)細(xì)節(jié)值得說明。第一日期和時(shí)間分開存book_date只存日期start_time、end_time存時(shí)間判斷沖突時(shí)直接用時(shí)間類型比較比存 datetime 再截取干凈得多。第二status用 tinyint 而不是字符串省空間也方便狀態(tài)機(jī)流轉(zhuǎn)后面審批接口只需要改數(shù)字。第三聯(lián)合索引idx_room_date建在room_id和book_date上因?yàn)闃I(yè)務(wù)里最高頻的查詢就是某一天某個(gè)會議室有沒有被占這個(gè)索引能直接命中。用戶表和會議室表結(jié)構(gòu)就簡單多了。用戶表帶username、password、role角色分普通用戶和管理員會議室表帶name、capacity、location、device、status。這里有個(gè)血淚經(jīng)驗(yàn)密碼別明文存。哪怕課設(shè)不要求安全演示至少用 MD5 加鹽或 BCrypt 做一次哈希這屬于答辯老師一眼就能瞟出來的低級問題別在這種地方送分。3. 把系統(tǒng)跑起來環(huán)境配置、數(shù)據(jù)庫初始化與前后端聯(lián)調(diào)3.1 環(huán)境清單版本匹配是第一個(gè)坑拿到任何畢設(shè)源碼第一步不是改代碼而是搭一個(gè)干凈環(huán)境。環(huán)境問題占了這類項(xiàng)目運(yùn)行失敗原因的六成以上而且是換臺電腦就崩潰的那種。先看版本匹配表格再對照自己機(jī)器。組件建議版本用途說明JDK1.8 或 11編譯運(yùn)行后端別直接裝 17部分老依賴會報(bào)錯(cuò)MySQL5.7 或 8.0數(shù)據(jù)存儲8.0 注意驅(qū)動版本和時(shí)區(qū)配置Maven3.6后端依賴管理配阿里云鏡像不然下載慢到懷疑人生Node.js14 或 16前端構(gòu)建Vue 3 Vite 建議 16Navicat / DBeaver任意數(shù)據(jù)庫導(dǎo)入DBeaver 免費(fèi)就夠用這里面最容易踩的是 JDK 版本。很多畢業(yè)設(shè)計(jì)項(xiàng)目是三四年前寫的用的依賴沒跟上新版本你拿 JDK 17 一啟動直接報(bào)UnsupportedClassVersionError網(wǎng)上答案說降版本你換了又出現(xiàn)新問題。我一般直接裝 JDK 1.8把JAVA_HOME指過去這套組合兼容性最強(qiáng)老師演示也不會因?yàn)槟阊b的是太新的版本而翻車。數(shù)據(jù)庫連接串也是一個(gè)高頻坑。Spring Boot 連 MySQL 8.0 時(shí)控制臺報(bào)Public Key Retrieval is not allowed是因?yàn)?MySQL 8 默認(rèn)的認(rèn)證插件要求顯式允許公鑰檢索。在application.yml的連接 URL 后面拼兩個(gè)參數(shù)就能解決見 3.3 節(jié)。3.2 數(shù)據(jù)庫初始化別用鼠標(biāo)雙擊 SQL 文件解壓 zip 之后工程里一般會有sql目錄里面放著建庫腳本。常見做法是用 Navicat 右鍵運(yùn)行 SQL 文件但更穩(wěn)的方式是在命令行里 source這樣報(bào)錯(cuò)信息直接顯示在哪一行。mysql -uroot -p CREATE DATABASE meeting_room DEFAULT CHARACTER SET utf8mb4; USE meeting_room; source /your/path/meeting_room.sql;邏輯說明先建一個(gè)指定utf8mb4字符集的庫再切進(jìn)去執(zhí)行腳本。utf8mb4一定要顯式寫不然遇到中文和表情符號可能出現(xiàn)亂碼。source 后面要用絕對路徑相對路徑容易因?yàn)楫?dāng)前目錄不對而報(bào)Failed to open file這是新手最常見的翻車點(diǎn)。執(zhí)行完檢查一下表是否建全。SHOW TABLES;應(yīng)該能看到user、meeting_room、booking_record三張及以上。如果 SQL 腳本里帶了初始管理員賬號和數(shù)據(jù)樣例這一步就已經(jīng)有可用數(shù)據(jù)了如果腳本是空表后面聯(lián)調(diào)時(shí)你會連登錄都進(jìn)不去得手動 INSERT 一條管理員記錄。3.3 前后端聯(lián)調(diào)接口地址、跨域與時(shí)區(qū)前后端分離項(xiàng)目跑不起來的第二個(gè)高頻原因是前端請求后端的地址配錯(cuò)了。前端跑在http://localhost:5173后端跑在http://localhost:8080瀏覽器跨域直接攔截。常規(guī)解法是前端用 Vite 代理把所有/api開頭的請求轉(zhuǎn)發(fā)給后端這樣前端代碼里只需要寫相對路徑。// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })參數(shù)說明port指定前端開發(fā)服務(wù)器端口proxy里的/api表示攔截所有以/api開頭的請求target是后端真實(shí)地址changeOrigin設(shè)為 true 保證請求頭里的 Host 被改寫后端才認(rèn)得是同一個(gè)項(xiàng)目。改完配置重啟npm run dev才生效。如果項(xiàng)目沒用代理而是后端開啟了全局 CORS那前端請求里就得寫完整地址。兩種方案都行我推薦代理因?yàn)椴渴鸬椒?wù)器時(shí) Nginx 反代用的是同一套思路答辨時(shí)能順口講一句前后端聯(lián)調(diào)和生產(chǎn)部署的路徑是一致的。后端側(cè)主要看application.yml里的數(shù)據(jù)源配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/meeting_room?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456這段配置里serverTimezoneAsia/Shanghai必寫否則 JDBC 驅(qū)動會拿默認(rèn) UTC 時(shí)區(qū)預(yù)約時(shí)間在頁面上顯示會差 8 個(gè)小時(shí)。allowPublicKeyRetrievaltrue是給 MySQL 8 用的配合useSSLfalse一起出現(xiàn)專治前面說的公鑰報(bào)錯(cuò)。密碼改成你自己數(shù)據(jù)庫的實(shí)際密碼寫死明文只是課設(shè)階段的權(quán)宜之計(jì)交到生產(chǎn)環(huán)境必須用配置中心或環(huán)境變量。3.4 啟動順序與驗(yàn)證后端先起來前端再啟動啟動順序別搞反。后端先啟動確認(rèn) 8080 端口無報(bào)錯(cuò)再啟動前端。后端如果是 Spring Boot 的 jar 包直接java -jar如果是 Maven 工程IDE 里運(yùn)行主類即可。前端安裝依賴再啟動npm install npm run devnpm install如果卡在某個(gè)包不動常見原因是默認(rèn)源在國外換成淘寶鏡像npm config set registry https://registry.npmmirror.com再裝速度立竿見影。依賴裝完后npm run dev啟動 Vite 開發(fā)服務(wù)器控制臺會打印訪問地址。驗(yàn)證路徑很簡單瀏覽器打開前端地址能看到登錄頁用初始管理員賬號登錄進(jìn)入系統(tǒng)后隨便點(diǎn)一個(gè)菜單。如果接口報(bào) 401 或 404先看后端控制臺有沒有請求日志有日志說明網(wǎng)絡(luò)通了問題在接口路徑或 Token沒有日志說明前端請求根本沒到后端回去檢查代理配置。4. 核心邏輯的代碼落地預(yù)約沖突檢測與狀態(tài)機(jī)實(shí)現(xiàn)4.1 沖突檢測同一會議室時(shí)間重疊怎么判斷預(yù)約系統(tǒng)的靈魂是沖突檢測。會議室同一個(gè)時(shí)間段只能被一個(gè)已通過的預(yù)約占用這個(gè)判斷寫不對所有頁面功能都是白搭。先說一下時(shí)間重疊的四種情況新預(yù)約完全在已有區(qū)間內(nèi)、已有區(qū)間完全在新預(yù)約內(nèi)、新預(yù)約開始早于已有結(jié)束、新預(yù)約結(jié)束晚于已有開始。四種情況其實(shí)覆蓋了同一件事——兩個(gè)區(qū)間有交集判斷條件是新開始小于已有結(jié)束且新結(jié)束大于已有開始。LambdaQueryWrapperBookingRecord wrapper new LambdaQueryWrapper(); wrapper.eq(BookingRecord::getRoomId, dto.getRoomId()) .eq(BookingRecord::getBookDate, dto.getBookDate()) .and(w - w.ge(BookingRecord::getEndTime, dto.getStartTime()) .le(BookingRecord::getStartTime, dto.getEndTime())) .and(w - w.ne(BookingRecord::getStatus, 2) .ne(BookingRecord::getStatus, 3)); Long count bookingMapper.selectCount(wrapper);邏輯說明第一層eq限定同一會議室同一天第二層and里的ge和le實(shí)現(xiàn)區(qū)間重疊第三層and排除掉狀態(tài)為已駁回和已取消的記錄因?yàn)檫@兩種狀態(tài)的時(shí)間是釋放的不能算占用。ge是大于等于le是小于等于正好覆蓋邊界9:00-10:00 和 10:00-11:00 這兩個(gè)預(yù)約在邊界點(diǎn)上相接不算沖突等于號不會誤傷。這里有一個(gè)隱蔽的坑如果待審核的記錄也算進(jìn)沖突查詢那用戶提交兩個(gè)待審核預(yù)約會互相卡死但如果不算兩個(gè)管理員同時(shí)審批就會撞車。正確思路是提交時(shí)只校驗(yàn)已通過的沖突審批時(shí)再查一次已通過 本次待審批的沖突雙保險(xiǎn)。MyBatis-Plus 的LambdaQueryWrapper比手寫 XML 在簡單查詢下更直觀但復(fù)雜 SQL 比如跨表統(tǒng)計(jì)時(shí)還是建議寫 XML別硬用構(gòu)造器拼。4.2 審批狀態(tài)機(jī)狀態(tài)字段與流轉(zhuǎn)規(guī)則狀態(tài)機(jī)是第二個(gè)答辯常問題目。預(yù)約記錄的狀態(tài)有五種我用一個(gè)枚舉管理而不是散落在代碼里的魔法數(shù)字public enum BookingStatus { PENDING(0, 待審核), APPROVED(1, 已通過), REJECTED(2, 已駁回), CANCELED(3, 已取消), FINISHED(4, 已結(jié)束); private final int value; private final String desc; BookingStatus(int value, String desc) { this.value value; this.desc desc; } public int getValue() { return value; } }流轉(zhuǎn)規(guī)則只有幾條待審核可以轉(zhuǎn)已通過或已駁回已通過可以轉(zhuǎn)已取消或已結(jié)束已駁回和已取消是終態(tài)已結(jié)束不可逆。這套規(guī)則在課設(shè)層面夠用了別引入 Flowable 之類的工作流引擎那是把簡單問題復(fù)雜化的典型操作答辯老師聽到你用了工作流引擎追問的問題比你的項(xiàng)目還深。審批接口是狀態(tài)機(jī)的核心入口代碼里要攔住重復(fù)審批PostMapping(/audit) public Result audit(RequestBody AuditDTO dto) { BookingRecord record bookingMapper.selectById(dto.getBookingId()); if (record null) { return Result.error(預(yù)約記錄不存在); } if (record.getStatus() ! BookingStatus.PENDING.getValue()) { return Result.error(該記錄已處理請勿重復(fù)審批); } if (dto.getApprove()) { boolean conflict checkConflict(record.getRoomId(), record.getBookDate(), record.getStartTime(), record.getEndTime()); if (conflict) { return Result.error(該時(shí)間段已存在通過審批的預(yù)約); } } record.setStatus(dto.getApprove() ? BookingStatus.APPROVED.getValue() : BookingStatus.REJECTED.getValue()); record.setAuditTime(new Date()); record.setAuditorId(getCurrentUserId()); bookingMapper.updateById(record); return Result.success(); }參數(shù)說明AuditDTO里帶bookingId和approve兩個(gè)字段approve為 true 表示通過false 表示駁回。先查狀態(tài)非待審核直接拒絕防止管理員在頁面上重復(fù)點(diǎn)擊產(chǎn)生兩次請求。通過審批前再查一次沖突正是 4.1 里說的雙保險(xiǎn)邏輯因?yàn)樘峤活A(yù)約和審批之間可能隔了幾天期間其他預(yù)約可能已經(jīng)把這臺會議室的時(shí)間段占掉。前端在管理員通過時(shí)會彈一次確認(rèn)框但這只是體驗(yàn)問題真正的攔截必須在后端做。前端校驗(yàn)可以繞過后端的重復(fù)審批檢查和沖突檢查才是不可跳過的防線這個(gè)認(rèn)知答辯時(shí)說出來老師會認(rèn)為你有工程意識。4.3 日歷視圖把占用情況一次性查出來日歷視圖是展示端的核心。用戶打開預(yù)約頁面第一眼要看的是今天這臺會議室哪些時(shí)間段能用所以后端要提供一個(gè)接口輸入某一天返回該天所有會議室的所有有效預(yù)約。wrapper.eq(BookingRecord::getBookDate, date) .in(BookingRecord::getStatus, BookingStatus.PENDING.getValue(), BookingStatus.APPROVED.getValue()) .orderByAsc(BookingRecord::getRoomId) .orderByAsc(BookingRecord::getStartTime);這段查詢把某天所有待審核和已通過的預(yù)約按會議室、開始時(shí)間排序。已駁回和已取消不返回因?yàn)樗鼈冊跁r(shí)間軸上沒有意義。前端拿到這份列表后按會議室維度分組渲染到表格或日歷組件里每個(gè)格子顯示09:00-10:00 需求評審會議這樣的信息。前后端的數(shù)據(jù)格式約定好后端返回扁平數(shù)組前端負(fù)責(zé)分組這樣接口足夠通用以后擴(kuò)展周視圖、月視圖都不動后端。5. 避坑手冊部署、業(yè)務(wù)邏輯與答辯問答的典型翻車實(shí)錄5.1 環(huán)境與部署五個(gè)高頻翻車點(diǎn)現(xiàn)象一啟動后端時(shí)報(bào)UnsupportedClassVersionError。原因JVM 版本低于編譯版本常見于你用 JDK 17 跑別人用 JDK 8 編譯的項(xiàng)目或反過來。解決統(tǒng)一 JDK 版本到 1.8檢查 IDE 里 Project Structure 的 SDK 和 Maven 的 compiler level 是否一致這兩個(gè)位置經(jīng)常出現(xiàn)一個(gè)管編譯、一個(gè)管運(yùn)行的脫節(jié)。現(xiàn)象二前端頁面能打開但登錄時(shí)接口全部 404。原因前端代理沒生效請求打到了 5173 端口自己。解決先看 Network 面板請求地址如果還是localhost:5173/api檢查 vite.config.js 是否修改后忘記重啟。Vite 代理配置改了必須重啟開發(fā)服務(wù)器才生效這是新手最容易忽略的細(xì)節(jié)?,F(xiàn)象三數(shù)據(jù)庫導(dǎo)入 SQL 時(shí)報(bào)No database selected。原因命令行里沒先USE切庫。解決source 前務(wù)必執(zhí)行USE meeting_room;Navicat 里則是選中目標(biāo)庫再運(yùn)行腳本兩個(gè)方式都行但別直接雙擊腳本讓它自己猜庫。現(xiàn)象四時(shí)間顯示差了 8 個(gè)小時(shí)。原因JDBC 連接串沒指定時(shí)區(qū)默認(rèn)用了 UTC。解決把serverTimezoneAsia/Shanghai加到連接 URL同時(shí)確認(rèn) MySQL 系統(tǒng)時(shí)區(qū)也不是 UTCshow variables like %time_zone%能看到?,F(xiàn)象五后端端口被占用啟動直接失敗。原因之前調(diào)試會話沒關(guān)干凈。解決Windows 上netstat -ano | findstr 8080找到 PID任務(wù)管理器結(jié)束進(jìn)程Mac 上lsof -i :8080。這不是代碼問題但卡住你的時(shí)候足夠讓人抓狂。5.2 業(yè)務(wù)邏輯三個(gè)隱藏雷區(qū)雷區(qū)一邊界時(shí)間被誤判為沖突。用戶預(yù)約 9:00-10:00另一個(gè)用戶預(yù)約 10:00-11:00這是合法的相鄰預(yù)約但如果沖突判斷用了大于小于而不是大于等于小于等于邊界點(diǎn)就會互相誤傷。解決堅(jiān)持end newStart AND start newEnd并在測試用例里專測邊界值。雷區(qū)二取消預(yù)約后時(shí)間沒有釋放。用戶的預(yù)約通過了臨時(shí)不開了點(diǎn)了取消但前臺查詢?nèi)匀徊榈玫竭@條記錄原因是取消時(shí)只改了頁面狀態(tài)數(shù)據(jù)庫里記錄還是已通過。解決取消操作就是一個(gè)status 3的更新沖突檢測里排除狀態(tài)為 2 和 3 的記錄兩條規(guī)則配合才完整。雷區(qū)三審批狀態(tài) shi 程序員寫死的魔法數(shù)字。代碼里到處是if (status 1)過一周自己都分不清 1 是已通過還是已駁回。解決用枚舉類統(tǒng)一收斂就是我 4.2 節(jié)寫的BookingStatus改狀態(tài)語義只動一個(gè)文件。這屬于代碼規(guī)范和業(yè)務(wù)邏輯的交界地答辯時(shí)講出來有加分效果。5.3 答辯問答老師最常問的三個(gè)問題第一個(gè)問題是狀態(tài)字段為什么不用字符串要用數(shù)字?;卮鹨c(diǎn)存儲體積小、索引效率高、擴(kuò)展方便。如果老師追問可讀性怎么辦補(bǔ)一句代碼里有枚舉類映射查詢結(jié)果轉(zhuǎn)成枚舉后頁面展示的是中文描述。第二個(gè)問題是你怎么防止兩個(gè)人同時(shí)提交同一個(gè)會議室的同一個(gè)時(shí)間段。這是并發(fā)問題。回答要點(diǎn)前端的按鈕防重復(fù)提交只是一層真正的兜底是數(shù)據(jù)庫層的表鎖或唯一索引。演示級項(xiàng)目可以用SELECT ... FOR UPDATE鎖住會議室當(dāng)天的記錄再插入預(yù)約如果要講得更深就說生產(chǎn)上會引入 Redis 分布式鎖課設(shè)能講到這一層已經(jīng)超過九成同級水平。第三個(gè)問題是查詢性能怎么保證?;卮鹨c(diǎn)idx_room_date聯(lián)合索引覆蓋了最高的查詢場景booking_record表數(shù)據(jù)量在千級時(shí)單表查詢毫秒級返回。如果老師追問數(shù)據(jù)量大了怎么辦答按會議室分表或按月分表即可不需要真做老師問的是有沒有考慮過。6. 交付之后的事定時(shí)任務(wù)、統(tǒng)計(jì)報(bào)表與簡歷化改造6.1 用定時(shí)任務(wù)自動收尾過期預(yù)約系統(tǒng)里有一個(gè)狀態(tài)永遠(yuǎn)沒人維護(hù)會議都開完了記錄還停在已通過。我這里用一個(gè) Spring 自帶的Scheduled定時(shí)任務(wù)兜底每天凌晨跑一遍把結(jié)束時(shí)間小于當(dāng)前時(shí)間的已通過預(yù)約改成已結(jié)束。Scheduled(cron 0 0 2 * * *) public void finishExpiredBookings() { LambdaUpdateWrapperBookingRecord wrapper new LambdaUpdateWrapper(); wrapper.eq(BookingRecord::getStatus, BookingStatus.APPROVED.getValue()) .lt(BookingRecord::getEndTime, LocalTime.now()) .set(BookingRecord::getStatus, BookingStatus.FINISHED.getValue()); bookingMapper.update(null, wrapper); }注意這里有個(gè)細(xì)節(jié)end_time是 time 類型只存了時(shí)分秒如果會議跨天晚上 10 點(diǎn)開始到凌晨 1 點(diǎn)這個(gè)任務(wù)會誤判。常見做法是把結(jié)束日期和結(jié)束時(shí)間拼成一個(gè) datetime 再比較或者干脆在表里冗余一個(gè)end_datetime字段。課設(shè)里通常不需要跨天場景但你心里要清楚這是邊界答辯時(shí)被發(fā)現(xiàn)比不發(fā)現(xiàn)強(qiáng)。6.2 統(tǒng)計(jì)報(bào)表從裸數(shù)據(jù)到可展示的圖表畢設(shè)加了統(tǒng)計(jì)頁答辯效果立刻不一樣。按月統(tǒng)計(jì)每個(gè)會議室的預(yù)約次數(shù)和使用率SQL 用 GROUP BY 就能完成。前端展示用 ECharts 的柱狀圖或餅圖組件封裝好后只需傳一個(gè)統(tǒng)計(jì)數(shù)組。建議做一個(gè)會議室使用率排名的接口返回前五名會議室及其次數(shù)輸出到柱狀圖。這一項(xiàng)就能覆蓋數(shù)據(jù)可視化的課程目標(biāo)而且工作量不大一周內(nèi)能做完。6.3 把課設(shè)項(xiàng)目包裝成簡歷項(xiàng)目不要寫智能會議室預(yù)約管理系統(tǒng)當(dāng)項(xiàng)目標(biāo)題寫成基于 Spring Boot 的多會議室資源調(diào)度平臺。描述里突出三件事設(shè)計(jì)了預(yù)約沖突檢測算法、實(shí)現(xiàn)了審批狀態(tài)機(jī)、用聯(lián)合索引優(yōu)化高頻查詢。這三句話每一個(gè)都能被面試官追問到細(xì)節(jié)而你在前面幾章已經(jīng)把細(xì)節(jié)吃透了。如果時(shí)間富余加一個(gè)操作日志表記錄誰在什么時(shí)候?qū)徟苏l的預(yù)約面試聊的時(shí)候說這是審計(jì)追蹤比說我做了個(gè)增刪改查有質(zhì)感的不是一星半點(diǎn)。從那以后我每次拿到一套課設(shè)源碼都會先強(qiáng)制走一遍環(huán)境檢查、數(shù)據(jù)庫初始化、聯(lián)調(diào)驗(yàn)證再開始動代碼。這套流程幫我省下的時(shí)間遠(yuǎn)比我寫代碼的時(shí)間多。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取