:從數(shù)據(jù)庫(kù)設(shè)計(jì)到部署擴(kuò)展全解析)
做畢設(shè)選方向的時(shí)候很多人一聽(tīng)“智能垃圾分類”第一反應(yīng)就是“這不就是個(gè)帶搜索功能的增刪改查嘛”然后隨隨便便找個(gè)模板套上去。結(jié)果做到答辯才發(fā)現(xiàn)老師問(wèn)“智能”在哪體現(xiàn)、數(shù)據(jù)怎么流轉(zhuǎn)、前端復(fù)用怎么做一個(gè)都答不上來(lái)。我手頭正好整理了一套基于Spring Boot Vue的智能垃圾分類系統(tǒng)代碼、文檔、數(shù)據(jù)庫(kù)腳本都齊也有配套的視頻講解這篇文章就圍繞這個(gè)項(xiàng)目把核心模塊的設(shè)計(jì)思路、前后端的關(guān)鍵實(shí)現(xiàn)、聯(lián)調(diào)部署時(shí)容易踩的坑以及拿到源碼后應(yīng)該怎么二次擴(kuò)展都拆開(kāi)了講一遍。無(wú)論是單純想交一份合格的畢設(shè)還是真的想把“智能推薦”“積分激勵(lì)”這些點(diǎn)做深這篇都能給你實(shí)打?qū)嵉膮⒖肌?. 項(xiàng)目整體設(shè)計(jì)與思路拆解1.1 需求定位畢設(shè)項(xiàng)目到底要解決什么問(wèn)題智能垃圾分類乍看是個(gè)小系統(tǒng)但深挖之后可以發(fā)現(xiàn)它其實(shí)覆蓋了“用戶端操作 管理員后臺(tái) 基礎(chǔ)數(shù)據(jù)維護(hù) 規(guī)則引擎 數(shù)據(jù)統(tǒng)計(jì)”五條線。畢設(shè)選題最怕的既不是太簡(jiǎn)單也不是太復(fù)雜而是找不到一個(gè)能自圓其說(shuō)的業(yè)務(wù)故事。垃圾分類系統(tǒng)天然就自帶一條清晰的主線用戶來(lái)到系統(tǒng)查詢某樣垃圾屬于什么類別完成分類投放獲得積分積分可以兌換小禮品管理員維護(hù)垃圾種類、管理投放記錄、審核反饋、查看統(tǒng)計(jì)報(bào)表。這套基于Spring Boot Vue的智能垃圾分類系統(tǒng)在功能設(shè)計(jì)上就遵循這條主線。前端用Vue做單頁(yè)應(yīng)用用戶端和管理員端拆成兩套界面后端用Spring Boot提供RESTful接口MySQL存業(yè)務(wù)數(shù)據(jù)。這種設(shè)計(jì)的好處是無(wú)論答辯老師問(wèn)你“前端怎么跟后端通信”“數(shù)據(jù)庫(kù)為什么這么設(shè)計(jì)”你都能從業(yè)務(wù)流和技術(shù)流兩個(gè)角度回答上來(lái)而不是背一段網(wǎng)上抄來(lái)的概念。1.2 技術(shù)選型分析為什么是Spring Boot Vue而不是其他組合很多同學(xué)糾結(jié)過(guò)要不要用更“新”的技術(shù)比如前后端不分離的Thymeleaf或者換Python系的Flask、Django。我的觀點(diǎn)很直接畢業(yè)設(shè)計(jì)的第一訴求是“穩(wěn)”第二訴求是“能講清楚”。Spring Boot的優(yōu)勢(shì)不在性能而在約定大于配置一個(gè)注解就能把接口、參數(shù)校驗(yàn)、異常處理串起來(lái)對(duì)Java基礎(chǔ)一般的同學(xué)來(lái)說(shuō)出問(wèn)題的概率遠(yuǎn)低于SSH那套老古董。Vue這邊選擇的是Vue 2 Element UI的組合理由也很樸素Vue 2的生態(tài)最成熟Element UI的組件基本能滿足后臺(tái)管理頁(yè)面的全部需求網(wǎng)上資料多到可以閉眼抄。如果你自己有精力換成Vue 3 Element Plus也不會(huì)傷筋動(dòng)骨前后端分離的思路完全一致只是組件庫(kù)API略有差異。說(shuō)到底畢設(shè)考察的是你對(duì)這套架構(gòu)的理解和運(yùn)用不是炫技。1.3 功能模塊劃分一份拿得出手的功能清單這套系統(tǒng)在功能模塊上劃分為用戶端、管理員端和公共模塊三大部分。用戶端包含注冊(cè)登錄、垃圾搜索查詢、分類投放記錄、積分查看與兌換、留言反饋等功能。垃圾搜索查詢是核心需要支持按名稱模糊搜索也能按類別瀏覽。管理員端包含后臺(tái)登錄、垃圾分類管理、投放記錄管理、用戶管理、積分規(guī)則管理、反饋管理和數(shù)據(jù)統(tǒng)計(jì)。公共模塊則包括圖片上傳、Excel導(dǎo)入導(dǎo)出這類基礎(chǔ)能力其中Excel導(dǎo)入用于初始化垃圾數(shù)據(jù)庫(kù)畢竟幾千條垃圾數(shù)據(jù)不可能靠手動(dòng)一條條插入。模塊劃分本身不復(fù)雜但邊界要清楚。我曾經(jīng)見(jiàn)過(guò)有同學(xué)把管理員接口跟用戶接口寫在一個(gè)Controller里結(jié)果權(quán)限控制亂成一團(tuán)。這套項(xiàng)目里用戶端跟管理端接口是嚴(yán)格分離的用戶端走/api/user/**管理端走/api/admin/**統(tǒng)一通過(guò)攔截器做角色校驗(yàn)。2. 核心功能與數(shù)據(jù)庫(kù)建模詳解2.1 數(shù)據(jù)庫(kù)表設(shè)計(jì)用實(shí)體關(guān)系圖理解數(shù)據(jù)流向數(shù)據(jù)表設(shè)計(jì)是畢設(shè)評(píng)分的重頭戲也是面試時(shí)容易被追問(wèn)的部分。我把這套系統(tǒng)的核心表歸納成6張它們之間的關(guān)聯(lián)關(guān)系非常清晰。用戶表sys_user用于存儲(chǔ)用戶和管理員信息包含密碼等加密字段以及狀態(tài)字段用于禁用賬號(hào)。垃圾類別表garbage_category存儲(chǔ)大類比如可回收垃圾、廚余垃圾、有害垃圾、其他垃圾并設(shè)置了排序字段和圖標(biāo)地址。垃圾信息表garbage_info是系統(tǒng)最核心的數(shù)據(jù)表每條記錄對(duì)應(yīng)一個(gè)具體物品名稱通過(guò)category_id外鍵關(guān)聯(lián)到類別表同時(shí)給名稱字段加了全文索引不然幾萬(wàn)條數(shù)據(jù)模糊查詢會(huì)很吃力。投放記錄表recycle_record記錄用戶每次投放的時(shí)間、垃圾類別、重量或數(shù)量、獲取的積分這里做了按用戶id和時(shí)間段的組合查詢優(yōu)化。積分規(guī)則表points_rule把不同垃圾類型的積分規(guī)則獨(dú)立成表方便管理員調(diào)整系數(shù)。反饋表feedback存儲(chǔ)用戶提交的意見(jiàn)建議或問(wèn)題申報(bào)管理端審核后可以修改處理狀態(tài)。數(shù)據(jù)表之間必須是清晰的“一對(duì)多”關(guān)系這也是判斷垃圾信息屬于哪個(gè)類別的邏輯基礎(chǔ)。答辯時(shí)如果你能順手在白板上畫一下這幾個(gè)表之間的關(guān)系再解釋一下為什么積分規(guī)則要單獨(dú)建表而不寫成硬編碼這題基本就穩(wěn)了。2.2 “智能”在哪里分類識(shí)別模塊的設(shè)計(jì)思路這一條是很多拿到源碼的人最想搞清楚的部分。系統(tǒng)里的“智能”不是深度學(xué)習(xí)、圖像識(shí)別那套高深的東西而是基于關(guān)鍵詞匹配的智能搜索引擎。對(duì)于畢設(shè)而言這反而是好事因?yàn)檫壿嫺煽?、解釋起?lái)更直白。具體的實(shí)現(xiàn)思路是用戶輸入一個(gè)垃圾名稱后端先把輸入做分詞處理去掉空格和常見(jiàn)的量詞比如“一個(gè)塑料瓶”會(huì)被打成關(guān)鍵詞“塑料瓶”。然后拿著關(guān)鍵詞去garbage_info表里做匹配匹配方式分三層精確匹配優(yōu)先完全命中直接返回類別其次做模糊匹配通過(guò)數(shù)據(jù)庫(kù)索引查詢名稱中包含關(guān)鍵詞的記錄如果找到多條結(jié)果按相關(guān)度排序相關(guān)度可以簡(jiǎn)單定義成名稱長(zhǎng)度與關(guān)鍵詞長(zhǎng)度的差值越短越靠前。如果三層都沒(méi)匹配到就返回“未收錄根據(jù)常識(shí)選擇類別”的提示避免直接報(bào)錯(cuò)。這種設(shè)計(jì)在代碼里就體現(xiàn)為GarbageServiceImpl中的search方法。它先做精確查詢沒(méi)結(jié)果再走like查詢最后對(duì)結(jié)果集做排序封裝。整段邏輯不超過(guò)50行但效果很穩(wěn)定。注意不要為了顯得“高級(jí)”而硬上深度學(xué)習(xí)圖像識(shí)別。識(shí)別一張圖片需要訓(xùn)練集、推理模型、GPU服務(wù)器畢設(shè)答辯現(xiàn)場(chǎng)只要網(wǎng)絡(luò)一卡整體體驗(yàn)會(huì)很糟糕。2.3 積分與兌換閉環(huán)業(yè)務(wù)如何串起來(lái)積分系統(tǒng)是很多展示型項(xiàng)目容易忽略的一環(huán)但垃圾分類系統(tǒng)如果砍掉積分用戶投放記錄就變成了流水賬沒(méi)有任何讓人眼前一亮的東西。這套項(xiàng)目的積分流程是用戶提交投放記錄時(shí)前端傳垃圾名稱和預(yù)估重量后端先查垃圾類別再根據(jù)類別查points_rule表里的積分單價(jià)計(jì)算獲得積分后寫入recycle_record同時(shí)更新用戶表的points字段。這中間有兩個(gè)容易出錯(cuò)的地方。第一個(gè)是積分變更的原子性如果投放記錄寫入成功但用戶積分更新失敗數(shù)據(jù)就對(duì)不上了。項(xiàng)目里直接用Transactional把兩步操作包在同一個(gè)事務(wù)里解決簡(jiǎn)單可靠。第二個(gè)是積分配置變更后老數(shù)據(jù)的處理邏輯項(xiàng)目里的處理方式是保留投放記錄的當(dāng)時(shí)積分快照字段避免規(guī)則修改后歷史數(shù)據(jù)全亂。3. 后端Spring Boot關(guān)鍵實(shí)現(xiàn)細(xì)節(jié)3.1 項(xiàng)目骨架與分層架構(gòu)拿到源碼后不要急著打開(kāi)所有文件先看包結(jié)構(gòu)。這套項(xiàng)目采用經(jīng)典的四層結(jié)構(gòu)controller層做參數(shù)接收和接口定義service層寫業(yè)務(wù)邏輯mapper層基于MyBatis操作數(shù)據(jù)庫(kù)entity層對(duì)應(yīng)數(shù)據(jù)表實(shí)體。另外還有獨(dú)立的config包里放了CORS跨域配置和攔截器配置common包里放了統(tǒng)一返回結(jié)果類、異常處理類和分頁(yè)對(duì)象。統(tǒng)一返回結(jié)果是我特別建議保留的一個(gè)設(shè)計(jì)。項(xiàng)目里所有接口返回的都是Result對(duì)象包含code、message、data三個(gè)字段。前端拿到返回體后統(tǒng)一判斷code是否為200再做后續(xù)處理。好處是無(wú)論查詢成功、參數(shù)錯(cuò)誤還是服務(wù)器內(nèi)部異常前端都走同一套處理邏輯調(diào)接口時(shí)心智負(fù)擔(dān)小很多。{ code: 200, message: 操作成功, data: { total: 128, records: [...] } }3.2 數(shù)據(jù)庫(kù)訪問(wèn)與接口開(kāi)發(fā)MyBatis還是MyBatis-Plus項(xiàng)目里的數(shù)據(jù)訪問(wèn)層用的是MyBatis-Plus這一點(diǎn)要特別說(shuō)明一下。MyBatis-Plus對(duì)單表CRUD的支持極好BaseMapper已經(jīng)內(nèi)置了增刪改查方法開(kāi)發(fā)效率比純手寫XML高出一大截。對(duì)于垃圾分類這種以單表操作為主的項(xiàng)目能用內(nèi)置方法就絕不寫SQL。但復(fù)雜查詢也不能逃避手寫SQL比如投放記錄的分頁(yè)統(tǒng)計(jì)、按時(shí)間段聚合積分這些都在mapper的XML文件里寫原生SQL確保查詢性能和靈活性。一個(gè)小建議如果你打算在這個(gè)基礎(chǔ)上擴(kuò)展一定要學(xué)會(huì)MyBatis-Plus的條件構(gòu)造器QueryWrapper。這是最常見(jiàn)的寫法也是網(wǎng)上教程代碼出現(xiàn)頻率最高的方式。我見(jiàn)過(guò)有人把QueryWrapper用成字符串拼接結(jié)果嵌套條件一多SQL直接報(bào)錯(cuò)。正確姿勢(shì)是鏈?zhǔn)秸{(diào)用清晰又安全。QueryWrapperGarbageInfo wrapper new QueryWrapper(); wrapper.like(StringUtils.isNotBlank(name), name, name); wrapper.eq(category_id, categoryId); wrapper.orderByAsc(sort);3.3 登錄鑒權(quán)與權(quán)限控制用戶端和管理員端必須嚴(yán)格區(qū)分權(quán)限不然誰(shuí)都能進(jìn)后臺(tái)的管理頁(yè)面這個(gè)系統(tǒng)就沒(méi)有任何可信度。項(xiàng)目用的是JWT 攔截器的方案。用戶登錄成功后后端生成一個(gè)包含用戶id和角色信息的Token返回給前端。前端把Token存在localStorage里每次請(qǐng)求在Authorization請(qǐng)求頭帶上。后端攔截器攔截所有/api/**請(qǐng)求解析Token并校驗(yàn)有效期同時(shí)把用戶信息放進(jìn)ThreadLocal供業(yè)務(wù)層隨時(shí)取用。這種方案比Session更契合前后端分離的場(chǎng)景因?yàn)闆](méi)有服務(wù)端會(huì)話狀態(tài)后端可以隨時(shí)水平擴(kuò)展。攔截器里要注意的是放行路徑的配置登錄接口和注冊(cè)接口必須加到白名單否則會(huì)出現(xiàn)“登錄接口自己報(bào)未登錄”的奇怪問(wèn)題。registry.addInterceptor(jwtInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/user/login, /api/user/register);攔截器里解析Token失敗時(shí)統(tǒng)一返回401狀態(tài)碼。前端在Axios的響應(yīng)攔截器里檢測(cè)到401就跳轉(zhuǎn)到登錄頁(yè)并清除本地Token。這個(gè)閉環(huán)不復(fù)雜但能讓整個(gè)系統(tǒng)的登錄體驗(yàn)順滑很多。3.4 文件上傳與Excel導(dǎo)入導(dǎo)出垃圾數(shù)據(jù)庫(kù)如果靠手動(dòng)錄入幾千條數(shù)據(jù)能錄到崩潰。所以項(xiàng)目里做了一個(gè)Excel導(dǎo)入功能管理員在后臺(tái)下載模板填寫垃圾名稱和類別后上傳后端用EasyExcel解析并把數(shù)據(jù)批量插入數(shù)據(jù)庫(kù)。配套的還有導(dǎo)出功能可以把投放記錄、用戶列表導(dǎo)出成Excel。EasyExcel的使用門檻很低只需要定義一個(gè)跟Excel列對(duì)應(yīng)的實(shí)體類然后調(diào)用EasyExcel.read()傳入輸入流和監(jiān)聽(tīng)器即可。項(xiàng)目里監(jiān)聽(tīng)器每讀取1000條數(shù)據(jù)就批量插入一次避免大文件上傳時(shí)一次插入過(guò)多數(shù)據(jù)導(dǎo)致內(nèi)存溢出。這個(gè)細(xì)節(jié)答辯時(shí)可以拿出來(lái)講一下體現(xiàn)你對(duì)實(shí)際業(yè)務(wù)體量的考慮。4. 前端Vue實(shí)現(xiàn)與頁(yè)面交互設(shè)計(jì)4.1 前端工程結(jié)構(gòu)與路由設(shè)計(jì)前端部分用Vue CLI構(gòu)建目錄結(jié)構(gòu)劃分得很規(guī)范views目錄按模塊分頁(yè)面router目錄配置路由api目錄封裝每個(gè)模塊的請(qǐng)求方法components目錄放公共組件utils目錄里是請(qǐng)求封裝和工具函數(shù)。路由設(shè)計(jì)上用戶端和管理員端采用不同的布局組件。用戶端路由掛在LayoutUser下管理端路由掛在LayoutAdmin下同時(shí)在路由守衛(wèi)里判斷用戶角色如果是管理員卻訪問(wèn)用戶端頁(yè)面直接跳轉(zhuǎn)到管理端首頁(yè)。這種通過(guò)路由做權(quán)限控制的方式在后端攔截器之外又增加了一道防線安全性是雙保險(xiǎn)。const routes [ { path: /user, component: LayoutUser, children: [ { path: search, component: GarbageSearch }, { path: records, component: RecycleRecords }, { path: points, component: PointsMall } ] }, { path: /admin, component: LayoutAdmin, meta: { role: ADMIN }, children: [ { path: garbage-list, component: GarbageManage }, { path: records, component: RecordManage }, { path: statistics, component: Statistics } ] } ]4.2 頁(yè)面組件拆分與復(fù)用思路前端最容易犯的錯(cuò)是一個(gè)頁(yè)面寫成一個(gè)巨大的組件幾百行模板疊在一起改一個(gè)按鈕要看半天。這套項(xiàng)目里的頁(yè)面都做了組件拆分。以垃圾查詢頁(yè)為例它被拆成搜索區(qū)組件、結(jié)果列表組件、類別標(biāo)簽組件三個(gè)部分。搜索區(qū)負(fù)責(zé)輸入和搜索按鈕結(jié)果列表負(fù)責(zé)渲染列表和翻頁(yè)類別標(biāo)簽負(fù)責(zé)篩選。這樣每個(gè)組件只干一件事后續(xù)要調(diào)整樣式或者替換交互邏輯改動(dòng)范圍非常小。復(fù)用性最高的是分頁(yè)組件和圖片上傳組件。分頁(yè)組件封裝了頁(yè)碼切換和每頁(yè)條數(shù)選擇所有需要分頁(yè)的列表頁(yè)直接引用圖片上傳組件封裝了上傳邏輯、回顯邏輯和刪除邏輯投放記錄里的照片和反饋里的圖片都可以共用。4.3 Axios封裝與接口對(duì)接前端與后端對(duì)接離不開(kāi)Axios但很多人把代碼寫成了到處import axios到處寫一遍get/post。這套項(xiàng)目在utils/request.js里做了一個(gè)統(tǒng)一封裝實(shí)例化Axios時(shí)設(shè)置了baseURL、超時(shí)時(shí)間為10秒請(qǐng)求攔截器里從localStorage取Token并放到請(qǐng)求頭響應(yīng)攔截器統(tǒng)一處理后端返回的code。如果code不等于200彈出錯(cuò)誤消息如果HTTP狀態(tài)碼是401跳轉(zhuǎn)登錄頁(yè)。數(shù)據(jù)字典這類公共接口在api/dict.js里單獨(dú)抽取避免每個(gè)頁(yè)面重復(fù)寫接口地址。實(shí)踐下來(lái)這么做的最大好處是后端接口地址一旦變更只需要改一個(gè)文件不會(huì)出現(xiàn)全局搜索替換的災(zāi)難現(xiàn)場(chǎng)。4.4 數(shù)據(jù)可視化讓項(xiàng)目在答辯時(shí)有亮點(diǎn)管理端的數(shù)據(jù)統(tǒng)計(jì)頁(yè)面使用了ECharts展示了四類垃圾的占比統(tǒng)計(jì)、每天投放數(shù)量的趨勢(shì)圖、積分發(fā)放量排名前十的用戶柱狀圖。這些圖表的背后是后端提供的統(tǒng)計(jì)接口例如/api/admin/statistics/category-ratio返回各類垃圾的記錄數(shù)占比前端拿到數(shù)據(jù)后簡(jiǎn)單組裝成ECharts需要的格式即可。做圖表有幾個(gè)經(jīng)驗(yàn)可以分享。第一個(gè)是圖表容器必須設(shè)置固定高度否則ECharts渲染出來(lái)是空白。第二個(gè)是組件銷毀前要調(diào)用chart.dispose()否則頁(yè)面切換后內(nèi)存泄漏會(huì)導(dǎo)致頁(yè)面越來(lái)越卡。第三個(gè)是數(shù)據(jù)量少的時(shí)候不要硬做動(dòng)態(tài)效果一個(gè)簡(jiǎn)單的餅圖帶圖例已經(jīng)足夠清晰表達(dá)數(shù)據(jù)含義。5. 前后端聯(lián)調(diào)、部署與問(wèn)題排查實(shí)錄5.1 本地聯(lián)調(diào)環(huán)境搭建拿到源碼到真正跑起來(lái)中間隔著的往往是一堆環(huán)境問(wèn)題。后端要裝JDK 8、Maven、MySQL前端要裝Node.js和npm。后端啟動(dòng)前必須改兩個(gè)配置文件application.yml里的數(shù)據(jù)庫(kù)連接地址和密碼以及file.upload-path指向本地一個(gè)存在的目錄不然文件上傳接口會(huì)報(bào)目錄不存在。前端啟動(dòng)前在vue.config.js里配置開(kāi)發(fā)服務(wù)器代理把/api開(kāi)頭的請(qǐng)求轉(zhuǎn)發(fā)到后端http://localhost:8080這樣才能解決跨域問(wèn)題。devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }需要特別提醒的是后端如果加了跨域配置前端代理又轉(zhuǎn)發(fā)一遍可能會(huì)出現(xiàn)二次跨域。解決辦法是二選一要么后端配置CORS要么前端配代理不要兩個(gè)同時(shí)上。5.2 部署打包步驟線上部署分成三步走。第一步是前端執(zhí)行npm run build把生成的dist目錄里的靜態(tài)文件部署到Nginx。第二步是后端執(zhí)行mvn clean package -DskipTests把項(xiàng)目打成jar包。第三步是配置Nginx做反向代理靜態(tài)文件走Nginx/api的請(qǐng)求轉(zhuǎn)發(fā)到后端的http://localhost:8080。Nginx配置里一個(gè)必須注意的坑是history路由模式。Vue Router如果用了history模式刷新頁(yè)面時(shí)Nginx會(huì)返回404需要在location /里加上try_files $uri $uri/ /index.html;。這個(gè)問(wèn)題在本地開(kāi)發(fā)時(shí)不會(huì)暴露很多同學(xué)都是部署完刷新頁(yè)面才發(fā)現(xiàn)白屏然后慌慌張張查兩天。項(xiàng)目里路由默認(rèn)用的是hash模式可以減少這個(gè)坑但如果你改成了history這段配置一定要加上。location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }5.3 常見(jiàn)問(wèn)題與排查技巧速查表在實(shí)際開(kāi)發(fā)調(diào)試過(guò)程中有一些問(wèn)題出現(xiàn)的頻率特別高我把它們整理成一個(gè)排查列表。問(wèn)題現(xiàn)象可能原因排查與解決前端請(qǐng)求404代理配置錯(cuò)誤或后端接口路徑不一致先看瀏覽器Network請(qǐng)求的URL是否正確再確認(rèn)RequestMapping路徑和前端api目錄里的地址是否一致后端啟動(dòng)報(bào)數(shù)據(jù)庫(kù)連接失敗MySQL沒(méi)起或密碼錯(cuò)誤檢查application.yml里的url和密碼確認(rèn)數(shù)據(jù)庫(kù)已執(zhí)行過(guò)初始化腳本登錄報(bào)“用戶名不存在”密碼加密方式不一致或用戶狀態(tài)禁用確認(rèn)注冊(cè)時(shí)密碼加密算法和登錄校驗(yàn)算法一致檢查status字段是否為1列表頁(yè)數(shù)據(jù)不刷新后端返回分頁(yè)數(shù)據(jù)格式不對(duì)確認(rèn)返回體里total和records字段名與前端約定一致上傳圖片失敗上傳路徑不存在或大小超限檢查file.upload-path配置確認(rèn)目錄存在并檢查spring.servlet.multipart.max-file-size配置跨域請(qǐng)求被攔截CORS配置缺失或重復(fù)使用后端CORS或前端代理二選一不要混用分類查詢結(jié)果為空垃圾數(shù)據(jù)表里沒(méi)有對(duì)應(yīng)記錄先執(zhí)行Excel導(dǎo)入功能確認(rèn)庫(kù)里有數(shù)據(jù)再測(cè)試5.4 幾個(gè)容易卡住的細(xì)節(jié)坑第一個(gè)坑是前后端時(shí)間格式不一致。后端Java返回的時(shí)間默認(rèn)格式是2024-01-01T12:00:00前端顯示的時(shí)候如果不做格式化頁(yè)面上的時(shí)間看起來(lái)特別別扭。解決方式是在后端實(shí)體的時(shí)間字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)或者在全局配置里統(tǒng)一設(shè)置Jackson的日期格式。第二個(gè)坑是密碼明文問(wèn)題。有些初版源碼里密碼直接明文存儲(chǔ)這是答辯時(shí)會(huì)被老師質(zhì)疑的點(diǎn)。項(xiàng)目里提供了BCrypt加密方案注冊(cè)時(shí)加密存入數(shù)據(jù)庫(kù)登錄時(shí)用BCryptPasswordEncoder校驗(yàn)千萬(wàn)別圖省事改成明文。第三個(gè)坑是分頁(yè)查詢里的“幽靈數(shù)據(jù)”。如果你改了數(shù)據(jù)庫(kù)里的數(shù)據(jù)但列表一直顯示舊內(nèi)容先檢查瀏覽器是不是緩存了響應(yīng)數(shù)據(jù)或者前端created生命周期里漏掉了首次加載數(shù)據(jù)的調(diào)用。6. 文檔撰寫、答辯準(zhǔn)備與后期擴(kuò)展6.1 畢設(shè)文檔怎么寫得有質(zhì)量拿到完整源碼只是第一步文檔才是決定評(píng)分的另一只腳。這套項(xiàng)目自帶的文檔包含了選題背景、可行性分析、需求分析、系統(tǒng)設(shè)計(jì)、數(shù)據(jù)庫(kù)設(shè)計(jì)、系統(tǒng)實(shí)現(xiàn)、系統(tǒng)測(cè)試和總結(jié)幾個(gè)核心部分。寫文檔時(shí)要特別注意兩點(diǎn)。第一點(diǎn)是數(shù)據(jù)庫(kù)設(shè)計(jì)章節(jié)必須配上ER圖和表結(jié)構(gòu)說(shuō)明。簡(jiǎn)潔的畫幾個(gè)表格把每個(gè)字段的含義、類型、約束列出來(lái)不需要堆砌十幾頁(yè)但要能講清楚為什么這么設(shè)計(jì)。第二點(diǎn)是系統(tǒng)測(cè)試章節(jié)不能只寫“功能正?!币獙懗赡銓?shí)際測(cè)試過(guò)的真實(shí)結(jié)果比如“輸入關(guān)鍵詞塑料瓶返回可回收垃圾響應(yīng)時(shí)間125ms”這種數(shù)據(jù)才有說(shuō)服力。6.2 答辯時(shí)容易被追問(wèn)的6個(gè)問(wèn)題根據(jù)我過(guò)往看過(guò)的答辯現(xiàn)場(chǎng)以下幾個(gè)問(wèn)題的高頻程度相當(dāng)高。第一個(gè)問(wèn)題是“系統(tǒng)中智能體現(xiàn)在哪里”不要回避直接講基于關(guān)鍵詞匹配的分類識(shí)別邏輯把三層匹配策略說(shuō)清楚再補(bǔ)充一句“如果后續(xù)要提升準(zhǔn)確率可以引入分詞服務(wù)和知識(shí)圖譜”。第二個(gè)問(wèn)題是“數(shù)據(jù)庫(kù)表為什么這么設(shè)計(jì)”以積分規(guī)則獨(dú)立建表為例解釋這是為了靈活配置不侵入業(yè)務(wù)代碼。第三個(gè)問(wèn)題是“如何處理并發(fā)下的積分更新”提Transactional事務(wù)管理再把樂(lè)觀鎖思路講一下。第四個(gè)問(wèn)題是“前端如何實(shí)現(xiàn)權(quán)限控制”講路由守衛(wèi)加Token鑒權(quán)雙層機(jī)制。第五個(gè)問(wèn)題是“為什么選擇Spring Boot而不是SSM”從配置簡(jiǎn)化、內(nèi)嵌容器、生態(tài)整合三個(gè)角度回答。第六個(gè)問(wèn)題是“系統(tǒng)有哪些可以改進(jìn)的地方”不要只說(shuō)“不知道”可以講“目前分類匹配依賴關(guān)鍵詞后續(xù)可以增加圖像識(shí)別模塊數(shù)據(jù)分析還停留在基礎(chǔ)統(tǒng)計(jì)后續(xù)可以增加預(yù)測(cè)模型”這反而會(huì)加分。6.3 拿到源碼后怎么做二次開(kāi)發(fā)如果不想只做一個(gè)“復(fù)制粘貼型”選手建議在原有基礎(chǔ)上做下面三個(gè)方向的擴(kuò)展。第一個(gè)方向是給garbage_info增加別名表或同義詞映射。目前匹配邏輯對(duì)“塑料瓶”“飲料瓶”這種同義詞可能漏查新增一張別名表通過(guò)外鍵關(guān)聯(lián)搜索時(shí)先查別名表再查主表能明顯提升命中率。第二個(gè)方向是將積分兌換擴(kuò)展成商品訂單模塊?,F(xiàn)在積分兌換可能是簡(jiǎn)單的扣除邏輯如果加上商品表、訂單表、發(fā)貨狀態(tài)整個(gè)項(xiàng)目的業(yè)務(wù)完整度立刻上升一個(gè)檔次也更好寫進(jìn)文檔。第三個(gè)方向是增加數(shù)據(jù)可視化的大屏展示頁(yè)在管理端做一塊大屏看板用ECharts把垃圾分類趨勢(shì)、地區(qū)分布、投放熱力圖全部展示出來(lái)視覺(jué)效果非常加分。7. 寫給大家的一些實(shí)在話我經(jīng)手過(guò)不少類似的畢設(shè)項(xiàng)目也幫很多同學(xué)看過(guò)代碼、改過(guò)Bug。最想說(shuō)的一點(diǎn)是源碼只是給你提供一套可運(yùn)行的骨架真正值錢的是你能不能在老師面前把每個(gè)設(shè)計(jì)決策講出道理。你不需要全部重寫但一定要把核心代碼逐行讀一遍尤其是垃圾分類匹配、積分事務(wù)、登錄鑒權(quán)這三塊因?yàn)榇蟾怕蕰?huì)成為被提問(wèn)的焦點(diǎn)。另外如果要往“簡(jiǎn)歷項(xiàng)目”方向發(fā)展我強(qiáng)烈建議在做完上述擴(kuò)展后把項(xiàng)目重新整理進(jìn)自己的作品集附上系統(tǒng)截圖、技術(shù)棧說(shuō)明和你在項(xiàng)目里獨(dú)立完成的模塊列表。也許它只是一個(gè)課程級(jí)別的項(xiàng)目但只要你把“為什么這么設(shè)計(jì)”和“怎么排查問(wèn)題”講得清楚它依然能成為一份扎實(shí)的實(shí)踐佐證。我個(gè)人在實(shí)操中最大的體會(huì)是答辯翻車往往不是因?yàn)轫?xiàng)目不夠復(fù)雜而是因?yàn)楹芏鄸|西只是‘能跑’但沒(méi)想通。把每個(gè)核心環(huán)節(jié)的邏輯都吃透哪怕代碼是拿來(lái)的最后講出來(lái)的效果也會(huì)完全不一樣。針對(duì)這套系統(tǒng)最有價(jià)值的二次開(kāi)發(fā)點(diǎn)就是把搜索命中率做上去、把積分商城做完整這兩件事做完項(xiàng)目從“典型畢設(shè)”立刻變成“有業(yè)務(wù)深度的作品”。之后不管你是拿去參加比賽還是寫進(jìn)簡(jiǎn)歷都拿得出手。