生租房平臺(tái):從需求設(shè)計(jì)到畢業(yè)答辯的完整實(shí)戰(zhàn))
1. 這個(gè)項(xiàng)目到底在做什么大學(xué)生租房平臺(tái)的業(yè)務(wù)定位與需求拆解每年畢業(yè)設(shè)計(jì)季被問(wèn)得最多的題目類型之一就是“基于Spring Boot的某某平臺(tái)”其中“大學(xué)生租房平臺(tái)”是典型中的典型。這題目的價(jià)值在于它不只是一個(gè)增刪改查的演示系統(tǒng)它背后站著的是一整條真實(shí)的業(yè)務(wù)鏈路房源錄入、信息檢索、預(yù)約看房、簽約成交、評(píng)價(jià)反饋。稍微把每個(gè)環(huán)節(jié)做扎實(shí)一點(diǎn)就是一個(gè)能放在簡(jiǎn)歷上講的完整項(xiàng)目。先說(shuō)清楚題目里的兩個(gè)關(guān)鍵詞。第一是“springboot007”這個(gè)007更像是一個(gè)內(nèi)部編號(hào)或課程設(shè)計(jì)歸檔號(hào)真正決定項(xiàng)目形態(tài)的是后半句——大學(xué)生租房平臺(tái)的設(shè)計(jì)與實(shí)現(xiàn)文檔源碼。也就是說(shuō)這個(gè)項(xiàng)目考核的不光是能跑的代碼還有配套的畢業(yè)設(shè)計(jì)文檔。很多同學(xué)只盯著源碼文檔草草拼湊最后答辯被問(wèn)得無(wú)話可說(shuō)這是我最想先提醒的一點(diǎn)。那為什么偏偏選“大學(xué)生租房”因?yàn)樾枨筇鎸?shí)了。高校周邊租房的核心場(chǎng)景是學(xué)生想找離學(xué)校近、價(jià)格合理、可以合租的房源房東手里有房但沒(méi)精力自己天天帶看中介收費(fèi)高學(xué)生承受不起。這個(gè)三方矛盾天然需要一套在線平臺(tái)來(lái)撮合房東發(fā)布房源、學(xué)生篩選房源、雙方約定時(shí)間看房。它比純粹的“商品管理系統(tǒng)”多了一層社會(huì)關(guān)系和線下交互也更好寫(xiě)出需求分析。再說(shuō)適合誰(shuí)。這個(gè)東西適合兩類人一類是做畢業(yè)設(shè)計(jì)或課程設(shè)計(jì)的學(xué)生需要快速理解一個(gè)Spring Boot完整項(xiàng)目的結(jié)構(gòu)另一類是剛?cè)腴T(mén)Java Web開(kāi)發(fā)、想找一個(gè)能模擬真實(shí)業(yè)務(wù)場(chǎng)景來(lái)練手的自學(xué)者。不管屬于哪類看完這篇文章你至少能回答三個(gè)問(wèn)題這個(gè)平臺(tái)有哪些功能、這些功能為什么這么設(shè)計(jì)、真把一個(gè)Spring Boot項(xiàng)目從零搭起來(lái)會(huì)踩哪些坑。1.1 為什么選“大學(xué)生租房”這個(gè)場(chǎng)景選題這件事很多同學(xué)想復(fù)雜了。要么選一個(gè)特別宏大的題目比如“智慧社區(qū)綜合服務(wù)平臺(tái)”結(jié)果自己根本說(shuō)不清楚哪塊是智慧要么選一個(gè)特別空洞的題目比如“基于SSM的網(wǎng)上商城”做完發(fā)現(xiàn)和網(wǎng)上的教程Demo幾乎一模一樣。大學(xué)生租房平臺(tái)的好處在于它有一個(gè)非常明確、非常好解釋的“核心痛點(diǎn)”。這個(gè)痛點(diǎn)就是信息不對(duì)稱。學(xué)生找房時(shí)最怕三件事房源是假的、價(jià)格是虛的、房東聯(lián)系不上。平臺(tái)要解決的就是把房源信息結(jié)構(gòu)化、把房東身份實(shí)名化、把看房流程線上化。換句話說(shuō)這個(gè)選題從需求層面就保證了系統(tǒng)里必須有“房源管理”“用戶管理”“預(yù)約流程”這三塊內(nèi)容不會(huì)出現(xiàn)為了湊功能而硬加模塊的情況。另一種常見(jiàn)選法是做“二手交易平臺(tái)”但二手交易涉及支付、物流、售后復(fù)雜度很容易控制不住。租房平臺(tái)可以把簽約和付款做成線下環(huán)節(jié)線上只負(fù)責(zé)信息和預(yù)約這正好落在畢業(yè)設(shè)計(jì)的合適難度區(qū)間——比純CRUD高一點(diǎn)又不至于高到做不完。這就是我常說(shuō)的選題原則業(yè)務(wù)閉環(huán)但復(fù)雜度可控。1.2 三類角色與核心功能清單租房平臺(tái)不可能只有一種用戶。最基礎(chǔ)的是三類角色學(xué)生租客、房東、管理員。如果再加一層就是“未登錄游客”游客只能瀏覽房源列表想看聯(lián)系方式或預(yù)約看房就必須注冊(cè)登錄。學(xué)生端注冊(cè)登錄、瀏覽房源、按區(qū)域/價(jià)格/戶型篩選、收藏房源、提交預(yù)約看房、查看預(yù)約狀態(tài)、查看個(gè)人收藏夾、對(duì)已看過(guò)的房源發(fā)表評(píng)價(jià)。房東端注冊(cè)登錄、發(fā)布房源標(biāo)題、描述、圖片、租金、戶型、地址、管理自己的房源下架/編輯、查看收到的預(yù)約請(qǐng)求并確認(rèn)/拒絕。管理端用戶管理禁用/啟用賬號(hào)、房源審核新發(fā)布的房源必須過(guò)審才能展示、公告管理、數(shù)據(jù)概覽用戶數(shù)、房源數(shù)、預(yù)約次數(shù)等簡(jiǎn)單統(tǒng)計(jì)。注意一個(gè)細(xì)節(jié)房源審核這個(gè)功能很有價(jià)值。它不光是業(yè)務(wù)需要還能讓你的系統(tǒng)多出一個(gè)“狀態(tài)流轉(zhuǎn)”的演示點(diǎn)寫(xiě)論文時(shí)能單獨(dú)占一節(jié)“審核機(jī)制設(shè)計(jì)”。很多同學(xué)做這類項(xiàng)目只在實(shí)體字段里加一個(gè)“狀態(tài)”數(shù)字卻不設(shè)計(jì)狀態(tài)流轉(zhuǎn)規(guī)則答辯時(shí)被問(wèn)“一個(gè)房源從發(fā)布到上架經(jīng)歷了什么狀態(tài)”就答不上來(lái)。1.3 從需求到原型的取舍思路拿到需求清單之后第一件事不是建表而是做取舍。我見(jiàn)過(guò)不少同學(xué)把功能表列得很滿今天加一個(gè)“在線聊天”明天加一個(gè)“租金計(jì)算器”最后數(shù)據(jù)庫(kù)二十多張表代碼四處漏風(fēng)。這里要有一個(gè)原則核心鏈路優(yōu)先周邊功能從簡(jiǎn)。這個(gè)項(xiàng)目的核心鏈路就是“發(fā)布房源-搜索房源-預(yù)約看房-確認(rèn)看房-評(píng)價(jià)”。所有角色和表結(jié)構(gòu)都應(yīng)該服務(wù)于這條鏈路。至于站內(nèi)信、消息通知這類屬于可有可無(wú)的加分項(xiàng)時(shí)間充裕再加。我做這個(gè)項(xiàng)目時(shí)會(huì)先畫(huà)一條最簡(jiǎn)單的業(yè)務(wù)線然后用“如果我是學(xué)生我會(huì)在哪個(gè)步驟卡住”來(lái)反推功能。比如學(xué)生看到房源想聯(lián)系房東如果平臺(tái)不提供任何聯(lián)系入口那這個(gè)平臺(tái)就廢了。所以“預(yù)約看房”不是附加功能是核心功能。原型階段不需要用Axure畫(huà)得多精致一張紙一支筆畫(huà)出每個(gè)角色的頁(yè)面流轉(zhuǎn)就行。桌面端和移動(dòng)端怎么取舍考慮到學(xué)生和房東大量使用手機(jī)界面要優(yōu)先適配移動(dòng)端但畢業(yè)設(shè)計(jì)演示通常是在電腦大屏上所以更推薦做得“響應(yīng)式”——Bootstrap或Vue加個(gè)移動(dòng)端適配兩邊都能看。這個(gè)細(xì)節(jié)寫(xiě)進(jìn)文檔里是“界面設(shè)計(jì)考慮到了多端訪問(wèn)”很加分。2. 技術(shù)選型與整體架構(gòu)Spring Boot為主線的務(wù)實(shí)方案技術(shù)選型決定一個(gè)項(xiàng)目好不好寫(xiě)完、好不好答辯。我不建議在畢業(yè)設(shè)計(jì)里追新技術(shù)更不建議“為了用而用”。一個(gè)成熟的課題方向是Spring Boot做后端MyBatis-Plus做數(shù)據(jù)訪問(wèn)MySQL存儲(chǔ)數(shù)據(jù)Redis做緩存和驗(yàn)證碼JWT做登錄鑒權(quán)Vue或Thymeleaf做前端。這套組合最大的優(yōu)點(diǎn)不是“新”而是“穩(wěn)”——網(wǎng)上資料多、社區(qū)問(wèn)答多、模板多出了問(wèn)題容易搜到答案。2.1 后端選型基于Spring Boot的版本與組件搭配Spring Boot的版本選擇是很多人的第一個(gè)坑?,F(xiàn)階段寫(xiě)新項(xiàng)目Spring Boot 2.7.x是一個(gè)比較穩(wěn)妥的選擇原因有二一是2.7屬于社區(qū)資源最豐富的版本段你搜到的大多數(shù)博客、網(wǎng)課、畢業(yè)設(shè)計(jì)模板都跑在這個(gè)版本上二是Spring Boot 3.x雖然新但底層是Jakarta EE命名空間javax變成jakarta很多舊資料里的import javax.servlet.*直接復(fù)制過(guò)來(lái)會(huì)報(bào)紅。如果你用了Spring Boot 3.x等于自己主動(dòng)放棄了大量現(xiàn)成參考資料。組件搭配上我推薦下面這個(gè)組合組件用途替代方案與說(shuō)明MyBatis-Plus數(shù)據(jù)訪問(wèn)層自帶分頁(yè)插件和條件構(gòu)造器原版MyBatis要寫(xiě)大量XMLPlus更適合快速開(kāi)發(fā)MySQL 8.x主數(shù)據(jù)庫(kù)5.7也能跑但8.x時(shí)區(qū)配置更省心Redis登錄Token存儲(chǔ)、驗(yàn)證碼緩存沒(méi)有Redis可用Session但用了Redis寫(xiě)文檔更好看JWT Spring Security接口鑒權(quán)畢設(shè)用Interceptor加JWT工具類就夠不必上Spring SecurityLombok簡(jiǎn)化實(shí)體類有爭(zhēng)議但能少寫(xiě)大量get/setHutool工具類集合驗(yàn)證碼生成、隨機(jī)數(shù)、日期處理都能用它為什么不用Spring Security因?yàn)檫@個(gè)平臺(tái)的權(quán)限模型非常簡(jiǎn)單三類角色少數(shù)幾個(gè)管理員接口需要校驗(yàn)管理員身份其余接口只要“已登錄”就行。拿Spring Security那套過(guò)濾器鏈、UserDetailsService、密碼編碼器去實(shí)現(xiàn)代碼量翻倍答辯時(shí)還容易把自己繞暈。一個(gè)自定義攔截器加JWT工具類兩千行以內(nèi)搞定權(quán)限控制效果完全夠用。2.2 前端與部署形態(tài)Vue打包放進(jìn)Spring Boot的實(shí)操前端的選擇會(huì)直接影響交付形式?!皊pringboot007大學(xué)生租房平臺(tái)”的設(shè)計(jì)通常有兩種一種是傳統(tǒng)的服務(wù)端渲染用Thymeleaf模板引擎加Bootstrap寫(xiě)完一個(gè)包直接扔進(jìn)static目錄就能跑另一種是前后端分離后端提供JSON接口前端用Vue框架開(kāi)發(fā)時(shí)前后端分兩個(gè)進(jìn)程跑交付時(shí)把Vue打包產(chǎn)物放到Spring Boot的static目錄下。第二種更像真實(shí)企業(yè)項(xiàng)目也更符合現(xiàn)在的招聘技能要求。但要注意如果把Vue打包后的dist目錄直接放到Spring Boot的src/main/resources/static下會(huì)有一個(gè)經(jīng)典問(wèn)題頁(yè)面路由是history模式時(shí)刷新某個(gè)子路徑會(huì)404。原因是Spring Boot默認(rèn)只把/映射到index.html并沒(méi)有把前端路由的路徑重新指回首頁(yè)。解決辦法也簡(jiǎn)單寫(xiě)一個(gè)WebMvcConfigurer把非接口路徑全部轉(zhuǎn)發(fā)到/index.html。另一個(gè)常見(jiàn)錯(cuò)誤是接口前綴不一致。前端Vue開(kāi)發(fā)時(shí)通過(guò)/api代理請(qǐng)求后端打包部署后沒(méi)有代理了必須保證前端請(qǐng)求的地址和后端接口實(shí)際地址一致。我的做法是后端所有接口統(tǒng)一以/api開(kāi)頭前端請(qǐng)求也直接寫(xiě)/api/...開(kāi)發(fā)時(shí)用Vite的proxy把/api轉(zhuǎn)發(fā)到http://localhost:8080部署后同源訪問(wèn)不需要改代碼。這個(gè)細(xì)節(jié)能幫你省下大量聯(lián)調(diào)時(shí)間。2.3 項(xiàng)目結(jié)構(gòu)設(shè)計(jì)按功能分包而不是按層分包很多剛接觸Spring Boot的同學(xué)會(huì)把項(xiàng)目結(jié)構(gòu)寫(xiě)成controller、service、mapper、entity四個(gè)包所有Controller堆在一起。小項(xiàng)目這么寫(xiě)問(wèn)題不大但當(dāng)一個(gè)模塊的功能增多后找代碼會(huì)變得很痛苦。租房平臺(tái)規(guī)模適中我建議按功能模塊分包c(diǎn)om.example.house ├── common # 通用工具、統(tǒng)一返回結(jié)果、異常處理 ├── config # 配置類如跨域、攔截器、緩存配置 ├── security # JWT工具、登錄攔截、角色校驗(yàn) ├── modules │ ├── user # 用戶模塊Controller、Service、Mapper、實(shí)體 │ ├── house # 房源模塊 │ ├── order # 預(yù)約模塊 │ ├── favorite # 收藏模塊 │ └── admin # 后臺(tái)管理模塊 └── HouseApplication.java按功能分包的好處是改“房源審核”時(shí)只需要打開(kāi)modules/house目錄所有相關(guān)的Controller、Service、Mapper都在里面不需要在四個(gè)大包里來(lái)回跳。寫(xiě)文檔的時(shí)候也更好描述“用戶模塊負(fù)責(zé)……房源模塊負(fù)責(zé)……”架構(gòu)圖更清晰。這個(gè)包結(jié)構(gòu)本身就是一個(gè)加分項(xiàng)說(shuō)明你有模塊化意識(shí)。3. 數(shù)據(jù)庫(kù)設(shè)計(jì)與核心表結(jié)構(gòu)房源、訂單與權(quán)限的關(guān)鍵建模數(shù)據(jù)庫(kù)設(shè)計(jì)是這個(gè)項(xiàng)目里最值得花時(shí)間的地方。表結(jié)構(gòu)定得好后面寫(xiě)代碼順風(fēng)順?biāo)斫Y(jié)構(gòu)定得爛后面每個(gè)查詢都在補(bǔ)洞。我用四張核心表展開(kāi)講用戶表、房源表、預(yù)約表、收藏表再加一張公告表作為管理端功能點(diǎn)。3.1 核心表清單與字段設(shè)計(jì)要點(diǎn)用戶表t_user要區(qū)分角色用role字段枚舉值STUDENT、LANDLORD、ADMIN。其他字段包括用戶名、密碼BCrypt加密、昵稱、手機(jī)號(hào)、頭像、學(xué)校/職業(yè)等。注意手機(jī)號(hào)在租房場(chǎng)景里是核心聯(lián)系手段必須做唯一索引。房源表t_house是最重的一張表。核心字段包括標(biāo)題、描述、封面圖、戶型幾室?guī)讖d、面積、租金元/月、出租方式整租/合租、所在區(qū)域、詳細(xì)地址、房東ID、狀態(tài)待審核/已上架/已下架、瀏覽量、創(chuàng)建時(shí)間。這里我特別提兩點(diǎn)一是租金用整數(shù)INT表示“元/月”不要用字符串“3000元/月”否則后面做價(jià)格區(qū)間查詢時(shí)沒(méi)法用數(shù)字比較二是詳細(xì)地址要單獨(dú)一個(gè)字段但列表頁(yè)只展示區(qū)域和小區(qū)名避免用戶隱私過(guò)早泄露這個(gè)點(diǎn)寫(xiě)進(jìn)文檔能體現(xiàn)安全意識(shí)。預(yù)約表t_appointment用于記錄看房預(yù)約字段包括房源ID、學(xué)生ID、預(yù)約時(shí)間、期望看房時(shí)間段、備注、狀態(tài)待確認(rèn)/已確認(rèn)/已取消/已完成、創(chuàng)建時(shí)間。預(yù)約狀態(tài)是這個(gè)模塊的核心一定要有狀態(tài)位。評(píng)價(jià)表t_review記錄學(xué)生看完房后的評(píng)價(jià)關(guān)聯(lián)預(yù)約ID或房源ID再關(guān)聯(lián)用戶ID。3.2 房源狀態(tài)與預(yù)約狀態(tài)的狀態(tài)機(jī)設(shè)計(jì)狀態(tài)機(jī)是答辯時(shí)最能展示你思考深度的部分。房源的完整狀態(tài)流轉(zhuǎn)是待審核(AUDITING)-已上架(ON_SALE)-已下架(OFF_SALE)其中待審核只能通過(guò)管理員審核變成已上架或已駁回(REJECTED)房東可以主動(dòng)把已上架下架但不能繞過(guò)審核直接上架。這里用代碼寫(xiě)一個(gè)校驗(yàn)不是很難但一定要有。預(yù)約的狀態(tài)流轉(zhuǎn)是待確認(rèn)(PENDING)-已確認(rèn)(CONFIRMED)也可以已取消(CANCELED)最后學(xué)生看房完畢由學(xué)生確認(rèn)或系統(tǒng)自動(dòng)置為已完成(FINISHED)。為什么預(yù)約要設(shè)計(jì)“待確認(rèn)”這一步因?yàn)榉繓|不一定有空必須給房東一個(gè)操作空間。很多同學(xué)這里直接設(shè)計(jì)成“學(xué)生提交預(yù)約就算約成功”這不符合真實(shí)場(chǎng)景答辯時(shí)也容易被追問(wèn)。一個(gè)實(shí)用的設(shè)計(jì)技巧是在狀態(tài)字段旁加一個(gè)status_text的展示字段前端直接讀取展示文案不用前端硬編碼狀態(tài)對(duì)應(yīng)的中文?;蛘哂肕yBatis-Plus的枚舉轉(zhuǎn)換把數(shù)據(jù)庫(kù)里的數(shù)字自動(dòng)轉(zhuǎn)成Java枚舉前端拿到的就是可讀狀態(tài)。無(wú)論如何狀態(tài)文案只允許在后端出別讓前端猜。3.3 數(shù)據(jù)訪問(wèn)層的選擇MyBatis-Plus分頁(yè)與條件構(gòu)造MyBatis-Plus是這類畢業(yè)設(shè)計(jì)項(xiàng)目的好搭檔。它內(nèi)置了BaseMapper單表CRUD幾乎不用寫(xiě)SQL分頁(yè)插件開(kāi)箱即用條件構(gòu)造器QueryWrapper能解決大部分動(dòng)態(tài)查詢。比如房源列表的篩選查詢學(xué)生可能按區(qū)域篩、按價(jià)格區(qū)間篩、按戶型篩、按“合租/整租”篩還可能同時(shí)組合。用MyBatis-Plus寫(xiě)很簡(jiǎn)潔public PageHouse searchHouse(HouseQuery query, int page, int size) { PageHouse p new Page(page, size); LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.hasText(query.getRegion()), House::getRegion, query.getRegion()) .eq(query.getRentType() ! null, House::getRentType, query.getRentType()) .between(query.getMinPrice() ! null query.getMaxPrice() ! null, House::getPrice, query.getMinPrice(), query.getMaxPrice()) .eq(House::getStatus, HouseStatus.ON_SALE) .orderByDesc(House::getCreateTime); return houseMapper.selectPage(p, wrapper); }注意這里我把“只看已上架房源”作為固定條件寫(xiě)死了防止學(xué)生刷出待審核或已下架的房源。這種細(xì)節(jié)就是安全邏輯的體現(xiàn)。另外分頁(yè)參數(shù)page和size一定要做邊界控制page最小為1size最大別超過(guò)100否則有人傳一個(gè)size999999一次把整表拉出來(lái)接口直接就慢成“演示事故”。4. 核心功能實(shí)現(xiàn)與代碼排坑實(shí)錄這一節(jié)是重點(diǎn)我把這個(gè)項(xiàng)目里最容易掉進(jìn)去的坑和對(duì)應(yīng)的實(shí)現(xiàn)思路集中講一遍。能寫(xiě)出來(lái)的都是我自己跑過(guò)、調(diào)試過(guò)、出過(guò)丑的真實(shí)經(jīng)驗(yàn)。4.1 用戶注冊(cè)登錄與JWT鑒權(quán)注冊(cè)登錄是入口但很多同學(xué)的入口代碼寫(xiě)得亂七八糟。先說(shuō)密碼絕不能明文存儲(chǔ)用BCryptPasswordEncoder加密。加密之后即使數(shù)據(jù)庫(kù)泄露攻擊者也很難反推出原始密碼。Spring Security里自帶這個(gè)類如果你沒(méi)引入Spring Security單獨(dú)引入spring-security-crypto依賴也能用。注冊(cè)時(shí)還要處理幾個(gè)問(wèn)題用戶名是否重復(fù)、手機(jī)號(hào)格式、密碼強(qiáng)度。我一般會(huì)在Service層做統(tǒng)一校驗(yàn)然后Mapper插入。接口返回統(tǒng)一用ResultT結(jié)構(gòu)比如public ResultString login(String username, String password) { User user userService.findByUsername(username); if (user null || !encoder.matches(password, user.getPassword())) { return Result.error(用戶名或密碼錯(cuò)誤); } String token jwtUtil.generateToken(user.getId(), user.getRole()); return Result.success(token); }登錄成功后返回JWT字符串前端存在localStorage里之后每個(gè)請(qǐng)求在Authorization頭帶上。后端攔截器解析Token取出用戶ID放到ThreadLocal或請(qǐng)求屬性里供后續(xù)寫(xiě)業(yè)務(wù)代碼使用。關(guān)于JWT有個(gè)坑要提示JWT默認(rèn)不加密payload是Base64明文千萬(wàn)不要把手機(jī)號(hào)、密碼之類的敏感信息塞進(jìn)去。Token里只需要用戶ID和角色需要更多信息時(shí)再查庫(kù)。4.2 房源發(fā)布與圖片上傳的坑房源發(fā)布里最鬧心的就是圖片上傳。畢設(shè)項(xiàng)目一般不用OSS對(duì)象存儲(chǔ)最常見(jiàn)的是把圖片存到本地磁盤(pán)。這里有兩個(gè)坑必須提前避開(kāi)。第一個(gè)坑是本地路徑問(wèn)題。如果直接把圖片寫(xiě)到項(xiàng)目根目錄下的upload文件夾然后以相對(duì)路徑訪問(wèn)打包成jar運(yùn)行時(shí)會(huì)發(fā)現(xiàn)圖片存到了jar包外面的臨時(shí)目錄或者根本寫(xiě)不進(jìn)去。我的做法是在application.yml里配置一個(gè)對(duì)外統(tǒng)一的存儲(chǔ)路徑比如E:/upload/或者Linux下的/data/upload/再配一個(gè)靜態(tài)資源映射Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadPath); }這樣訪問(wèn)http://localhost:8080/upload/xxx.jpg就能直接看到圖片而且圖片文件和項(xiàng)目代碼分離重打jar包也不會(huì)丟數(shù)據(jù)。第二個(gè)坑是文件類型和大小校驗(yàn)。只檢查拓展名是不可靠的一個(gè)改名成.jpg的腳本文件能直接繞過(guò)。至少做到用MimeType校驗(yàn)限制文件大小比如單張不超過(guò)5MB并把文件重命名為UUID加拓展名保存避免中文名和“同名覆蓋”問(wèn)題。安全這塊如果寫(xiě)進(jìn)文檔的“系統(tǒng)安全設(shè)計(jì)”里是個(gè)很有分量的加分點(diǎn)。4.3 搜索篩選與預(yù)約看房的關(guān)鍵代碼搜索篩選上一節(jié)展示過(guò)了這里重點(diǎn)說(shuō)預(yù)約看房的流程實(shí)現(xiàn)。學(xué)生選擇“預(yù)約看房”時(shí)前端提交房源ID、預(yù)約日期、時(shí)間段、備注。后端要校驗(yàn)四件事房源存在且已上架當(dāng)前用戶已登錄且角色是學(xué)生該房源沒(méi)有被當(dāng)前用戶重復(fù)提交待確認(rèn)的預(yù)約預(yù)約時(shí)間不能是過(guò)去。校驗(yàn)通過(guò)后插入一條PENDING狀態(tài)的預(yù)約記錄。public ResultVoid createAppointment(AppointmentRequest req, Long studentId) { House house houseMapper.selectById(req.getHouseId()); if (house null || house.getStatus() ! ON_SALE) { return Result.error(房源不存在或不可預(yù)約); } long count appointmentMapper.selectCount(new LambdaQueryWrapperAppointment() .eq(Appointment::getHouseId, req.getHouseId()) .eq(Appointment::getStudentId, studentId) .eq(Appointment::getStatus, PENDING)); if (count 0) { return Result.error(你已預(yù)約過(guò)該房源請(qǐng)等待房東確認(rèn)); } if (req.getAppointmentDate().isBefore(LocalDate.now())) { return Result.error(預(yù)約時(shí)間不能早于今天); } // 插入預(yù)約記錄 return Result.success(); }房東端看到預(yù)約請(qǐng)求后可以選擇“確認(rèn)”或“拒絕”。確認(rèn)時(shí)把狀態(tài)改為CONFIRMED同時(shí)給學(xué)生做一個(gè)站內(nèi)通知或發(fā)送一條系統(tǒng)消息這塊用一張簡(jiǎn)單的t_message表就行不一定要上WebSocket。這套交互做完項(xiàng)目在業(yè)務(wù)上就是一個(gè)完整的閉環(huán)了。5. 文檔寫(xiě)作與答辯準(zhǔn)備畢設(shè)項(xiàng)目不能只交代碼很多同學(xué)把精力全部放在寫(xiě)代碼上文檔最后三天趕工結(jié)果代碼能跑但論文質(zhì)量差答辯照樣被批。我必須強(qiáng)調(diào)這個(gè)題目的交付物是“文檔源碼”兩樣缺一不可。文檔不是代碼的說(shuō)明書(shū)它要回答“你為什么做這個(gè)系統(tǒng)”“系統(tǒng)怎么設(shè)計(jì)”“遇到什么問(wèn)題怎么解決”。5.1 論文/文檔怎么寫(xiě)才不像湊字?jǐn)?shù)常見(jiàn)的畢業(yè)設(shè)計(jì)文檔結(jié)構(gòu)是選題背景與意義、國(guó)內(nèi)外研究現(xiàn)狀、需求分析、系統(tǒng)設(shè)計(jì)、數(shù)據(jù)庫(kù)設(shè)計(jì)、功能實(shí)現(xiàn)、系統(tǒng)測(cè)試、總結(jié)與展望。這套結(jié)構(gòu)本身沒(méi)問(wèn)題但大多數(shù)同學(xué)寫(xiě)出來(lái)的內(nèi)容充滿了廢話。比如“選題背景”不要寫(xiě)“隨著互聯(lián)網(wǎng)的發(fā)展人們的生活水平提高了”一開(kāi)口就知道是拼湊的應(yīng)該寫(xiě)清楚你這個(gè)平臺(tái)面向的具體人群和具體痛點(diǎn)。哪怕這樣寫(xiě)都更好“高校周邊租房市場(chǎng)長(zhǎng)期存在信息不對(duì)稱問(wèn)題學(xué)生找房成本高、房東發(fā)布渠道分散、看房時(shí)間難以協(xié)調(diào)。針對(duì)以上問(wèn)題本課題設(shè)計(jì)并實(shí)現(xiàn)一個(gè)面向高校學(xué)生和房東的租房信息平臺(tái)通過(guò)線上房源審核與預(yù)約看房機(jī)制降低租房交易的信息門(mén)檻?!毙枨蠓治霾糠植灰毁N用例圖還要畫(huà)數(shù)據(jù)流圖把“學(xué)生-平臺(tái)-房東”三方的信息交互流程講清楚。系統(tǒng)設(shè)計(jì)部分要有總體架構(gòu)圖和各模塊設(shè)計(jì)。功能實(shí)現(xiàn)部分挑至少3-4個(gè)核心功能詳細(xì)展開(kāi)比如JWT鑒權(quán)流程、房源審核流程、價(jià)格區(qū)間篩選的實(shí)現(xiàn)思路配上核心代碼和運(yùn)行截圖。5.2 答辯時(shí)的高頻提問(wèn)與應(yīng)答思路答辯老師不一定會(huì)把項(xiàng)目跑一遍但他們一定會(huì)問(wèn)“所以你是怎么做的”和“為什么這么做”。常見(jiàn)問(wèn)題我列一下你們提前準(zhǔn)備為什么選Spring Boot而不是SSH/SSM答Spring Boot簡(jiǎn)化了配置、內(nèi)置了Tomcat、生態(tài)成熟適合快速構(gòu)建獨(dú)立服務(wù)且在求職市場(chǎng)上Spring Boot是主流要求。你的系統(tǒng)是如何保證安全的答密碼BCrypt加密、JWT無(wú)狀態(tài)鑒權(quán)、管理員審核房源、上傳文件類型與大小校驗(yàn)、SQL走M(jìn)yBatis-Plus參數(shù)化避免字符串拼接SQL。如果并發(fā)量上來(lái)了系統(tǒng)瓶頸在哪答當(dāng)前是單體架構(gòu)瓶頸會(huì)在數(shù)據(jù)庫(kù)的房源表和預(yù)約表的寫(xiě)入與查詢上可以加索引緩存后續(xù)可引入Redis緩存熱門(mén)房源列表預(yù)約邏輯可以異步化。為什么預(yù)約要設(shè)計(jì)“待確認(rèn)”狀態(tài)答因?yàn)榉繓|時(shí)間不確定必須給予確認(rèn)環(huán)節(jié)保證雙方信息對(duì)稱。答不出來(lái)的老老實(shí)實(shí)說(shuō)“這個(gè)當(dāng)時(shí)沒(méi)有深入考慮但我的想法是……”比硬編一個(gè)答案強(qiáng)得多。千萬(wàn)不要背答案老師一眼就能看出來(lái)。5.3 項(xiàng)目二次擴(kuò)展把源碼變成自己的東西很多同學(xué)拿到一套“大學(xué)生租房平臺(tái)”源碼第一反應(yīng)是改個(gè)名字提交。這是最危險(xiǎn)的做法因?yàn)閿?shù)據(jù)庫(kù)里可能還留著原作者的測(cè)試數(shù)據(jù)、包名、logo甚至Git提交歷史。至少要做四件事全局替換包名和項(xiàng)目名、清空或重置數(shù)據(jù)庫(kù)、刪除殘留無(wú)用的圖片和測(cè)試數(shù)據(jù)、把代碼從頭到尾讀一遍并注釋理解。讀一遍也不是瞎讀。我建議按“請(qǐng)求進(jìn)入-攔截器校驗(yàn)-Controller- Service- Mapper- SQL”這條鏈路把一個(gè)核心流程完整追一遍比如“學(xué)生提交預(yù)約看房的完整調(diào)用鏈”。追完之后再改兩三個(gè)自己感興趣的功能比如加一個(gè)“地圖選房”頁(yè)面或者加一個(gè)“房東收入統(tǒng)計(jì)”的報(bào)表。哪怕只是很小的改動(dòng)答辯時(shí)你說(shuō)“我在原基礎(chǔ)上增加了XXX”底氣都不一樣。6. 常見(jiàn)問(wèn)題速查表與避坑清單最后貢獻(xiàn)一份實(shí)用的避坑清單。這里面的問(wèn)題我?guī)缀趺總€(gè)都在真實(shí)項(xiàng)目里見(jiàn)過(guò)按照“環(huán)境搭建期、編碼實(shí)現(xiàn)期、部署演示期”三個(gè)階段整理遇到直接查表。6.1 環(huán)境搭建期的高頻問(wèn)題問(wèn)題現(xiàn)象原因與解決啟動(dòng)報(bào)Failed to configure a DataSource沒(méi)有配置或沒(méi)有識(shí)別到數(shù)據(jù)源。檢查application.yml里spring.datasource配置是否正確注意MySQL驅(qū)動(dòng)坐標(biāo)有沒(méi)有引入Spring Boot版本太高引入老依賴直接報(bào)錯(cuò)Spring Boot 3.x對(duì)javax改名為jakarta老依賴可能不兼容。建議用2.7.x或每引入一個(gè)依賴先看它是否聲明支持Spring Boot 3端口被占用啟動(dòng)后訪問(wèn)不到改server.port或者殺掉占用進(jìn)程。Windows下查端口netstat -ano前端請(qǐng)求接口CORS報(bào)錯(cuò)開(kāi)發(fā)時(shí)配置跨域。寫(xiě)一個(gè)WebMvcConfigureraddCorsMappings允許本地前端地址訪問(wèn)或者按前文統(tǒng)一/api前綴部署后同源無(wú)跨域這里單獨(dú)說(shuō)一說(shuō)“Spring Boot版本太高”這個(gè)問(wèn)題。我見(jiàn)過(guò)太多同學(xué)從官網(wǎng)直接拉最新版3.3、3.4然后開(kāi)始百度找各種starter結(jié)果發(fā)現(xiàn)老教程的配置都不生效。不是說(shuō)新版本不好而是畢業(yè)設(shè)計(jì)不應(yīng)該把時(shí)間花在和版本搏斗上。用2.7.x踩坑成本最低。6.2 編碼實(shí)現(xiàn)期的經(jīng)典坑問(wèn)題現(xiàn)象原因與解決時(shí)間字段返回?cái)?shù)組或奇怪字符串默認(rèn)JSON序列化對(duì)LocalDateTime支持不友好。統(tǒng)一用Jackson配置全局日期格式或在字段上加JsonFormat圖片上傳后訪問(wèn)404檢查靜態(tài)資源映射是否配置檢查存儲(chǔ)路徑是否帶了文件名檢查路徑中是否有中文或空格建議全部重命名為UUID分頁(yè)數(shù)據(jù)對(duì)但總數(shù)不對(duì)MyBatis-Plus分頁(yè)插件沒(méi)有注冊(cè)。需要配置MybatisPlusInterceptor并添加PaginationInnerInterceptor刪除用戶時(shí)外鍵報(bào)錯(cuò)需要先刪關(guān)聯(lián)數(shù)據(jù)或者刪用戶時(shí)做“邏輯刪除”用deleted字段標(biāo)記而不是物理刪除邏輯刪除這件事強(qiáng)烈建議做。用戶表、房源表、預(yù)約表都加一個(gè)deleted字段查詢時(shí)自動(dòng)過(guò)濾。好處有兩個(gè)一是數(shù)據(jù)不真丟誤刪還能恢復(fù)二是SQL自帶where deleted 0對(duì)答辯時(shí)“數(shù)據(jù)安全”的提問(wèn)也有東西可講。6.3 部署與演示期的注意事項(xiàng)最后的演示環(huán)節(jié)翻車(chē)最不值得。提前做好三件事第一數(shù)據(jù)庫(kù)要準(zhǔn)備一批真實(shí)感強(qiáng)的測(cè)試數(shù)據(jù)房源圖片不要用網(wǎng)上隨機(jī)抓的模糊圖至少十幾套清晰圖片房源地址起“大學(xué)城地鐵站附近”“圖書(shū)館東門(mén)500米”這類真實(shí)的校園周邊位置第二演示前把項(xiàng)目啟動(dòng)好瀏覽器直接打開(kāi)頁(yè)面別在臺(tái)上等著五六秒的啟動(dòng)過(guò)程第三準(zhǔn)備一條“演示腳本”從學(xué)生注冊(cè)、搜索房源、預(yù)約看房到房東登錄、確認(rèn)預(yù)約再到管理員審核房源完整走一遍控制在3分鐘以內(nèi)。圖片資源這塊我多說(shuō)一句。很多畢設(shè)的房源圖片一眼假全是同一張占位圖。花點(diǎn)時(shí)間找真實(shí)的房屋照片或者自己用手機(jī)拍幾張會(huì)讓整個(gè)項(xiàng)目在答辯時(shí)的質(zhì)感完全不一樣。“細(xì)節(jié)到位”在畢設(shè)評(píng)價(jià)里從來(lái)都是加分項(xiàng)。最后再分享一個(gè)小經(jīng)驗(yàn)做完項(xiàng)目之后一定把你寫(xiě)過(guò)的核心代碼按模塊整理成幾篇帶注釋的Markdown筆記配上表和結(jié)構(gòu)圖。這不只是給文檔用也是你面試談項(xiàng)目時(shí)的提詞器。把“大學(xué)生租房平臺(tái)”講清楚、講深一層比簡(jiǎn)歷上寫(xiě)十個(gè)“熟練使用”都管用。如果你現(xiàn)在正卡在某個(gè)環(huán)境問(wèn)題或編碼小坑里對(duì)照這份避坑清單再試一次多半就能過(guò)去。