站畢設(shè)項(xiàng)目全解析:從Vue前端到Spring Boot后端)
簡(jiǎn)介這是一套面向計(jì)算機(jī)專業(yè)本科生的畢業(yè)設(shè)計(jì)與期末大作業(yè)實(shí)戰(zhàn)資源聚焦游戲攻略網(wǎng)站的全流程開(kāi)發(fā)實(shí)踐解決學(xué)生缺乏可運(yùn)行、可擴(kuò)展、文檔完備的SSMVue全棧項(xiàng)目參考的痛點(diǎn)。資源包共774個(gè)文件24.08MB涵蓋156個(gè)JavaScript前端邏輯文件、87個(gè)Java后端業(yè)務(wù)代碼、38個(gè)Vue組件、46個(gè)CSS樣式文件、2個(gè)SQL數(shù)據(jù)庫(kù)腳本及2個(gè)Word論文文檔輔以SVG圖標(biāo)、GIF動(dòng)效、MP4演示視頻等增強(qiáng)可讀性與實(shí)用性。已有35人學(xué)習(xí)下載適合中等技術(shù)基礎(chǔ)的學(xué)習(xí)者快速上手——不僅提供本地已編譯通過(guò)、含完整注釋的前后端源碼還包含系統(tǒng)設(shè)計(jì)文檔、用戶手冊(cè)、E-R圖、數(shù)據(jù)表結(jié)構(gòu)說(shuō)明及bat一鍵啟停腳本如2-run.bat目錄模塊清晰含IndexHeader、BreadCrumbs等標(biāo)準(zhǔn)化布局組件便于理解工程化組織方式與響應(yīng)式交互實(shí)現(xiàn)邏輯。 去年幫一個(gè)學(xué)弟審過(guò)一套“游戲攻略網(wǎng)站”的畢設(shè)項(xiàng)目壓縮包名字就叫“游戲攻略網(wǎng)站的設(shè)計(jì)與實(shí)現(xiàn)vue源碼、論文、說(shuō)明文檔、數(shù)據(jù)庫(kù)文檔.zip”。說(shuō)實(shí)話這種打包形式一看就是典型的畢設(shè)/課設(shè)交付物但里面東西的完整度比多數(shù)人自己趕工出來(lái)的要高不少前端Vue、后端接口、數(shù)據(jù)庫(kù)腳本、論文文檔全齊。這篇文章就基于這類項(xiàng)目把游戲攻略網(wǎng)站從前端到后端、從數(shù)據(jù)庫(kù)到文檔材料拆開(kāi)講清楚給正在做類似項(xiàng)目或者想拿這種項(xiàng)目練手的人一個(gè)能直接參考的路線。如果你是準(zhǔn)備做畢設(shè)、打算接外包項(xiàng)目、或者單純想通過(guò)一個(gè)完整的前后端分離項(xiàng)目入門Vue實(shí)戰(zhàn)這篇都值得看完。我會(huì)先把需求定位講清楚然后說(shuō)技術(shù)選型再走一遍數(shù)據(jù)庫(kù)設(shè)計(jì)、核心接口實(shí)現(xiàn)最后聊一聊最容易卡住人的部署問(wèn)題和論文/文檔怎么配合著寫。1. 為什么這類項(xiàng)目一直有人做需求拆解與功能邊界游戲攻略網(wǎng)站不是個(gè)新鮮概念從早期的NGA、游民星空到現(xiàn)在各種垂直攻略站本質(zhì)都是內(nèi)容社區(qū)。但放到畢設(shè)、課設(shè)場(chǎng)景里“游戲攻略網(wǎng)站”其實(shí)是一種很穩(wěn)定的項(xiàng)目原型它既有前臺(tái)內(nèi)容展示又有后臺(tái)管理還牽扯用戶體系、內(nèi)容生產(chǎn)、交互行為能覆蓋一個(gè)完整Web系統(tǒng)的大部分知識(shí)點(diǎn)但又不會(huì)復(fù)雜到一個(gè)人做不完。真正動(dòng)手之前應(yīng)該先把功能邊界畫清楚這決定了你后面建表、寫接口、畫原型的工作量。1.1 用戶角色與權(quán)限邊界游戲攻略網(wǎng)站通常分三種角色游客、注冊(cè)用戶、管理員。很多新手拿到項(xiàng)目就直接開(kāi)寫代碼結(jié)果到最后又說(shuō)權(quán)限亂了、后臺(tái)進(jìn)不去根源就是一開(kāi)始沒(méi)把這三類人的能做什么界定好。游客瀏覽首頁(yè)、看攻略列表、搜攻略、打開(kāi)攻略詳情。注冊(cè)用戶在游客基礎(chǔ)上可以登錄能點(diǎn)贊、收藏、評(píng)論也能在個(gè)人中心發(fā)攻略、管理自己發(fā)的攻略。管理員擁有后臺(tái)管理頁(yè)面能審核/下架攻略、管理分類、管理用戶、刪評(píng)論。這里有個(gè)容易被忽視的點(diǎn)攻略發(fā)布需不需要審核很多簡(jiǎn)單項(xiàng)目會(huì)把用戶發(fā)的攻略直接上架省掉審核這一步。但做游戲攻略站內(nèi)容質(zhì)量參差不齊管理員審核是個(gè)很合理也很有存在感的功能寫論文時(shí)還能多寫一章節(jié)建議保留。1.2 核心功能清單按模塊拆分大概是下表這樣模塊子功能說(shuō)明用戶模塊注冊(cè)、登錄、退出、修改資料登錄用Token認(rèn)證密碼加密存儲(chǔ)攻略模塊攻略列表、搜索、按分類篩選、攻略詳情、發(fā)布/編輯/刪除攻略詳情頁(yè)展示富文本內(nèi)容支持瀏覽量統(tǒng)計(jì)互動(dòng)模塊點(diǎn)贊、收藏、評(píng)論、回復(fù)評(píng)論防止重復(fù)點(diǎn)贊/收藏需要有唯一約束分類模塊分類展示、分類管理前臺(tái)按分類刷攻略后臺(tái)維護(hù)分類后臺(tái)管理攻略審核、用戶管理、評(píng)論管理、數(shù)據(jù)統(tǒng)計(jì)獨(dú)立路由管理端與前臺(tái)分離輔助功能瀏覽量統(tǒng)計(jì)、熱門排行、最新發(fā)布常見(jiàn)但很加分的點(diǎn)圖片上傳也要考慮。攻略內(nèi)容里的配圖、用戶頭像如果不上傳圖片只填URL功能會(huì)弱不少。建議把圖片上傳做成獨(dú)立接口前端用Element Plus的Upload組件對(duì)接后端存本地靜態(tài)目錄或OSS畢設(shè)場(chǎng)景存本地就夠了。1.3 為什么功能邊界要提前鎖定我見(jiàn)過(guò)太多做這類項(xiàng)目的人需求文檔寫得模棱兩可做完主流程后又想加這加那最后設(shè)計(jì)論文里的功能列表和實(shí)際系統(tǒng)對(duì)不上。鎖邊界不是為了限制你是為了讓項(xiàng)目在有限時(shí)間內(nèi)可交付。游戲攻略網(wǎng)站的核心是“內(nèi)容生產(chǎn)內(nèi)容消費(fèi)”圍繞這個(gè)主軸把攻略、分類、用戶、互動(dòng)做扎實(shí)次要功能私信、好友、充值等都可以砍掉它們對(duì)評(píng)分和面試幫助不大反而會(huì)拖慢進(jìn)度。2. 技術(shù)棧選型的邏輯Vue 3 Spring Boot這套組合為什么經(jīng)久不衰“vue”出現(xiàn)在項(xiàng)目標(biāo)題里說(shuō)明這套項(xiàng)目的前端選型是Vue。Vue在國(guó)內(nèi)教學(xué)和畢設(shè)場(chǎng)景里的統(tǒng)治地位由來(lái)已久中文資料全、上手平滑、模板語(yǔ)法對(duì)后端同學(xué)友好Vue 3 Element Plus的組件生態(tài)又非常成熟。2.1 前端技術(shù)棧具體怎么搭一套標(biāo)準(zhǔn)的Vue 3前端通常包含這幾樣Vue 3Composition API script setupVue Router 4路由管理與路由守衛(wèi)Pinia狀態(tài)管理替代Vuex實(shí)現(xiàn)用戶狀態(tài)共享Element PlusUI組件庫(kù)AxiosHTTP請(qǐng)求封裝ECharts后臺(tái)數(shù)據(jù)統(tǒng)計(jì)圖表Node版本建議用16或18Vite構(gòu)建比Webpack快很多配好鏡像源后npm install基本不會(huì)卡。2.2 后端技術(shù)棧的選擇后端選Spring Boot是最主流的方案。Vue做前端、Spring Boot做接口、MySQL存數(shù)據(jù)這套前后端分離架構(gòu)是當(dāng)前工作里最常見(jiàn)的一種形態(tài)做畢設(shè)或者面試講項(xiàng)目都可以直接套用。Spring Boot 2.7.x 或 3.x根據(jù)自己JDK版本選JDK8用2.xJDK17用3.xMyBatis-Plus簡(jiǎn)化單表CRUD分頁(yè)好用MySQL 5.7 或 8.0JWT做登錄認(rèn)證Lombok減少實(shí)體類樣板代碼這套技術(shù)棧不需要額外寫太多重復(fù)代碼MyBatis-Plus可以扛住90%的單表操作聯(lián)表查詢自己寫SQL即可。2.3 選型的“為什么”比“是什么”更重要寫論文、準(zhǔn)備答辯的時(shí)候老師大概率會(huì)問(wèn)“你為什么選這套框架”。如果你自己心里沒(méi)有答案現(xiàn)場(chǎng)會(huì)非常拉胯。比如為什么用Vue不用React因?yàn)閂ue的模板語(yǔ)法對(duì)新手更友好、社區(qū)資料中文豐富、國(guó)內(nèi)企業(yè)使用率也高。為什么用MyBatis-Plus不用JPA因?yàn)镸yBatis-Plus學(xué)習(xí)成本低、分頁(yè)好用、SQL可控性強(qiáng)畢設(shè)項(xiàng)目里性能要求不高反而需要把SQL邏輯寫清楚給老師看。為什么用JWT不用Session因?yàn)榍昂蠖朔蛛x后后端不存會(huì)話狀態(tài)Token無(wú)狀態(tài)、擴(kuò)展性好、移動(dòng)端也能用同一套接口。這些理由不是背出來(lái)的而是你真的理解了這套選型的取舍后自然能說(shuō)出來(lái)的東西。技術(shù)選型沒(méi)有絕對(duì)好壞能解釋清楚“為什么”就成功了一半。3. 數(shù)據(jù)庫(kù)建模攻略網(wǎng)站的核心表結(jié)構(gòu)與設(shè)計(jì)思路數(shù)據(jù)庫(kù)設(shè)計(jì)是這類項(xiàng)目最見(jiàn)功力的部分。一套合理的表結(jié)構(gòu)能讓前后端開(kāi)發(fā)順暢很多表設(shè)計(jì)不合理寫SQL的時(shí)候就會(huì)到處不得勁。這里分享一套我驗(yàn)證過(guò)多次的設(shè)計(jì)方案。3.1 核心表清單與字段說(shuō)明一般來(lái)說(shuō)游戲攻略網(wǎng)站至少要包含以下這些表用戶表 user字段類型說(shuō)明idbigint主鍵自增usernamevarchar(50)用戶名唯一passwordvarchar(100)BCrypt加密后的密碼nicknamevarchar(50)昵稱avatarvarchar(255)頭像URLroletinyint1-用戶2-管理員statustinyint0-禁用1-正常create_timedatetime注冊(cè)時(shí)間分類表 category字段類型說(shuō)明idbigint主鍵namevarchar(50)分類名如“角色扮演”“射擊競(jìng)技”sortint排序值create_timedatetime創(chuàng)建時(shí)間攻略表 article字段類型說(shuō)明idbigint主鍵titlevarchar(100)標(biāo)題covervarchar(255)封面圖contentlongtext富文本正文summaryvarchar(255)摘要category_idbigint所屬分類user_idbigint作者IDview_countint瀏覽量冗余字段like_countint點(diǎn)贊數(shù)冗余字段favorite_countint收藏?cái)?shù)冗余字段statustinyint0-待審核1-已發(fā)布2-已下架create_timedatetime發(fā)布時(shí)間update_timedatetime更新時(shí)間評(píng)論表 comment字段類型說(shuō)明idbigint主鍵article_idbigint攻略IDuser_idbigint評(píng)論人contentvarchar(500)評(píng)論內(nèi)容parent_idbigint父評(píng)論ID用于樓中樓回復(fù)create_timedatetime評(píng)論時(shí)間點(diǎn)贊表 like_record、收藏表 favorite結(jié)構(gòu)類似主鍵自增加user_id、article_id、create_time并且對(duì)兩列建唯一索引uk_user_article(user_id, article_id)。這個(gè)唯一索引用處很大能防止同一用戶重復(fù)點(diǎn)贊/收藏。3.2 設(shè)計(jì)時(shí)的幾個(gè)關(guān)鍵決策首先是冗余計(jì)數(shù)字段。瀏覽數(shù)、點(diǎn)贊數(shù)、收藏?cái)?shù)直接存在article表里而不是每次實(shí)時(shí)count。攻略站的數(shù)據(jù)是讀多寫少列表頁(yè)要展示這些數(shù)字如果每條記錄都left join統(tǒng)計(jì)SQL會(huì)非常繁瑣。每次點(diǎn)贊或收藏時(shí)對(duì)對(duì)應(yīng)字段做1/-1即可代價(jià)極小收益卻很直觀。其次是邏輯刪除 vs 物理刪除。用戶刪除自己發(fā)的攻略、后臺(tái)下架攻略建議用status狀態(tài)字段控制而不是真刪行。原因有二一是評(píng)論表還引用著article_id物理刪除會(huì)導(dǎo)致評(píng)論成為孤兒數(shù)據(jù)二是保留數(shù)據(jù)有利于論文里寫數(shù)據(jù)統(tǒng)計(jì)和分析。類似地用戶禁用用status控制也不建議物理刪。第三是外鍵問(wèn)題。很多教材會(huì)教你建物理外鍵但實(shí)際工程里反而很少用。我建議表設(shè)計(jì)上不建物理外鍵只建普通索引邏輯關(guān)聯(lián)在代碼層保證。原因很簡(jiǎn)單ORM框架和分庫(kù)分表場(chǎng)景下物理外鍵會(huì)帶來(lái)額外約束和維護(hù)成本畢設(shè)項(xiàng)目尤其沒(méi)必要。但是ER圖里要把邏輯關(guān)系畫清楚論文里數(shù)據(jù)庫(kù)設(shè)計(jì)部分會(huì)需要。第四是索引設(shè)計(jì)。article表的category_id、create_time建索引支持分類篩選和按時(shí)間排序status字段也要索引因?yàn)榍芭_(tái)只查已發(fā)布的攻略。搜索功能如果用title LIKE %關(guān)鍵字%走不了索引但數(shù)據(jù)量不大時(shí)可接受。3.3 初始化數(shù)據(jù)怎么造光有表結(jié)構(gòu)不夠開(kāi)發(fā)階段還得有演示數(shù)據(jù)。我的做法是寫一個(gè)data.sql或者直接在項(xiàng)目啟動(dòng)時(shí)用CommandLineRunner插入初始數(shù)據(jù)。內(nèi)容包括一個(gè)管理員賬號(hào)、十幾個(gè)普通用戶、5~8個(gè)游戲分類、每個(gè)分類下3~5篇攻略文章。攻略文章的富文本內(nèi)容不能是空殼得寫點(diǎn)像樣的游戲攻略正文??梢詮挠螒蚬倬W(wǎng)、百科找一些公開(kāi)的資料整理成幾段帶小標(biāo)題、帶列表格式的內(nèi)容這樣前臺(tái)詳情頁(yè)渲染出來(lái)才好看截圖和演示視頻也有說(shuō)服力。千萬(wàn)別拿100字的占位符糊弄答辯的時(shí)候老師下拉頁(yè)面看到內(nèi)容很干印象分直接降一個(gè)檔次。4. 前端Vue實(shí)現(xiàn)的關(guān)鍵環(huán)節(jié)路由守衛(wèi)、富文本與組件通信前端是整個(gè)項(xiàng)目的門面也是Vue技術(shù)的核心展示區(qū)。這一節(jié)挑幾個(gè)最容易出錯(cuò)也最值得寫進(jìn)技術(shù)亮點(diǎn)的部分展開(kāi)。4.1 路由設(shè)計(jì)與權(quán)限控制Vue Router 4的路由表建議分成三塊公開(kāi)路由、需要登錄的路由、管理端路由。公開(kāi)路由包括首頁(yè)攻略列表、攻略詳情、搜索結(jié)果、登錄頁(yè)、注冊(cè)頁(yè)。需要登錄的路由包括個(gè)人中心、發(fā)布攻略、我的收藏。管理端路由統(tǒng)一掛在/admin路徑下單獨(dú)做一套后臺(tái)布局。權(quán)限控制通過(guò)路由守衛(wèi)實(shí)現(xiàn)router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path.startsWith(/admin)) { const user JSON.parse(localStorage.getItem(userInfo) || {}) if (token user.role 2) { next() } else { next(/login) } } else if (to.meta.requiresAuth !token) { next(/login) } else { next() } })這段代碼的邏輯是訪問(wèn)管理端必須同時(shí)滿足“已登錄”和“是管理員”兩個(gè)條件訪問(wèn)個(gè)人中心、發(fā)布頁(yè)等需要登錄的頁(yè)面沒(méi)token就踢回登錄頁(yè)。白屏問(wèn)題也要注意。刷新頁(yè)面時(shí)如果動(dòng)態(tài)路由沒(méi)有重新加載會(huì)出現(xiàn)“刷新后404”的經(jīng)典坑。處理辦法是路由守衛(wèi)里做動(dòng)態(tài)路由復(fù)檢或者干脆不使用動(dòng)態(tài)路由而用后端返回的按鈕權(quán)限來(lái)控制頁(yè)面內(nèi)容。畢設(shè)場(chǎng)景用固定路由表加守衛(wèi)控制就夠了復(fù)雜度低、演示穩(wěn)定。4.2 富文本編輯與回顯攻略編輯是前端的一個(gè)技術(shù)亮點(diǎn)。直接用textarea寫純文本效果太差展示不了“圖文并茂”的攻略頁(yè)。要用富文本編輯器。目前Vue 3下比較好用的是wangEditor和Quill。wangEditor的坑在于對(duì)分辨率比較敏感如果你按官方demo配置完字很小多半是樣式?jīng)]引入完整。Quill的坑則在工具欄定制和圖片上傳回調(diào)。我的推薦是wangEditornpm install wangeditor/editor npm install wangeditor/editor-for-vuenext編輯器組件里核心處理兩件事一是圖片上傳回調(diào)上傳完把返回的圖片URL插到編輯區(qū)二是編輯回顯時(shí)把HTML字符串賦值給編輯器注意用editor.setHtml()而不是v-model直接綁。富文本回顯到詳情頁(yè)時(shí)用v-html。這里有一個(gè)安全坑如果模板渲染script標(biāo)簽里的內(nèi)容存在XSS風(fēng)險(xiǎn)。雖然富文本圖片地址和正文來(lái)自用戶提交但上線項(xiàng)目要用dompurify做白名單過(guò)濾。畢設(shè)項(xiàng)目可以簡(jiǎn)單提一下寫論文時(shí)作為系統(tǒng)安全性的一個(gè)點(diǎn)寫出來(lái)。4.3 點(diǎn)贊、收藏與評(píng)論的交互狀態(tài)攻略詳情頁(yè)的點(diǎn)贊和收藏交互上有一個(gè)注意點(diǎn)頁(yè)面刷新后要能正確回顯“我是否已經(jīng)點(diǎn)贊/收藏”。做法是詳情接口返回兩個(gè)字段liked當(dāng)前用戶是否已點(diǎn)贊、favorited是否已收藏。后端根據(jù)當(dāng)前登錄用戶ID去查like_record和favorite表避免前端拿本地狀態(tài)硬湊。按鈕的異步處理也很關(guān)鍵。用戶快速連點(diǎn)兩次點(diǎn)贊按鈕如果前端沒(méi)有加節(jié)流或loading狀態(tài)會(huì)發(fā)出兩次請(qǐng)求第二次可能因?yàn)槲ㄒ凰饕苯訄?bào)錯(cuò)。我的做法是點(diǎn)擊后先禁用按鈕等接口返回成功再更新?tīng)顟B(tài)、恢復(fù)點(diǎn)擊。這個(gè)小細(xì)節(jié)很加用戶體驗(yàn)分答辯演示的時(shí)候也不會(huì)尷尬。4.4 組件通信的幾個(gè)常見(jiàn)場(chǎng)景很多前端新手會(huì)在組件通信上卡殼。實(shí)際開(kāi)發(fā)中總結(jié)下來(lái)就三種場(chǎng)景父子組件傳參props向下傳emit向上通知。比如首頁(yè)列表組件把當(dāng)前分頁(yè)數(shù)據(jù)傳給分頁(yè)組件分頁(yè)組件切換頁(yè)碼時(shí)emit給父組件重新發(fā)請(qǐng)求。非父子組件通信用Pinia。比如登錄成功后頭像欄和用戶信息彈窗都要更新用Pinia存userInfo兩個(gè)組件分別從store讀取即可。刷新頁(yè)面數(shù)據(jù)同步個(gè)人中心修改昵稱后導(dǎo)航欄應(yīng)該同步顯示新昵稱。把用戶信息放Pinia配合localStorage持久化任何組件里改完store就能自動(dòng)同步。4.5 搜索欄的防抖處理搜索功能如果做成輸入一個(gè)字就請(qǐng)求一次既浪費(fèi)資源又容易觸發(fā)競(jìng)態(tài)。用watch加防抖import { watch, ref } from vue import { useDebounceFn } from vueuse/core const keyword ref() const search useDebounceFn(() { fetchSearchList(keyword.value) }, 500) watch(keyword, search)輸入停止500毫秒后才發(fā)請(qǐng)求體驗(yàn)順暢代碼量也不大。5. 后端核心模塊拆解從登錄鑒權(quán)到攻略發(fā)布接口后端部分沿著一個(gè)請(qǐng)求的完整鏈路來(lái)部署實(shí)現(xiàn)前端發(fā)請(qǐng)求到ControllerController調(diào)ServiceService調(diào)MapperMapper操作數(shù)據(jù)庫(kù)結(jié)果原路返回。5.1 統(tǒng)一返回體與全局異常處理所有接口統(tǒng)一返回結(jié)構(gòu)是后端開(kāi)發(fā)的基本功Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(操作成功); r.setData(data); return r; } public static T ResultT error(Integer code, String message) { ResultT r new Result(); r.setCode(code); r.setMessage(message); return r; } }配合RestControllerAdvice做全局異常處理業(yè)務(wù)代碼里拋BusinessException統(tǒng)一由處理器轉(zhuǎn)成JSON返回給前端。這樣做的好處是前端Axios攔截器可以統(tǒng)一判斷code不用每個(gè)接口單獨(dú)寫錯(cuò)誤處理。Axios攔截器典型寫法service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message) if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message)) } return res }, error { Message.error(error.message) return Promise.reject(error) } )401統(tǒng)一踢回登錄頁(yè)前端只需要在意請(qǐng)求成功的數(shù)據(jù)錯(cuò)誤一律交給攔截器。5.2 登錄鑒權(quán)JWT 攔截器登錄流程是后端最核心的模塊。用戶提交用戶名密碼后端先查用戶是否存在、密碼是否正確PasswordEncoder.matches驗(yàn)證BCrypt密文驗(yàn)證通過(guò)后生成JWT存到本地指定過(guò)期時(shí)間返回給前端。String token Jwts.builder() .setSubject(user.getId().toString()) .claim(username, user.getUsername()) .claim(role, user.getRole()) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 60 * 60 * 1000)) .setIssuedAt(new Date()) .signWith(SignatureAlgorithm.HS256, secretKey) .compact();攔截器里校驗(yàn)TokenComponent public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { return error(response, 401, 未登錄); } try { Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token.replace(Bearer , )) .getBody(); UserContext.set(claims); return true; } catch (Exception e) { return error(response, 401, 登錄已過(guò)期); } } }Token解析出的用戶信息放進(jìn)ThreadLocalUserContext后續(xù)Service里直接取當(dāng)前用戶ID。注意攔截器注冊(cè)時(shí)排除登錄注冊(cè)接口和攻略瀏覽接口否則游客沒(méi)法看攻略了。5.3 攻略發(fā)布與圖片上傳發(fā)布攻略的接口設(shè)計(jì)POST /api/article Content-Type: application/json { title: 只狼全Boss無(wú)傷攻略, summary: 從蝴蝶夫人到葦名一心全Boss打法詳解, cover: http://localhost:8080/uploads/cover.png, categoryId: 1, content: p開(kāi)頭先說(shuō)一下.../p }圖片上傳接口使用MultipartFile接收存儲(chǔ)到本地目錄并拼接訪問(wèn)URL。注意配置靜態(tài)資源映射把本地目錄映射成可訪問(wèn)的URL路徑spring: web: resources: static-locations: file:D:/upload/,classpath:/static/攻略發(fā)布后的狀態(tài)是待審核。前臺(tái)只查status1的攻略后臺(tái)管理端查status0的待審核列表管理員點(diǎn)擊審核通過(guò)后status改為1攻略上架。這個(gè)流程讓評(píng)論和瀏覽的權(quán)限控制更加清晰。5.4 點(diǎn)贊收藏的并發(fā)問(wèn)題點(diǎn)贊收藏接口雖然邏輯簡(jiǎn)單但并發(fā)場(chǎng)景下有一個(gè)問(wèn)題用戶同時(shí)點(diǎn)贊count字段會(huì)不會(huì)臟對(duì)畢設(shè)項(xiàng)目來(lái)說(shuō)在數(shù)據(jù)庫(kù)層加唯一索引先insert后會(huì)報(bào)重復(fù)錯(cuò)誤捕獲后返回“請(qǐng)勿重復(fù)操作”即可。真正需要做冪等的地方是遇到重復(fù)請(qǐng)求時(shí)不要讓count加兩次可以通過(guò)先查后插來(lái)兜底。開(kāi)發(fā)時(shí)一個(gè)小技巧點(diǎn)贊前后端都加限制前端按鈕狀態(tài)防重后端唯一索引做最終保障雙保險(xiǎn)不會(huì)出大問(wèn)題。6. 論文、說(shuō)明文檔和數(shù)據(jù)庫(kù)文檔項(xiàng)目之外的“隱形分”這個(gè)壓縮包名字里特別標(biāo)注了論文、說(shuō)明文檔、數(shù)據(jù)庫(kù)文檔說(shuō)明這套東西的完整度非常關(guān)鍵。很多搞技術(shù)的人重代碼輕文檔結(jié)果畢設(shè)論文寫到一半發(fā)現(xiàn)系統(tǒng)和論文章節(jié)對(duì)不上回頭改系統(tǒng)效率極低。正確的順序是先定需求、再建表、再寫文檔骨架最后開(kāi)發(fā)代碼。這樣寫論文時(shí)每部分都有實(shí)際內(nèi)容支撐。6.1 論文的章節(jié)結(jié)構(gòu)怎么安排游戲攻略網(wǎng)站的論文一般按這個(gè)章節(jié)框架走緒論項(xiàng)目背景、國(guó)內(nèi)外研究現(xiàn)狀、研究意義、論文結(jié)構(gòu)安排相關(guān)技術(shù)介紹Vue、Spring Boot、MySQL、MyBatis-Plus簡(jiǎn)介需求分析功能性需求、非功能性需求、用例圖系統(tǒng)設(shè)計(jì)總體架構(gòu)設(shè)計(jì)、功能模塊設(shè)計(jì)、數(shù)據(jù)庫(kù)設(shè)計(jì)ER圖表結(jié)構(gòu)系統(tǒng)實(shí)現(xiàn)分模塊截圖核心代碼說(shuō)明系統(tǒng)測(cè)試功能測(cè)試用例、測(cè)試結(jié)果寫系統(tǒng)實(shí)現(xiàn)部分的核心技巧是每個(gè)功能模塊配1~2張清晰截圖核心代碼只貼關(guān)鍵片段不要整段粘貼。比如登錄模塊貼JwtUtil和攔截器的核心代碼攻略發(fā)布貼Controller接口和Service方法配合文字說(shuō)明業(yè)務(wù)邏輯。老師最關(guān)注的是“你寫的代碼你能不能講明白”而不是代碼有多少行。6.2 說(shuō)明文檔怎么才能讓別人按步驟跑起來(lái)說(shuō)明文檔通常是接手項(xiàng)目的人第一個(gè)打開(kāi)的文件它的質(zhì)量決定了別人對(duì)你的第一印象。建議包含這些內(nèi)容環(huán)境要求JDK版本、Node版本、MySQL版本、Maven版本初始化數(shù)據(jù)庫(kù)如何執(zhí)行項(xiàng)目里的init.sql需要修改哪些配置后端啟動(dòng)步驟改application.yml里的數(shù)據(jù)庫(kù)連接、啟動(dòng)Spring Boot、確認(rèn)端口前端啟動(dòng)步驟npm install、npm run dev、訪問(wèn)地址默認(rèn)賬號(hào)管理員賬號(hào)密碼、測(cè)試用戶賬號(hào)密碼常見(jiàn)問(wèn)題端口沖突、npm install報(bào)錯(cuò)、跨域配置、圖片上傳路徑不存在有些項(xiàng)目甚至?xí)懸粋€(gè)啟動(dòng)順序的說(shuō)明圖這個(gè)在小項(xiàng)目里是加分項(xiàng)。6.3 數(shù)據(jù)庫(kù)文檔的寫法數(shù)據(jù)庫(kù)文檔的價(jià)值在于讓閱讀者不需要打開(kāi)Navicat就能看清全貌。用心去寫的話包含以下內(nèi)容ER圖用PowerDesigner或者draw.io畫標(biāo)注表之間的關(guān)聯(lián)關(guān)系。每張表的結(jié)構(gòu)說(shuō)明字段名、類型、是否為空、默認(rèn)值、備注。關(guān)鍵索引說(shuō)明哪些字段建了索引、為什么建索引。示例數(shù)據(jù)說(shuō)明初始化腳本里有哪些分類、哪些測(cè)試賬號(hào)。這個(gè)數(shù)據(jù)庫(kù)文檔不只是給答辯老師看的也是給后期接手者看的。你寫完這套文檔相當(dāng)于把整個(gè)項(xiàng)目的持久層設(shè)計(jì)徹底梳理了一遍出問(wèn)題的概率也會(huì)低很多。7. 跑通項(xiàng)目的完整步驟與高頻踩坑很多同學(xué)拿到項(xiàng)目第一步就卡在環(huán)境上。這里給一套從零到一跑通這個(gè)項(xiàng)目包的流程每一步都寫清楚避坑為主。7.1 環(huán)境準(zhǔn)備必須裝的環(huán)境工具JDK 8 或 11對(duì)應(yīng)Spring Boot 2.xMaven 3.6Node.js 16 或 18MySQL 5.7 或 8.0Navicat 或 MySQL Workbench用來(lái)導(dǎo)數(shù)據(jù)庫(kù)IDEA 和 VSCode后端和前端開(kāi)發(fā)建議先檢查一下命令版本很多時(shí)候問(wèn)題出在版本不匹配。Node 17以上版本跑Vite會(huì)有OpenSSL錯(cuò)誤解決方案是修改構(gòu)建命令# package.json 里 script 部分 scripts: { dev: vite, build: vite build }如果還是報(bào)error:0308010C:digital envelope routines::unsupported可以設(shè)置環(huán)境變量NODE_OPTIONS--openssl-legacy-provider或用nvm切換到Node 16。7.2 數(shù)據(jù)庫(kù)初始化的順序先用Navicat創(chuàng)建數(shù)據(jù)庫(kù)如game_guide字符集選utf8mb4然后執(zhí)行項(xiàng)目里的init.sql或者database.sql腳本。執(zhí)行完畢后檢查一下表是否都建出來(lái)了用戶表里是否有一條管理員賬號(hào)。檢查點(diǎn)如果啟動(dòng)后端時(shí)報(bào)“Table xxx doesnt exist”一般是SQL腳本沒(méi)有完整執(zhí)行如果報(bào)“Access denied for user”則要檢查application.yml里的數(shù)據(jù)庫(kù)用戶名密碼是否正確。7.3 后端啟動(dòng)步驟后端啟動(dòng)前確認(rèn)三件事application.yml里數(shù)據(jù)庫(kù)地址jdbc:mysql://localhost:3306/game_guide?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8是否準(zhǔn)確。mapper-locations指向的XML目錄是否存在。端口是否被占用默認(rèn)一般是8080。用IDEA打開(kāi)項(xiàng)目后等待Maven下載依賴。如果網(wǎng)絡(luò)不好可以在~/.m2/settings.xml里配置阿里云鏡像mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共倉(cāng)庫(kù)/name urlhttps://maven.aliyun.com/repository/public/url /mirror啟動(dòng)成功日志里出現(xiàn)Tomcat started on port(s): 8080 (http)就算過(guò)了。7.4 前端啟動(dòng)步驟前端項(xiàng)目用VSCode打開(kāi)先在根目錄執(zhí)行npm install如果卡在node-sass或者python相關(guān)依賴說(shuō)明項(xiàng)目大概率用的node-sass太老建議用npm install --force跳過(guò)沖突或者用npm config set registry https://registry.npmmirror.com切換鏡像再試。依賴裝完啟動(dòng)npm run dev默認(rèn)Vite端口是5173瀏覽器打開(kāi)訪問(wèn)。如果前端請(qǐng)求后端接口出現(xiàn)跨域報(bào)錯(cuò)兩種改法都行在Vite的vite.config.js里配置代理export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })或者在Spring Boot里寫一個(gè)CORS配置類放行所有來(lái)源Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }兩種用哪種都可以我更推薦Vite代理的方式因?yàn)樯a(chǎn)環(huán)境和開(kāi)發(fā)環(huán)境的訪問(wèn)路徑一致不用改前端代碼。7.5 一些高頻啟動(dòng)報(bào)錯(cuò)整理報(bào)錯(cuò)信息原因解決辦法Access denied for user rootlocalhost數(shù)據(jù)庫(kù)密碼錯(cuò)誤檢查application.yml里的spring.datasource.passwordUnknown database game_guide數(shù)據(jù)庫(kù)沒(méi)創(chuàng)建或名字不對(duì)在Navicat創(chuàng)建相同名字的數(shù)據(jù)庫(kù)Port 8080 was already in use端口被占用netstat -ano查占用進(jìn)程kill掉或改端口Failed to execute goal org.apache.maven.pluginsMaven依賴下載失敗配阿里云鏡像后重新mvn clean installerror:0308010C digital envelope routinesNode版本過(guò)高用Node16或設(shè)NODE_OPTIONS環(huán)境變量Failed to resolve import ...npm包沒(méi)裝全刪node_modules和package-lock.json重新npm install圖片上傳后訪問(wèn)404靜態(tài)資源映射沒(méi)配置確認(rèn)spring.web.resources.static-locations配置正確8. 從跑通到加分的幾個(gè)擴(kuò)展方向基礎(chǔ)功能都做完之后如果還有時(shí)間有幾個(gè)性價(jià)比非常高的擴(kuò)展點(diǎn)能給論文和答辯加分不少。8.1 數(shù)據(jù)可視化后臺(tái)統(tǒng)計(jì)面板用ECharts畫一個(gè)后臺(tái)統(tǒng)計(jì)頁(yè)面展示幾種數(shù)據(jù)每日新增攻略數(shù)量折線圖、分類占比餅圖、用戶增長(zhǎng)柱狀圖。后端對(duì)應(yīng)寫幾個(gè)統(tǒng)計(jì)接口// 按分類統(tǒng)計(jì)攻略數(shù)量 SELECT category_id, COUNT(*) AS cnt FROM article GROUP BY category_id // 按日期統(tǒng)計(jì)用戶注冊(cè)數(shù)量 SELECT DATE(create_time) AS day, COUNT(*) AS cnt FROM user GROUP BY DATE(create_time)前端用ECharts的option配置就能渲染出圖。這個(gè)功能代碼量不大但演示效果非常直觀而且論文系統(tǒng)實(shí)現(xiàn)部分可以直接放圖表截圖比純列表好看得多。8.2 瀏覽量異步更新攻略詳情頁(yè)每打開(kāi)一次就UPDATE article SET view_count view_count 1 WHERE id ?會(huì)拖慢詳情接口??梢宰鰞?yōu)化先更新Redis中的計(jì)數(shù)定時(shí)批量刷到數(shù)據(jù)庫(kù)。對(duì)畢設(shè)來(lái)說(shuō)如果項(xiàng)目里沒(méi)引Redis直接在詳情接口里同步更新也能接受但如果你在論文里把Redis方案寫進(jìn)去作為一個(gè)優(yōu)化點(diǎn)是會(huì)加分的。8.3 數(shù)據(jù)導(dǎo)入導(dǎo)出管理員后臺(tái)可以按分類導(dǎo)出攻略列表為Excel或者支持CSV導(dǎo)入。用EasyExcel或Apache POI實(shí)現(xiàn)代碼量不會(huì)超過(guò)100行但屬于“系統(tǒng)實(shí)用性”的證明。面試講項(xiàng)目時(shí)提到數(shù)據(jù)導(dǎo)入導(dǎo)出也正好能引出POI使用經(jīng)驗(yàn)和分批次讀取大數(shù)據(jù)量文件的考慮。8.4 多角色權(quán)限細(xì)化如果想把權(quán)限做得更細(xì)可以在用戶表加permissions字段或單獨(dú)建角色權(quán)限表用自定義注解控制接口權(quán)限。但對(duì)攻略網(wǎng)站來(lái)說(shuō)管理員和普通用戶兩種角色已經(jīng)足夠覆蓋場(chǎng)景不需要過(guò)度設(shè)計(jì)。最后分享幾個(gè)實(shí)際操作中的體會(huì)做這類前后端分離項(xiàng)目最耗時(shí)間的往往不是寫代碼本身而是環(huán)境配置和聯(lián)調(diào)階段的排查。我自己上手這類項(xiàng)目包時(shí)第一件事永遠(yuǎn)是先看README或說(shuō)明文檔再看數(shù)據(jù)庫(kù)腳本最后才看代碼。沒(méi)有說(shuō)明文檔時(shí)按“數(shù)據(jù)庫(kù) → 后端 → 前端”的順序去試錯(cuò)能省下大量時(shí)間。另外一點(diǎn)很實(shí)用項(xiàng)目包里如果自帶論文一定先把論文的目錄和系統(tǒng)實(shí)現(xiàn)章節(jié)讀一遍看它描述的功能與代碼是否一致。很多時(shí)候畢設(shè)代碼本身沒(méi)Bug但論文里寫的功能你找不到入口或者論文里的截圖跟當(dāng)前代碼長(zhǎng)得不一樣這就要在答辯前統(tǒng)一起來(lái)。游戲攻略網(wǎng)站這個(gè)題目本身不難但要做到完整、美觀、能演示、能講清楚工程量并不小。按這篇文章的思路走一遍從需求、數(shù)據(jù)庫(kù)、后端接口、前端頁(yè)面到論文文檔每一步都踏實(shí)做完你會(huì)發(fā)現(xiàn)答辯時(shí)手上的底氣完全不一樣。本文還有配套的精品資源點(diǎn)擊獲取