:Spring Boot+Vue全棧開發(fā)詳解)
快到畢業(yè)季了后臺總有人私信問我“藥房購藥系統(tǒng)”這類題目該怎么下手。作為經(jīng)常幫畢業(yè)生審代碼、理思路的老學長我今天就以這套基于JAVA的藥房購藥系統(tǒng)為例子把從需求拆解、技術選型、數(shù)據(jù)庫設計到核心代碼實現(xiàn)的完整鏈路掰開揉碎講一遍。這套系統(tǒng)不只是應付答辯你要是真能把它吃透Spring Boot Vue 的前后端分離開發(fā)、RBAC權限模型、訂單狀態(tài)機這些硬技能都能實實在在落到簡歷上。先給還不清楚狀況的同學定位一下這是一個典型的JavaWeb方向的管理信息系統(tǒng)業(yè)務邊界清晰技術棧主流特別適合計算機專業(yè)、軟件工程、信息管理類專業(yè)的畢業(yè)設計。系統(tǒng)解決的核心痛點很樸素——傳統(tǒng)藥房靠手工記賬、紙質(zhì)處方管理藥品庫存靠人工盤點效期管理靠肉眼排查而這套系統(tǒng)要做的就是把這些線下流程搬到線上實現(xiàn)藥品信息管理、庫存預警、前臺購藥結算、訂單追蹤、角色權限控制的一體化閉環(huán)。如果只是隨便找個開源項目改改名字交差那你大概率會在答辯時被問懵。我這篇博文的目標是讓你真正理解每個設計決策背后的原因為什么訂單金額用BigDecimal而不是double為什么用戶表和角色表要拆開為什么庫存扣減要放在數(shù)據(jù)庫事務里做把這些“為什么”搞明白你不僅能順利通過答辯還能在以后的面試里多幾分底氣。1. 項目整體設計與需求拆解1.1 從“購藥”到“管藥”一個完整的業(yè)務閉環(huán)很多人拿到這個題目第一反應是“這不就是個網(wǎng)上商城嗎把賣衣服換成賣藥不就行了”。這是最大的誤區(qū)。藥房購藥系統(tǒng)雖然長得像電商系統(tǒng)但它的業(yè)務復雜度遠超普通商城核心差異在于三個地方第一個差異是藥品的雙重屬性。藥品既是商品也是特殊監(jiān)管對象它有關聯(lián)的批準文號、生產(chǎn)廠家、批號、有效期、處方類型處方藥與非處方藥、儲存條件這些額外字段。普通商城只需要管理SKU和價格藥品管理系統(tǒng)必須管理效期和批號因為臨近過期的藥不能賣過了效期的藥必須鎖定下架。第二個差異是用戶角色的多樣化。一套完整的藥房系統(tǒng)至少要有四類角色管理員管人和管數(shù)據(jù)、藥師審核處方、上下架藥品、收銀員銷售開單、普通用戶瀏覽藥品、下單購藥。不同角色看到的界面不同操作的權限不同這就牽扯到RBAC權限模型的設計。第三個差異是庫存精度要求更高。普通商品庫存少一個多一個影響頂多是補貨藥品庫存出錯會直接影響患者用藥安全。所以庫存操作必須做事務控制每次出庫入庫都要留操作日志方便事后追溯。搞清楚這三點你再回來看這個選題就會發(fā)現(xiàn)它其實是個“麻雀雖小、五臟俱全”的綜合訓練——既有常規(guī)CRUD又有權限控制、事務處理、狀態(tài)流轉(zhuǎn)、報表統(tǒng)計該有的難點全都有。1.2 核心需求逐條拆解我習慣先畫功能樹再寫代碼。這套系統(tǒng)的功能規(guī)劃大致如下前臺用戶端針對普通購藥用戶用戶注冊、登錄、個人信息維護、密碼修改藥品分類瀏覽、關鍵字搜索、藥品詳情查看含說明書、用法用量購物車管理加入、修改數(shù)量、刪除、批量結算提交訂單、在線模擬支付、查看訂單狀態(tài)與歷史訂單常見問題FAQ與留言反饋后臺管理端針對管理員、藥師、收銀員登錄認證與基于角色的菜單權限控制藥品管理增刪改查、上架/下架、藥品圖片上傳、效期預警分類管理藥品類別維護支持二級分類庫存管理入庫登記、出庫登記、庫存盤點、低庫存預警訂單管理訂單列表篩選、訂單狀態(tài)更新發(fā)貨、完成、取消、訂單明細導出用戶管理用戶列表、禁用/啟用賬號、重置密碼輪播圖與系統(tǒng)公告管理這里要特別說明一下處方藥流程。很多畢設偷懶把所有藥品都當普通商品直接下單這是不專業(yè)的。但在畢業(yè)設計階段完整實現(xiàn)處方上傳、藥師審核、審核通過后才能支付的流程工作量確實偏大。實際操作中多數(shù)優(yōu)質(zhì)畢設會把處方藥設計成“加入購物車時需要填寫用藥人信息并勾選已確診聲明”然后在后端加一道藥師審核節(jié)點這樣既體現(xiàn)了業(yè)務理解深度又不至于把項目周期拖得太長。1.3 系統(tǒng)架構前后端分離還是單體應用這個問題幾乎每個來問我的人都糾結過。我的建議很直接有基礎就上前后端分離沒基礎就用單體Bootstrap模板引擎兩種方案都能做出優(yōu)質(zhì)畢設關鍵看你怎么選。前后端分離方案是當前工業(yè)界的主流形態(tài)后端用Spring Boot暴露RESTful API前端用Vue 3 Element Plus構建單頁應用通過JSON交換數(shù)據(jù)。好處是技術棧新、代碼組織清晰、簡歷上好看壞處是你要同時掌握后端接口設計和前端組件開發(fā)調(diào)試跨域、處理token過期這類問題的排查鏈路更長。單體模板方案是Spring Boot Thymeleaf或者JSP頁面由后端渲染邏輯簡單直接沒有跨域煩惱開發(fā)速度快。缺點是技術棧相對傳統(tǒng)前端交互體驗一般。我個人強烈推薦前后端分離因為答辯時老師大概率會問“為什么選前后端分離”這個問題的標準答案本身就體現(xiàn)了你對現(xiàn)代開發(fā)流程的理解。而且后端API的測試、前端組件的復用這些實踐都是可以直接帶到工作中的。后面我講的代碼示例也是以后端API為視角展開的。2. 技術選型與開發(fā)環(huán)境搭建2.1 技術棧逐項解析這套系統(tǒng)我采用的是Java生態(tài)里最經(jīng)典也最穩(wěn)妥的組合后端主體JDK 8 或 JDK 11JDK 11是分水嶺畢業(yè)設計用8也行但11的局部變量類型推斷等特性更好用Spring Boot 2.7.x不用最新的3.x因為3.x要求JDK 17而且很多配套組件版本變化較大容易給自己挖坑MyBatis-Plus 3.5.x數(shù)據(jù)持久層相比純MyBatis省去大量單表操作的XML配置MySQL 8.0關系型數(shù)據(jù)庫8.0以上更好用支持窗口函數(shù)Redis 可選用于緩存驗證碼、token如果不想引入中間件本地Map也能應付前端主體Vue 3 Vite構建速度快組合式API寫邏輯更順手Element Plus組件庫表格、表單、彈窗、分頁都是現(xiàn)成的Pinia狀態(tài)管理比Vuex更簡潔AxiosHTTP請求庫ECharts可視化圖表用于首頁統(tǒng)計輔助工具Maven依賴管理JWT Spring Security 或者 Sa-Token認證授權Lombok簡化實體代碼Hutool工具類庫處理日期、加密、驗證碼很方便這個組合是目前B站和培訓機構主流的教學組合資料多、報錯網(wǎng)上都能搜到解決方案對畢設來講是風險最低的選擇。2.2 為什么不用SSM框架很多同學在選題時后臺老師會列“Spring SpringMVC MyBatisSSM”讓你選。我的意見是能不用SSM就不用。SSM屬于上一個時代的開發(fā)方式配置繁瑣——光一個Spring配置和MyBatis配置就要寫幾十行XML而Spring Boot通過自動配置把這些問題全部抹平了。你做畢設是為了展示工程能力不是為了證明自己能手工拼裝XML。同理為什么不推薦用ShiroShiro和Spring Security都能做權限控制但Spring Security在最近版本中不斷演進社區(qū)活躍度更高與Spring Boot的集成更無縫。當然如果你擅長Shiro選它也完全沒問題核心是你要能講清楚Session認證與Token認證的區(qū)別。我這個項目用的是Sa-Token它的學習曲線平緩API設計對初學者非常友好文檔又全屬于小眾但非常香的選擇。2.3 環(huán)境搭建的坑與版本匹配環(huán)境搭建這一步我見過太多人卡死在版本匹配上。直接給一套我已驗證可用的版本組合JDK1.8.0_202 或 Amazon Corretto 8Maven3.6.3 或 3.8.xSpring Boot2.7.14MyBatis-Plus3.5.3.1注意3.5.4之后分頁插件寫法有變化MySQL8.0.32 或 5.7.265.7也可以但注意時區(qū)配置Node.js16.20.x 或 18.xVite 4需要Node 16.20npm8.x一個非常容易踩的坑是Maven倉庫依賴下載緩慢或版本沖突。建議在Maven的settings.xml里配置阿里云鏡像并且只用spring-boot-starter-parent做統(tǒng)一版本管理不要自己隨意指定子模塊版本否則極容易出現(xiàn)NoSuchMethodError之類的問題。遇到啟動報錯先別急著懷疑代碼按這個順序排查Maven依賴是否下載完整 → 數(shù)據(jù)庫連接配置是否有誤 → 端口是否被占用 → 啟動類位置是否放對Spring Boot默認掃描啟動類所在包及其子包。80%的啟動失敗都能用這四步解決。3. 數(shù)據(jù)庫設計藥房業(yè)務的地基3.1 數(shù)據(jù)表全景數(shù)據(jù)庫設計是面試和答辯時老師最愛深挖的地方也是最能體現(xiàn)你水平的地方。這張表結構圖我建議直接背下來并能講出每一張表為什么這么設計。核心表sys_user用戶表用戶ID、用戶名、密碼、昵稱、頭像、手機、郵箱、角色標識、狀態(tài)、創(chuàng)建時間sys_role角色表角色ID、角色編碼、角色名稱sys_user_role用戶角色關聯(lián)表多對多關系drug_category藥品分類表分類ID、父分類ID、分類名稱、排序drug_info藥品信息表藥品ID、批準文號、藥品名稱、通用名、規(guī)格、劑型、單位、生產(chǎn)廠家、儲存條件、處方類型、零售價、會員價、庫存數(shù)量、預警值、圖片、說明書、創(chuàng)建時間drug_stock_log庫存變動日志表日志ID、藥品ID、變動數(shù)量、變動類型入庫/出庫/盤點調(diào)整、操作前后數(shù)量、操作人、操作時間、備注cart_item購物車表購物車ID、用戶ID、藥品ID、藥品數(shù)量、加入時間order_info訂單主表訂單號、用戶ID、訂單總金額、實付金額、訂單狀態(tài)、收貨人、聯(lián)系電話、收貨地址、下單時間、支付時間、完成時間order_item訂單明細表明細ID、訂單ID、藥品ID、藥品名稱快照、單價快照、數(shù)量、小計金額sys_notice公告表公告ID、標題、內(nèi)容、發(fā)布時間sys_pic輪播圖表圖片ID、圖片地址、跳轉(zhuǎn)鏈接、排序這幾張表覆蓋了一個藥房系統(tǒng)的全部核心鏈路。多說一句訂單明細表里的藥品名稱快照字段特別重要它是為了防止藥品信息被修改后歷史訂單數(shù)據(jù)跟著變臟的做法。畢業(yè)設計能考慮到這一點答辯時是明顯的加分項。3.2 關鍵字段設計意圖解讀我挑幾個容易踩坑的字段詳細說說。藥品價格字段必須使用DECIMAL(10,2)。我審過不少畢設代碼發(fā)現(xiàn)有人圖省事用double存價格這是嚴重的低級錯誤。float和double在二進制浮點運算中會產(chǎn)生誤差0.1 0.2輸出的結果不是0.3這在涉及金額累加、優(yōu)惠折扣計算時會造成分級別錯誤。Java后端對應使用BigDecimal所有價格計算必須通過BigDecimal的add、subtract、multiply方法完成禁止直接使用算術運算符。庫存字段藥品表冗余存一個當前庫存字段同時用庫存變動日志表記錄每一次出入庫明細。為什么這么設計因為如果只靠日志表時時SUM統(tǒng)計庫存訂單并發(fā)高的時候性能撐不住而且查詢歷史維度特別麻煩如果只存一個當前庫存數(shù)字又無法追溯“這個月總共入庫多少、出庫多少”。兩者互補才是完整方案。訂單狀態(tài)字段用int類型TINYINT存狀態(tài)值配合后端狀態(tài)機做流轉(zhuǎn)校驗。不要用字符串狀態(tài)直接存因為不同模塊對狀態(tài)的叫法可能不一致比如前端叫“待付款”后端叫“PENDING_PAY”用數(shù)字枚舉后端統(tǒng)一維護并配合狀態(tài)機限制流轉(zhuǎn)路徑比如“已發(fā)貨”狀態(tài)下不能直接跳到“已取消”這種限制能防止臟數(shù)據(jù)。3.3 物理外鍵與邏輯外鍵的選擇這是個很經(jīng)典的開發(fā)爭議話題。很多教科書鼓勵建外鍵約束但我實際做項目時的習慣是數(shù)據(jù)庫層面不建物理外鍵邏輯外鍵由代碼控制。原因很簡單——物理外鍵會導致幾個問題一是每次插入子表都要檢查主表寫并發(fā)高的時候性能受損二是刪除主表數(shù)據(jù)時如果被外鍵約束攔住會出現(xiàn)一堆“刪除失敗”的詭異報錯三是項目運行中如果要調(diào)整表結構比如分庫分表外鍵是最難遷移的。所以在互聯(lián)網(wǎng)公司里物理外鍵幾乎絕跡都是靠開發(fā)人員在業(yè)務層保證引用完整性。但在答辯時如果老師質(zhì)疑“為什么沒有外鍵”你要能解釋清楚這套邏輯而不是支支吾吾答不上來。這本身就是設計思維的體現(xiàn)。4. 核心功能模塊的實現(xiàn)細節(jié)4.1 用戶認證JWT令牌機制用戶登錄是整套系統(tǒng)的第一道門。我不建議用傳統(tǒng)的Session方案因為前后端分離的場景下Session天然要依賴Cookie而跨域場景下Cookie處理很麻煩。這里采用JWTJSON Web Token方案。簡單講用戶輸入用戶名密碼后端校驗通過后生成一個帶簽名的令牌前端把令牌存到本地每次請求在請求頭里帶上Authorization: Bearer xxx后端過濾器攔截并驗簽。這套機制的核心價值是無狀態(tài)——服務器不需要保存會話信息天然支持水平擴容。JWT的生成代碼在用Sa-Token時非常簡單PostMapping(/login) public ResultString login(RequestBody LoginDTO dto) { // 1. 校驗驗證碼防止暴力破解 // 2. 根據(jù)用戶名查用戶 SysUser user userService.getByUsername(dto.getUsername()); // 3. 密碼加密比對BCrypt加密 if (user null || !BCrypt.checkpw(dto.getPassword(), user.getPassword())) { return Result.error(用戶名或密碼錯誤); } // 4. 賬號是否被禁用 if (user.getStatus() 0) { return Result.error(賬號已被禁用請聯(lián)系管理員); } // 5. 簽發(fā)token StpUtil.login(user.getUserId()); return Result.success(StpUtil.getTokenValue()); }這里要強調(diào)一個核心原則數(shù)據(jù)庫里的密碼絕不能存明文。正確的做法是使用BCrypt或Spring Security自帶的加密器對密碼做哈希處理登錄時用BCrypt.checkpw比對。如果你在項目里發(fā)現(xiàn)密碼直接明文存儲答辯時這就是被攻擊的點——老師只要問一句“數(shù)據(jù)庫泄露了怎么辦”你就無言以對。4.2 庫存扣減事務控制藥品下單是整個系統(tǒng)里并發(fā)風險最高的操作。比如100個用戶同時爭搶庫存僅剩3盒的藥品如果代碼寫成“先查庫存-判斷足夠-再減庫存”在高并發(fā)場景下會產(chǎn)生嚴重的超賣問題。因為查庫存和減庫存兩步之間有時間窗其他請求可能在這期間插進來大家都查到庫存是3都以為自己能買結果最終扣成負數(shù)。正確做法是用數(shù)據(jù)庫層面的原子更新加事務控制Transactional(rollbackFor Exception.class) public void deductStock(ListCartItem items) { for (CartItem item : items) { // 原子更新庫存大于購買數(shù)量時才扣減 int rows drugInfoMapper.deductStock(item.getDrugId(), item.getQuantity()); if (rows 0) { throw new RuntimeException(庫存不足商品 item.getDrugName()); } // 記錄庫存變動日志 drugStockLogService.recordLog(item.getDrugId(), -item.getQuantity(), ...); } }對應Mapper中的SQLUPDATE drug_info SET stock stock - #{quantity} WHERE drug_id #{drugId} AND stock #{quantity}注意這段SQL的巧妙之處它在一條語句里同時完成了“判斷庫存是否充足”和“扣減庫存”兩個操作利用數(shù)據(jù)庫行鎖保證了并發(fā)安全。如果影響行數(shù)為0說明條件不成立那就拋異常讓整個事務回滾訂單也不會創(chuàng)建。這種寫法比你“先select后update”的方式在性能和安全性上高出不止一個檔次。庫存扣減還有兩個細節(jié)需要注意。第一必須在Transactional里執(zhí)行一旦后面的訂單創(chuàng)建、明細寫入任何一個環(huán)節(jié)報錯庫存能跟著回滾不會出現(xiàn)“錢沒收到、藥卻扣了”的局面。第二可以額外設計一個庫存預警機制——當庫存低于預警值時后臺首頁會提示采購員補貨這個邏輯在數(shù)據(jù)字典里有預警值字段。4.3 訂單狀態(tài)機與異常處理訂單模塊看起來只是簡單的insert和update但真正折磨人的是各種邊界狀態(tài)。用戶下單后不支付怎么辦支付了但不發(fā)貨怎么辦發(fā)貨了但用戶要退貨怎么辦如果代碼里沒有約束就會出現(xiàn)任意狀態(tài)跳到任意狀態(tài)的混亂情況。我建議用一個私有方法集中處理狀態(tài)流轉(zhuǎn)public boolean changeOrderStatus(String orderNo, int targetStatus) { OrderInfo order this.getByOrderNo(orderNo); // 合法狀態(tài)流轉(zhuǎn)映射 MapInteger, ListInteger validTransitions new HashMap(); validTransitions.put(0, Arrays.asList(1, 5)); // 待支付 - 已支付 / 已取消 validTransitions.put(1, Arrays.asList(2)); // 已支付 - 已發(fā)貨 validTransitions.put(2, Arrays.asList(3, 4)); // 已發(fā)貨 - 已完成 / 已退款 ListInteger allowed validTransitions.get(order.getStatus()); if (allowed null || !allowed.contains(targetStatus)) { throw new BizException(非法的訂單狀態(tài)流轉(zhuǎn)); } // 執(zhí)行更新 }這200行不到的狀態(tài)映射表是你答辯時說明“對業(yè)務有完整理解”的絕佳材料。老師問“你怎么保證訂單狀態(tài)不亂”你就可以拿這個類直接講設計思路。訂單模塊還有幾個容易遺漏的細節(jié)生成訂單號要保證唯一且有序一般用時間戳加隨機數(shù)或雪花算法保存收貨地址要單獨存字符串快照而不是存地址ID防止用戶修改地址后歷史訂單地址跟著變刪除訂單只能做軟刪除避免破壞與明細表之間的引用關系。4.4 前端頁面的核心交互邏輯前端部分藥房購藥系統(tǒng)的核心頁面包括首頁輪播圖 藥品展示 公告、藥品列表分類篩選 關鍵詞搜索 分頁、藥品詳情、購物車頁、訂單結算頁、個人中心、后臺管理頁。這里提兩個前端頁面里最值得優(yōu)化的交互點。第一個是購物車數(shù)量加減的防抖處理。用戶在購物車里面連續(xù)點擊“”號時如果每次都發(fā)一次請求更新數(shù)據(jù)庫會產(chǎn)生大量無用請求而且容易導致數(shù)據(jù)錯亂。正確做法是前端在200毫秒內(nèi)合并點擊操作只發(fā)最后一次請求或者更穩(wěn)妥的做法是購物車數(shù)量變動只在內(nèi)存中操作等用戶點擊“去結算”時才批量提交后端。后端再根據(jù)提交的數(shù)據(jù)進行數(shù)量重新校驗避免用戶直接篡改價格或數(shù)量。第二個是訂單提交前的秒殺式重復點擊防護。用戶點擊“提交訂單”后按鈕立刻變成loading狀態(tài)同時生成一個前端請求序列號后端判斷該序列號是否已處理過處理過就直接返回結果而不是再建一次單。這些細節(jié)雖然不起眼但答辯時被問“系統(tǒng)有哪些安全設計”時你能說出這些點老師會非常認可。前端還有一塊容易被忽視的是路由權限控制。Vue Router里定義路由meta信息比如meta: { roles: [admin] }路由守衛(wèi)里根據(jù)當前用戶的角色過濾。這不難但能讓后臺管理系統(tǒng)的不同角色進入不同菜單屬于必須有的體驗。5. 開發(fā)全流程記錄與踩坑實錄5.1 從建表到跑通首個接口的開發(fā)順序很多同學拿到題目不知道先干什么。我梳理一個我認為最高效的開發(fā)順序第一步畫數(shù)據(jù)庫ER圖建表并插入測試數(shù)據(jù)。這一步花兩天時間都不為過因為后續(xù)的所有代碼都圍繞表結構寫。測試數(shù)據(jù)要造得像樣藥品名、批準文號、生產(chǎn)廠家都要真實比如“阿莫西林膠囊 0.25g*24粒 國藥準字H20003263”這種。第二步搭后端骨架建項目、配置數(shù)據(jù)庫連接、寫實體類、Mapper先跑通一個登錄接口。到這里你的項目已經(jīng)能啟動了。第三步實現(xiàn)用戶端核心鏈路注冊登錄 - 藥品列表 - 藥品詳情 - 加入購物車 - 提交訂單 - 模擬支付 - 查看訂單。這個流程打通之后系統(tǒng)的大梁就算是立住了。第四步實現(xiàn)后臺核心鏈路藥品CRUD - 分類管理 - 庫存出入庫 - 訂單處理發(fā)貨/取消 - 用戶管理。到這里你的系統(tǒng)已經(jīng)可以真實使用了。第五步做輔助功能輪播圖管理、公告管理、數(shù)據(jù)統(tǒng)計圖表、個人中心。這些都是錦上添花的內(nèi)容工作量可控但非常提升完成度。最后是測試和補充用Postman把每個接口測一遍記錄異常情況并修復檢查權限接口是否被正確攔截處理前端控制臺警告編寫部署說明文檔。5.2 前后端分離聯(lián)調(diào)時的跨域問題前后端分離項目第一個攔路虎就是跨域。前端跑在5173端口后端跑在8080端口瀏覽器的同源策略會直接攔截Ajax請求控制臺報錯CORS policy。有三種主流解決方案一是在后端寫全局跨域配置類二是前后端通過Nginx反向代理同一域名下轉(zhuǎn)發(fā)三是用Spring Cloud Gateway之類的東西做網(wǎng)關代理。對畢設而言第一種最簡單直接。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意allowedOriginPatterns(*)與allowCredentials(true)必須同時使用只寫allowedOrigins(*)在部分瀏覽器版本上會與credentials沖突??缬騿栴}排查不難但如果你不知道原理改半天代碼都找不到問題在哪最后往往是后端過濾器把OPTIONS預檢請求攔截了。5.3 MyBatis-Plus分頁失效問題分頁可以說是查詢功能里必踩的坑。MyBatis-Plus使用分頁需要配置一個分頁插件Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }如果你忘記配置這個插件調(diào)用selectPage不會報錯但數(shù)據(jù)會全部返回分頁完全失效。更隱蔽的問題是分頁查詢返回的total值默認是普通count如果數(shù)據(jù)量極大這種count是全表掃描的性能很差。MyBatis-Plus默認做了優(yōu)化但如果你的查詢SQL里包含多表joincount語句可能會統(tǒng)計出錯誤的總數(shù)需要手動指定optimizeCountSql或單獨的處理方式。另一個常見問題是分頁時帶上條件查詢MyBatis-Plus會自動拼接條件但如果你在條件里傳了模糊匹配的搜索詞而這個詞帶%或_這種SQL通配符就會導致查詢結果異常。需要用QueryWrapper的like方法配合Escape處理或者自己手動轉(zhuǎn)義。5.4 文件上傳藥品圖片的存儲策略藥品管理里圖片上傳看似簡單其實有個規(guī)劃問題圖片存到哪里常見三種方案。方案一是存本地磁盤上傳路徑寫在配置文件里前端通過Nginx映射訪問。這種方案適合畢設簡單可靠但要注意服務器重啟后路徑配置不能寫死。方案二是存到數(shù)據(jù)庫的Blob字段但這種方案極度不推薦數(shù)據(jù)庫文件過大會拖性能備份和遷移都是災難。方案三是存到云端對象存儲阿里云OSS/MinIO畢設用MinIO自建就夠。如果不想引入額外組件方案一在本地環(huán)境足夠了。上傳接口用Spring Boot自帶的MultipartFile接收配合Hutool的FileUtil生成隨機文件名限制文件大小2MB校驗文件后綴名。這些代碼網(wǎng)上都有但你要能講清楚為什么限制大小和類型答案是防惡意上傳這屬于系統(tǒng)安全設計的一部分。5.5 報表統(tǒng)計模塊的數(shù)據(jù)組織藥房系統(tǒng)的首頁儀表盤通常要展示今日銷售額、今日訂單數(shù)、會員總數(shù)、庫存預警藥品數(shù)、近一周銷售趨勢圖。這部分我用ECharts來實現(xiàn)效果很直觀。數(shù)據(jù)庫層面的做法是寫聚合SQLSELECT DATE(create_time) AS order_date, SUM(total_amount) AS amount, COUNT(*) AS cnt FROM order_info WHERE create_time DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY DATE(create_time) ORDER BY order_date;后端把這個List返回給前端前端ECharts直接把日期和金額作為x軸、y軸渲染成折線圖。這部分在答辯時是非常出彩的展示亮點建議認真做。5.6 常見的啟動與運行異常速查表記一份異常排查手冊可以省掉很多上網(wǎng)搜索的時間。異?,F(xiàn)象可能原因解決方案啟動報Consider defining a bean of type xxxMapperMapper接口沒有被掃描到在啟動類加MapperScan注解前端請求返回401 UnauthorizedToken缺失或過期檢查前端請求攔截器是否帶了請求頭用Postman直接測后端驗證中文寫入數(shù)據(jù)庫變成?數(shù)據(jù)庫連接URL未指定characterEncoding在JDBC URL中加?useUnicodetruecharacterEncodingutf8上傳圖片后訪問404靜態(tài)資源路徑未映射配置WebMvcConfigurer的addResourceHandlersjava.sql.SQLException: Access denied for user數(shù)據(jù)庫賬號密碼錯誤或權限不足在MySQL里執(zhí)行GRANT ALL PRIVILEGES ON db_name.* TO rootlocalhost啟動時端口被占用其他進程占了8080用netstat -anoRedis連接超時Redis未啟動或密碼錯誤啟動Redis服務或檢查密碼如果使用Windows版Redis檢查服務狀態(tài)前端npm run dev啟動失敗Node版本過低或依賴不完整升級Node到16.20刪掉node_modules重新安裝6. 項目優(yōu)化方向與答辯準備經(jīng)驗6.1 比基本功能再多走一步的優(yōu)化方案如果你的系統(tǒng)已經(jīng)按上述功能完成了且還有時間精力我建議按性價比從高到低挑幾個方向做強化第一藥品效期管理。在藥品信息表中增加生產(chǎn)日期和有效期字段用定時任務掃描即將過期的藥品假設有效期低于3個月在后臺首頁生成警告列表臨期藥品在列表頁打上特殊標簽。這個功能尤其貼合醫(yī)藥行業(yè)屬性答辯時說“我用Scheduled定時任務實現(xiàn)了效期預警”這是非常亮眼的工程亮點。第二操作日志審計。用一個AOP切面記錄管理員的所有敏感操作比如刪除藥品、修改價格、強制下架。在設計層面體現(xiàn)合規(guī)意識醫(yī)藥行業(yè)對操作留痕要求很高。第三訂單超時未支付自動取消。用戶下單后15分鐘不支付訂單自動關閉并回滾庫存。這里有兩個實現(xiàn)方案一是后端定時任務掃描超時訂單批量處理二是使用延遲消息或者Redisson的延遲隊列精準觸發(fā)。畢設用定時任務就夠了但如果你能主動講出兩種方案的區(qū)別說明你真的理解系統(tǒng)設計而不只是能寫出代碼。第四數(shù)據(jù)導入導出。藥品信息支持Excel批量導入訂單列表支持導出Excel。用EasyExcel或Hutool的Excel工具就能做工作量半天起步但“批量導入”功能對管理員來說很實用也是答辯時容易被追問的點。6.2 答辯時的高頻問題與回答思路我能猜到你最緊張的是什么沒錯就是答辯。整理幾個高頻問題及回答思路老師問“你這個系統(tǒng)的角色權限是怎么實現(xiàn)的”回答主線RBAC模型 用戶-角色多對多表 菜單權限攔截器。在攔截器里判斷當前登錄用戶的角色標識決定哪些接口能訪問。前端再根據(jù)角色渲染對應的菜單。強調(diào)RBAC的核心理念是“權限不直接綁定用戶而是綁定角色”這樣新增角色、調(diào)整權限不用改代碼。老師問“藥品庫存你是怎么保證數(shù)據(jù)準確性的”回答主線數(shù)據(jù)庫事務 原子更新SQL 庫存變動日志。解釋清楚UPDATE drug_info SET stock stock - quantity WHERE stock quantity這條SQL在并發(fā)場景下如何避免超賣事務回滾如何保證數(shù)據(jù)一致性庫存日志如何實現(xiàn)事后追溯。這一套邏輯下來老師基本不會繼續(xù)追問了。老師問“訂單支付這塊你做的是模擬支付安全和真實支付有什么區(qū)別”回答主線坦率回答這是模擬支付真實支付需要對接微信支付或支付寶流程包括下單時生成支付二維碼、支付回調(diào)通知、回調(diào)驗簽、訂單狀態(tài)異步刷新。重點說出“真實支付的核心難點在于回調(diào)處理與冪等設計”展示出你對真實業(yè)務的理解而不是硬吹自己實現(xiàn)得多完善。老師問“你的系統(tǒng)有哪些地方可以繼續(xù)優(yōu)化”回答主線千萬不要說“沒有了”。準備兩個方向一是引入Redis做緩存把藥品熱門數(shù)據(jù)查詢放緩存降低數(shù)據(jù)庫壓力二是引入消息隊列在訂單高峰期削峰填谷提高系統(tǒng)的并發(fā)處理能力。這樣既展示了你的問題意識也說明你有架構演進思維。6.3 源碼管理與論文撰寫的配合最后聊聊源碼管理和論文。如果你用Git做版本管理每次寫完一個功能模塊就提交一次commit message寫清功能含義比如feat: 實現(xiàn)購物車結算功能。答辯時展示Git提交記錄能直觀證明項目是你一步步做的而不是網(wǎng)上搬的。論文寫作方面核心章節(jié)要對應系統(tǒng)設計第三章寫系統(tǒng)分析第四章寫系統(tǒng)設計功能模塊圖 數(shù)據(jù)庫E-R圖 表結構第五章寫系統(tǒng)實現(xiàn)每個核心功能的截圖 核心代碼片段完美呼應。論文里引用的每張圖表都是你系統(tǒng)里真實運行出來的這種真實性就是高分的基礎。寫在最后的一點體會做畢業(yè)設計這個東西說穿了是一個“一個人扮演一個團隊”的過程。你可能要在同一個項目里同時當產(chǎn)品經(jīng)理、后端工程師、前端工程師、測試工程師、部署運維還得當自己的答辯教練。這個過程確實累但請相信我只要你按“理解業(yè)務 - 拆分功能 - 設計數(shù)據(jù) - 實現(xiàn)鏈路 - 打磨細節(jié)”的順序走下來你得到的收獲絕對不只是那一紙成績單。你會在不知不覺里搞明白什么叫事務、什么叫狀態(tài)機、什么叫權限模型而這些概念寫在簡歷上比任何修飾詞都更有說服力。這套藥房購藥系統(tǒng)不算難但也不簡單它的業(yè)務閉環(huán)天然適合作為JavaWeb的綜合性訓練項目。不管你是打算照著這個思路自己寫一遍還是拿到了完整源碼想把它吃透改成自己的東西我的建議都一樣把每個模塊的代碼讀一遍自己動手改兩個功能把數(shù)據(jù)庫里的數(shù)據(jù)查一查然后在答辯前自己把系統(tǒng)從頭到尾操作三遍。做到這個程度你就能自信地說——這個項目里每一行代碼的意圖你都清楚。祝順利。