場(chǎng)管理系統(tǒng)畢設(shè)全解析:從架構(gòu)設(shè)計(jì)到部署實(shí)戰(zhàn))
做畢設(shè)選“SpringBootVue停車(chē)場(chǎng)管理系統(tǒng)”這個(gè)題目的人這兩年是真的多。原因也很實(shí)在技術(shù)棧主流、業(yè)務(wù)場(chǎng)景清晰、前后端分離的架構(gòu)踩中當(dāng)下企業(yè)開(kāi)發(fā)的主流范式答辯時(shí)既能講業(yè)務(wù)又能講技術(shù)怎么都有話說(shuō)。不過(guò)題目選得容易真正落地卻有不少坑——版本兼容、權(quán)限控制、數(shù)據(jù)庫(kù)設(shè)計(jì)、打包部署每一步都有可能卡住你三五天。我前前后后幫人看過(guò)不少這個(gè)題目的代碼自己也完整搭過(guò)一版這篇就把整個(gè)項(xiàng)目的設(shè)計(jì)和實(shí)操細(xì)節(jié)拆開(kāi)揉碎講一遍給正在做這個(gè)畢設(shè)的同學(xué)當(dāng)個(gè)參考。先說(shuō)清楚這套系統(tǒng)是做什么的管理員登錄后臺(tái)維護(hù)車(chē)位信息、管理月卡用戶、設(shè)置計(jì)費(fèi)規(guī)則用戶端可以注冊(cè)登錄、綁定車(chē)牌、查看車(chē)位余量、繳納停車(chē)費(fèi)、申請(qǐng)?jiān)驴āU麄€(gè)系統(tǒng)跑在SpringBootJPA/MyBatis這套后端上前端用Vue全家桶做SPA單頁(yè)應(yīng)用通過(guò)RESTful API通信。麻雀雖小五臟俱全該有的模塊一個(gè)不少用來(lái)做畢業(yè)論文的技術(shù)承載面足夠?qū)挾颐總€(gè)模塊都能引出對(duì)應(yīng)的技術(shù)點(diǎn)寫(xiě)到論文里不愁沒(méi)有內(nèi)容。1. 項(xiàng)目定位與整體設(shè)計(jì)思路1.1 為什么選SpringBootVue這套組合畢業(yè)設(shè)計(jì)的技術(shù)選型第一原則不是“哪個(gè)最屌”而是“哪個(gè)最穩(wěn)、最能讓你順利畢業(yè)”。SpringBootVue正好踩在這個(gè)平衡點(diǎn)上。從后端看SpringBoot把Spring的配置地獄徹底壓平了你不需要理解復(fù)雜的XML配置幾個(gè)注解就能把接口跑起來(lái)。這對(duì)精力上要兼顧論文和找工作的應(yīng)屆生來(lái)說(shuō)非常友好。更關(guān)鍵的是SpringBoot的自動(dòng)裝配原理、內(nèi)嵌Tomcat、Starter機(jī)制這些都是答辯時(shí)的經(jīng)典提問(wèn)點(diǎn)屬于“你稍微準(zhǔn)備一下就能答好”的送分題。從前端看Vue的學(xué)習(xí)曲線比React平滑中文資料多遇到問(wèn)題一搜就有答案。Vue的雙向綁定讓表單處理變得很直觀路由、狀態(tài)管理、組件通信這些概念理解起來(lái)也不費(fèi)勁。更重要的是Vue全家桶你能寫(xiě)明白“響應(yīng)式原理”“虛擬DOM”“路由守衛(wèi)”這幾個(gè)點(diǎn)論文的技術(shù)分析章節(jié)就有著落了。這套組合還有一層隱性?xún)?yōu)勢(shì)社區(qū)活躍度極高。你寫(xiě)到某一步卡住了不管是CSDN還是GitHub幾乎都能找到現(xiàn)成的解決方案不會(huì)像小眾框架那樣問(wèn)遍全網(wǎng)無(wú)應(yīng)答。1.2 功能模塊拆解從業(yè)務(wù)需求推導(dǎo)架構(gòu)做畢設(shè)不要一上來(lái)就寫(xiě)代碼先把業(yè)務(wù)流程理清楚。停車(chē)場(chǎng)管理系統(tǒng)核心是“車(chē)進(jìn)場(chǎng)、車(chē)出場(chǎng)、收錢(qián)、管卡”這個(gè)閉環(huán)圍繞它展開(kāi)功能模塊管理員端車(chē)位管理增刪改查車(chē)位、設(shè)置車(chē)位類(lèi)型、訂單管理查看收費(fèi)記錄、處理異常訂單、月卡管理審核月卡申請(qǐng)、續(xù)期、退卡、用戶管理禁用/啟用賬號(hào)、計(jì)費(fèi)規(guī)則設(shè)置按時(shí)計(jì)費(fèi)或按次計(jì)費(fèi)、數(shù)據(jù)統(tǒng)計(jì)看板今日營(yíng)收、車(chē)輛進(jìn)出趨勢(shì)、車(chē)位利用率。用戶端注冊(cè)登錄、綁定車(chē)牌、實(shí)時(shí)車(chē)位余量查看、停車(chē)記錄查詢(xún)、在線繳費(fèi)微信/支付寶模擬支付即可、月卡申請(qǐng)與續(xù)費(fèi)、個(gè)人資料維護(hù)。模塊劃分上建議遵循“高內(nèi)聚、低耦合”。車(chē)、訂單、用戶、計(jì)費(fèi)這四塊盡量獨(dú)立成package各模塊之間只通過(guò)Service接口交互不要互相直接拆內(nèi)部實(shí)現(xiàn)。這樣寫(xiě)的好處是第一你論文里可以畫(huà)出清晰的模塊架構(gòu)圖答辯時(shí)有東西可講第二后期維護(hù)和加功能不會(huì)牽一發(fā)而動(dòng)全身。我見(jiàn)過(guò)很多人的畢設(shè)代碼是Controller里堆了兩千行所有業(yè)務(wù)邏輯全寫(xiě)在接口里數(shù)表和訂單表強(qiáng)耦合。這種代碼跑起來(lái)沒(méi)問(wèn)題但答辯時(shí)老師看一眼源碼問(wèn)一句“你這個(gè)層與層的職責(zé)怎么劃分的”基本就啞火了。所以設(shè)計(jì)階段多花半天畫(huà)清楚模塊關(guān)系圖后面寫(xiě)代碼能省不止兩天。1.3 技術(shù)棧選型與版本選擇別小看這個(gè)環(huán)節(jié)技術(shù)棧列表是論文里最顯眼的部分也是答辯老師最?lèi)?ài)盯著看的部分。推薦一套親測(cè)穩(wěn)的組合層級(jí)技術(shù)選型說(shuō)明前端框架Vue 2.6 Vue Router 3 Vuex用Vue 2是因?yàn)榻坛套疃唷⒆畛墒霦lement UI也完美兼容Vue 3項(xiàng)目如果時(shí)間充裕也可以但配套組件庫(kù)要先確認(rèn)兼容性UI組件庫(kù)Element UI表格、表單、對(duì)話框都有現(xiàn)成組件幾行代碼就能拼出后臺(tái)管理界面省下大把寫(xiě)CSS的時(shí)間后端框架SpringBoot 2.5.xJDK8最高兼容組合網(wǎng)上的教程大部分基于這個(gè)版本遇到問(wèn)題容易搜到答案你非要上Spring Boot 3.x就得配JDK17很多坑等著你填ORM層MyBatis-Plus單表CRUD不用寫(xiě)SQL自帶分頁(yè)插件比原生MyBatis省一半工作量數(shù)據(jù)庫(kù)MySQL 5.7 / 8.05.7穩(wěn)定8.0性能更好兩個(gè)版本語(yǔ)法上基本無(wú)感隨便選認(rèn)證方案JWT Spring Security無(wú)狀態(tài)認(rèn)證前后端分離項(xiàng)目標(biāo)配答辯能講的東西多構(gòu)建工具M(jìn)aven 3.6.主流中的主流IDEA內(nèi)置支持直接用它build就行這里特別提醒一句版本不是越新越好尤其是畢設(shè)這個(gè)場(chǎng)景。你在搜索時(shí)如果發(fā)現(xiàn)“SpringBoot版本太高”引發(fā)配置類(lèi)找不到、依賴(lài)沖突等問(wèn)題大概率是用了3.x結(jié)果自己還沒(méi)意識(shí)到JDK版本沒(méi)對(duì)上。老老實(shí)實(shí)鎖死版本號(hào)按上表來(lái)能省掉大量不必要的排查時(shí)間。前端也一樣不要一上來(lái)就去追Vue 3 Vite的組合等你把Vue 2這套跑通了原理都弄明白了再去聊新特性不遲。2. 核心技術(shù)與實(shí)現(xiàn)細(xì)節(jié)解析2.1 后端SpringBoot自動(dòng)裝配原理與項(xiàng)目分層后端這塊我先說(shuō)幾個(gè)大家容易忽略但答辯會(huì)問(wèn)到的點(diǎn)。SpringBoot自動(dòng)裝配到底自動(dòng)了什么簡(jiǎn)單說(shuō)SpringBoot用EnableAutoConfiguration注解開(kāi)啟自動(dòng)裝配這個(gè)注解通過(guò)Import(AutoConfigurationImportSelector.class)去加載META-INF/spring.factories文件里聲明的所有配置類(lèi)然后通過(guò)ConditionalOnClass、ConditionalOnProperty這類(lèi)條件注解按當(dāng)前classpath和配置動(dòng)態(tài)決定哪些Bean要?jiǎng)?chuàng)建。比如你引入了spring-boot-starter-webclasspath下有DispatcherServlet它就自動(dòng)把WebMvc相關(guān)的那套Bean裝配好。放到你的停車(chē)場(chǎng)系統(tǒng)里這個(gè)原理體現(xiàn)在哪在你引入spring-boot-starter-data-jpa或MyBatis-Starter的時(shí)候SpringBoot自動(dòng)幫你裝配好了DataSource、SqlSessionFactory、事務(wù)管理器。你不需要寫(xiě)一行XML配置只需要在application.yml里填好數(shù)據(jù)庫(kù)連接信息就行。這個(gè)“自動(dòng)”的背后就是條件裝配在起作用。后端分層這塊無(wú)論如何都要拆干凈。我習(xí)慣按這套結(jié)構(gòu)組織代碼com.example.parking ├── controller // 接收請(qǐng)求參數(shù)校驗(yàn)返回結(jié)果 ├── service // 業(yè)務(wù)邏輯層事務(wù)控制在這里 ├── mapper/repository // 數(shù)據(jù)訪問(wèn)層 ├── entity/model // 實(shí)體類(lèi) ├── dto // 數(shù)據(jù)傳輸對(duì)象避免實(shí)體類(lèi)直接暴露給前端 ├── config // 配置類(lèi)如SecurityConfig、CorsConfig ├── common // 統(tǒng)一返回結(jié)果、異常處理、工具類(lèi) └── utils // JWT工具、日期工具等Controller只負(fù)責(zé)“翻譯”HTTP請(qǐng)求和業(yè)務(wù)層調(diào)用不寫(xiě)任何具體業(yè)務(wù)判斷Service負(fù)責(zé)業(yè)務(wù)規(guī)則比如計(jì)費(fèi)邏輯、月卡校驗(yàn)Mapper做純粹的數(shù)據(jù)讀寫(xiě)。這么拆完論文里寫(xiě)系統(tǒng)架構(gòu)設(shè)計(jì)時(shí)直接套三層架構(gòu)的圖例說(shuō)明就行說(shuō)出去體面代碼也好維護(hù)。事務(wù)控制是必考題。停車(chē)?yán)U費(fèi)這個(gè)場(chǎng)景就是典型事務(wù)需求——生成訂單、更新錢(qián)包余額、更新車(chē)位的狀態(tài)三步必須同時(shí)成功或同時(shí)失敗。在Service層的pay()方法上標(biāo)注Transactional就好了。答辯時(shí)老師問(wèn)“你這個(gè)系統(tǒng)哪里用到了事務(wù)”你把這個(gè)案例拋出來(lái)然后順便說(shuō)出事務(wù)的ACID四大特性這就是加分項(xiàng)。2.2 數(shù)據(jù)庫(kù)設(shè)計(jì)三范式、核心表結(jié)構(gòu)與索引策略數(shù)據(jù)庫(kù)設(shè)計(jì)直接決定你后期寫(xiě)代碼時(shí)是絲滑還是痛苦。停車(chē)場(chǎng)管理系統(tǒng)我建議設(shè)計(jì)這幾張核心表用戶表user_id主鍵username唯一索引passwordBCrypt加密存儲(chǔ)phonerole區(qū)分管理員/普通用戶status啟用/禁用create_time。車(chē)位表space_id主鍵space_no唯一location位置描述type普通/VIP/新能源充電車(chē)位status空閑/占用/維修price_per_hour。訂單表order_id主鍵order_no業(yè)務(wù)編號(hào)user_id關(guān)聯(lián)用戶plate_number車(chē)牌號(hào)space_id關(guān)聯(lián)車(chē)位start_time、end_timetotal_amountstatus進(jìn)行中/已完成/已取消/異常。月卡表card_id主鍵user_idplate_numberstart_dateend_datestatus待審核/生效/過(guò)期/退卡注意一個(gè)用戶可能有多輛車(chē)所以車(chē)牌號(hào)要獨(dú)立存。計(jì)費(fèi)規(guī)則表rule_idrule_nameunit_priceunit_type按小時(shí)/按次max_daily_charge封頂值start_time/end_time可設(shè)置夜間優(yōu)惠時(shí)段。設(shè)計(jì)時(shí)有三條建議第一時(shí)間字段統(tǒng)一用datetime或timestamp別用字符串存后續(xù)計(jì)算停車(chē)時(shí)長(zhǎng)、統(tǒng)計(jì)營(yíng)收會(huì)非常崩潰第二價(jià)格字段用decimal(10,2)絕不用float浮點(diǎn)數(shù)的精度問(wèn)題在計(jì)費(fèi)場(chǎng)景是不可接受的第三常用的查詢(xún)條件字段記得加索引——用戶停車(chē)記錄按user_id查車(chē)位列表按status篩選訂單表按order_no查明細(xì)這些都是高頻率查詢(xún)。索引不是越多越好每個(gè)表3~5個(gè)就夠?qū)懖僮黝l繁的表再多就反而拖慢性能了。2.3 前端Vue路由配置、插槽與組件通信前端這塊很多人上來(lái)就寫(xiě)頁(yè)面寫(xiě)到一半發(fā)現(xiàn)代碼亂成一團(tuán)。先用組件化的思路拆頁(yè)面結(jié)構(gòu)views/Login.vue登錄/注冊(cè)頁(yè)views/UserHome.vue用戶端首頁(yè)車(chē)位余量、入場(chǎng)繳費(fèi)、月卡申請(qǐng)views/AdminDashboard.vue管理員工作臺(tái)數(shù)據(jù)統(tǒng)計(jì)views/Admin/OrderList.vue訂單管理views/Admin/SpaceManage.vue車(chē)位管理views/Admin/CardManage.vue月卡管理路由配置要分角色這是Vue路由的核心考點(diǎn)。用戶和管理員看到的頁(yè)面不一樣不能就粗暴地把所有路由堆在一起。做法是利用Vue Router的meta字段配合前置守衛(wèi)// router/index.js const routes [ { path: /login, component: Login }, { path: /, component: Layout, redirect: /dashboard, meta: { requiresAuth: true, roles: [ADMIN] }, children: [ { path: dashboard, component: AdminDashboard }, { path: orders, component: OrderList } ] }, { path: /user, component: Layout, redirect: /user/home, meta: { requiresAuth: true, roles: [USER] }, children: [ { path: home, component: UserHome } ] } ] router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else if (token to.meta.roles !to.meta.roles.includes(store.state.role)) { next(/403) // 沒(méi)權(quán)限跳轉(zhuǎn) } else { next() } })路由守衛(wèi)幾乎是前端面試題的??湍隳苤v明白“它是怎么攔截未登錄用戶的”“如何按角色控制訪問(wèn)”答辯時(shí)這一塊就穩(wěn)了。如果再能說(shuō)說(shuō)動(dòng)態(tài)路由也就是從后端按角色返回路由表、前端router.addRoutes動(dòng)態(tài)注冊(cè)那技術(shù)深度一下子就上去了。組件通信是Vue的另一個(gè)高頻考點(diǎn)。停車(chē)余量這個(gè)數(shù)據(jù)用戶首頁(yè)、管理員看板、車(chē)位狀態(tài)列表都要用。這種跨組件共享的狀態(tài)就別用props一級(jí)級(jí)傳了直接用Vuex管理。在store里定義parkingSpaces模塊接口返回后commit到state任何組件里mapGetters一下就能用。至于$emit自定義事件傳參適合父子組件間簡(jiǎn)單交互比如“點(diǎn)擊車(chē)位格子觸發(fā)彈窗顯示詳情”。插槽這塊容易被忽略但實(shí)際用起來(lái)很香。我在做管理員訂單列表時(shí)Element UI的el-table里需要根據(jù)不同訂單狀態(tài)顯示不同顏色和操作按鈕待支付、已完成、異常單。這時(shí)候就用到插槽el-table-column label狀態(tài) template #default{ row } el-tag :typestatusTagMap[row.status]{{ statusTextMap[row.status] }}/el-tag /template /el-table-column這樣你就不需要為每一種狀態(tài)單獨(dú)寫(xiě)一列數(shù)據(jù)和展示分離代碼簡(jiǎn)潔也不會(huì)重復(fù)。答辯講到“組件復(fù)用”的時(shí)候把插槽和具名插槽舉出來(lái)當(dāng)例子說(shuō)服力很強(qiáng)。3. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 后端工程搭建從IDEA新建到第一個(gè)接口跑通這一步我會(huì)手把手拆解實(shí)際操作過(guò)程因?yàn)楹芏嗳丝ㄔ谧铋_(kāi)始的環(huán)境配置上。第一步本地環(huán)境檢查。JDK必須是1.8這個(gè)很重要——你裝了JDK17再配SpringBoot 2.5就會(huì)出問(wèn)題。在終端敲java -version確認(rèn)。接著裝Maven 3.6或3.8不一定要單獨(dú)裝IDEA自帶Bundled Maven也能用但用獨(dú)立Maven更方便管理本地倉(cāng)庫(kù)。第二步在IDEA里新建項(xiàng)目。選擇Spring Initializr填好Group和Artifact依賴(lài)這里沒(méi)必要勾太多——先勾Spring Web、MyBatis Framework或Spring Data JPA、MySQL Driver、Lombok這幾個(gè)。Lombok一定要勾它能用注解幫你自動(dòng)生成getter/setter省掉的代碼量是肉眼可見(jiàn)的。第三步配application.yml。注意數(shù)據(jù)庫(kù)連接串的寫(xiě)法server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/parking_system?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImplserverTimezoneAsia/Shanghai這個(gè)參數(shù)不加數(shù)據(jù)庫(kù)連接會(huì)報(bào)時(shí)區(qū)錯(cuò)誤這是新手最常遇到的經(jīng)典坑之一。log-impl配置成StdOutImpl后控制臺(tái)能直接看到SQL日志調(diào)試聯(lián)調(diào)的時(shí)候太方便了。第四步寫(xiě)第一個(gè)接口“查詢(xún)空閑車(chē)位列表”。Controller、Service、Mapper各層各寫(xiě)幾行代碼。跑起來(lái)后瀏覽器訪問(wèn)http://localhost:8080/api/spaces/available能在JSON里看到數(shù)據(jù)就說(shuō)明后端全鏈路通了。這一步的意義在于后端的骨架搭起來(lái)了后面每加一個(gè)模塊都按同樣的方式復(fù)制粘貼就行。我個(gè)人的習(xí)慣是每寫(xiě)完一個(gè)模塊就啟動(dòng)跑一遍絕不等全部寫(xiě)完再啟動(dòng)。真等到全寫(xiě)完再調(diào)一百個(gè)報(bào)錯(cuò)疊在一起排查起來(lái)會(huì)讓人崩潰。3.2 前端工程搭建Vue安裝、依賴(lài)配置與開(kāi)發(fā)環(huán)境避坑前端這塊很多同學(xué)掛在環(huán)境配置上所以我重點(diǎn)說(shuō)一下踩坑細(xì)節(jié)。Vue環(huán)境的完整安裝路徑先裝Node.js注意版本不要用最新的推薦16.x的LTS版本。裝完了終端敲node -v和npm -v確認(rèn)。然后安裝Vue CLI腳手架npm install -g vue/cli。如果這一步在國(guó)內(nèi)網(wǎng)絡(luò)環(huán)境下很慢就配一下npm淘寶鏡像源npm config set registry https://registry.npmmirror.com。這是必須的一步否則你后面npm install裝依賴(lài)可能要等到天荒地老。創(chuàng)建項(xiàng)目在項(xiàng)目根目錄下執(zhí)行vue create parking-web。選擇Manually select features手動(dòng)勾選Vue Router、VuexCSS預(yù)處理器我習(xí)慣選Less。這樣創(chuàng)建出來(lái)的項(xiàng)目自帶路由和狀態(tài)管理的目錄結(jié)構(gòu)不用自己從零搭。注意靈犀一點(diǎn)Vue CLI 5.x版本創(chuàng)建的是Vue 3項(xiàng)目如果你想按上文說(shuō)的用Vue 2創(chuàng)建時(shí)選Vue CLI的Preset或版本選項(xiàng)或者直接在腳手架里手動(dòng)指定Vue版本這個(gè)細(xì)節(jié)一定要提前確認(rèn)。裝Element UI和Axiosnpm install element-ui -S npm install axios -S npm install echarts -S # 后面做統(tǒng)計(jì)圖表用封axios請(qǐng)求和統(tǒng)一Token處理在src/utils/request.js里創(chuàng)建一個(gè)axios實(shí)例設(shè)置baseURL為http://localhost:8080/api。請(qǐng)求攔截器里從localStorage取token塞到請(qǐng)求頭響應(yīng)攔截器里處理403、500等錯(cuò)誤碼同時(shí)把后端返回的結(jié)構(gòu)統(tǒng)一剝離。service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response { if (response.data.code ! 200) { Message.error(response.data.message) return Promise.reject(new Error(response.data.message)) } return response.data }, error { if (error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )這一套寫(xiě)完所有頁(yè)面直接調(diào)用request.get(/xx)就行不再需要每個(gè)接口都重復(fù)寫(xiě)token邏輯。答辯時(shí)老師問(wèn)你“你怎么做用戶認(rèn)證的”從這個(gè)攔截器講起把JWT的流程完整說(shuō)出來(lái)深度就夠了。開(kāi)發(fā)環(huán)境跨域解決前后端分離開(kāi)發(fā)時(shí)前端地址是localhost:3000后端接口是localhost:8080瀏覽器默認(rèn)會(huì)攔截跨域請(qǐng)求。最簡(jiǎn)單的方案是在前端vue.config.js里配置devServer的proxymodule.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }這樣前端的/api請(qǐng)求會(huì)被代理到后端地址瀏覽器視角是“同源請(qǐng)求”跨域問(wèn)題直接消解。同時(shí)后端也要配置CORS策略雙保險(xiǎn)。這個(gè)點(diǎn)特別重要因?yàn)楹芏嗳寺?lián)調(diào)時(shí)一看瀏覽器報(bào)跨域就懵實(shí)際上就是少配了這一個(gè)小文件。3.3 核心業(yè)務(wù)邏輯實(shí)現(xiàn)計(jì)費(fèi)規(guī)則、月卡校驗(yàn)與數(shù)據(jù)統(tǒng)計(jì)停車(chē)計(jì)費(fèi)是業(yè)務(wù)邏輯的核心也是答辯時(shí)最容易展示技術(shù)含量的地方。我建議這樣設(shè)計(jì)計(jì)費(fèi)服務(wù)入場(chǎng)記錄startTime出場(chǎng)時(shí)先算出時(shí)長(zhǎng)然后調(diào)用計(jì)費(fèi)規(guī)則模塊按規(guī)則計(jì)算金額。// 簡(jiǎn)單演示實(shí)際項(xiàng)目要結(jié)合規(guī)則表動(dòng)態(tài)計(jì)算 Transactional public PayResult calculateAndPay(String orderNo) { Order order orderMapper.findByOrderNo(orderNo); long hours ChronoUnit.HOURS.between(order.getStartTime(), LocalDateTime.now()); // 不足一小時(shí)按一小時(shí)算這是簡(jiǎn)化的取整邏輯 if (hours 0) { hours 1; } // 如果有月卡且未過(guò)期覆蓋計(jì)費(fèi) MonthlyCard card cardMapper.findByPlateAndStatus( order.getPlateNumber(), ACTIVE); if (card ! null) { order.setTotalAmount(BigDecimal.ZERO); order.setStatus(COMPLETED); return new PayResult(order, MEMBER_CARD); } // 普通計(jì)費(fèi)調(diào)用規(guī)則表里的單價(jià) BigDecimal amount ruleMapper.getCurrentRule() .getUnitPrice().multiply(BigDecimal.valueOf(hours)); // 封頂邏輯 ... order.setTotalAmount(amount); order.setStatus(COMPLETED); orderMapper.updateById(order); spaceMapper.updateStatus(order.getSpaceId(), FREE); return new PayResult(order, PAID); }這個(gè)方法的亮點(diǎn)在于把“月卡優(yōu)先”和“按時(shí)段計(jì)費(fèi)”都體現(xiàn)出來(lái)了而且邏輯集中在Service層。答辯時(shí)你能說(shuō)明白“為什么要放在當(dāng)前這個(gè)方法而非拆分到Controller”“事務(wù)為什么加在這一層”那說(shuō)明你真的理解了這個(gè)系統(tǒng)的運(yùn)轉(zhuǎn)方式。數(shù)據(jù)統(tǒng)計(jì)看板是論文的亮點(diǎn)展示位。用ECharts畫(huà)幾個(gè)圖——近一周營(yíng)收柱狀圖、24小時(shí)車(chē)輛進(jìn)出折線圖、車(chē)位類(lèi)型利用率餅圖。后端提供對(duì)應(yīng)的聚合查詢(xún)接口比如用SQL的DATE_FORMAT分組統(tǒng)計(jì)每天營(yíng)收SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS day, SUM(total_amount) AS revenue FROM parking_order WHERE status COMPLETED GROUP BY DATE_FORMAT(create_time, %Y-%m-%d) ORDER BY day前端拿到數(shù)組以后用ECharts幾行代碼就能渲染出來(lái)。這塊呈現(xiàn)到論文上是“可視化數(shù)據(jù)展示”放到系統(tǒng)演示環(huán)節(jié)是“哇有個(gè)大屏看板”投入產(chǎn)出比極高。3.4 發(fā)布部署Vue打包放進(jìn)SpringBoot里畢設(shè)最后驗(yàn)收通常需要交付一個(gè)能直接跑起來(lái)演示的完整系統(tǒng)。兩邊分開(kāi)部署不現(xiàn)實(shí)最簡(jiǎn)單可靠的方式是把Vue打包后的靜態(tài)文件放進(jìn)SpringBoot里用一個(gè)8080端口搞定所有。第一步前端打包。在項(xiàng)目根目錄執(zhí)行npm run build靜態(tài)資源會(huì)輸出到dist文件夾。如果你的路由用了history模式在vue.config.js里配一下publicPath: ./否則部署后靜態(tài)資源路徑會(huì)找不到頁(yè)面白屏。這一步很多教程不會(huì)細(xì)說(shuō)但是幾乎每個(gè)人都會(huì)踩到。第二步把dist整個(gè)文件夾復(fù)制到SpringBoot的src/main/resources/static目錄下。SpringBoot默認(rèn)情況下靜態(tài)文件放在這個(gè)目錄就能直接通過(guò)localhost:8080訪問(wèn)到。第三步做一個(gè)兜底路由。Vue是SPA前端路由由js控制如果用戶直接訪問(wèn)http://localhost:8080/admin/dashboard后端兜底到index.html由前端再接管路由。配置方式是在后端加一個(gè)ViewContorller或者直接寫(xiě)一個(gè)Controller處理轉(zhuǎn)發(fā)Controller public class PageForwardController { RequestMapping(value /**/{path:[^\\.]*}) public String forward() { return forward:/index.html; } }注意這里的正則表達(dá)式排除了帶點(diǎn)的路徑不會(huì)影響接口請(qǐng)求和靜態(tài)資源文件本身。第四步打包后端。在IDEA右側(cè)Maven面板里執(zhí)行clean再執(zhí)行package生成target目錄下那個(gè)parking-0.0.1-SNAPSHOT.jar。然后在命令行里執(zhí)行java -jar parking-0.0.1-SNAPSHOT.jar。瀏覽器打開(kāi)http://localhost:8080就能看到完整的系統(tǒng)了。這一個(gè)Jar包就能演示全部功能驗(yàn)收現(xiàn)場(chǎng)只需要一臺(tái)電腦。4. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄做了這么多次類(lèi)似項(xiàng)目我把最典型的問(wèn)題整理成速查表你可以直接拿來(lái)當(dāng)排障手冊(cè)用?,F(xiàn)象大概率原因解決方案SpringBoot項(xiàng)目啟動(dòng)失敗提示DataSource相關(guān)錯(cuò)誤沒(méi)配數(shù)據(jù)庫(kù)連接或MySQL沒(méi)啟動(dòng)檢查application.yml配置確認(rèn)MySQL服務(wù)已啟動(dòng)賬號(hào)密碼正確前端頁(yè)面白屏控制臺(tái)報(bào)靜態(tài)資源404publicPath配置不對(duì)或dist路徑不對(duì)Vue項(xiàng)目設(shè)publicPath: ./重新打包目錄確認(rèn)放到static根路徑下列表頁(yè)能打開(kāi)點(diǎn)擊查詢(xún)后報(bào)跨域錯(cuò)誤前后端跨域未處理前端配devServer.proxy后端配CorsConfig雙重保障登錄失敗一直提示用戶名或密碼錯(cuò)誤密碼沒(méi)做加密導(dǎo)致比對(duì)不一致后端確認(rèn)用的是BCryptPasswordEncoder加密和matches校驗(yàn)保存訂單后事務(wù)不生效Service類(lèi)上沒(méi)加Transactional或方法不是public檢查注解位置注意Spring對(duì)非public方法的代理限制接口報(bào)500但SQL日志沒(méi)打印MyBatis映射文件里的SQL有誤或resultType錯(cuò)誤打開(kāi)log-impl: StdOutImpl看完整SQL在Navicat里手動(dòng)跑一遍驗(yàn)證上傳的圖片顯示不了路徑不對(duì)靜態(tài)資源映射沒(méi)配置或路徑拼接錯(cuò)誤確認(rèn)圖片保存路徑和訪問(wèn)路徑一致用絕對(duì)URI拼接訪問(wèn)地址排查思路心法遇到報(bào)錯(cuò)先看最上層的異常信息不要慌著翻日志全文。大多數(shù)情況是“配置缺失”或“依賴(lài)版本沖突”兩個(gè)原因之一。SpringBoot的報(bào)錯(cuò)信息其實(shí)寫(xiě)得很貼心它會(huì)直接告訴你缺什么類(lèi)、少什么配置。按著提示一步步排查比漫無(wú)目的地搜“SpringBoot xxx報(bào)錯(cuò)”要快得多。另外說(shuō)兩個(gè)容易忽視的坑第一寫(xiě)SQL時(shí)表名和字段名盡量不要和MySQL關(guān)鍵字沖突。比如我見(jiàn)過(guò)有人把表名命名為order結(jié)果插入時(shí)永遠(yuǎn)報(bào)語(yǔ)法錯(cuò)誤因?yàn)閛rder是保留字。要么加反引號(hào)要么直接叫parking_order后者更省心。第二JWT密鑰不要寫(xiě)在代碼里硬編碼。放到application.yml里用Value注入論文里可以順帶提一句“使用配置化密鑰管理提高安全性”顯得考慮周全。同理數(shù)據(jù)庫(kù)密碼也不要寫(xiě)死在代碼里配置化是最好的習(xí)慣。答辯前一周過(guò)一遍技術(shù)問(wèn)答清單SpringBoot自動(dòng)裝配的原理是什么說(shuō)說(shuō)SpringMVC的處理流程從請(qǐng)求到響應(yīng)的完整過(guò)程。JWT認(rèn)證相比Session的優(yōu)勢(shì)在哪logout如何實(shí)現(xiàn)Vue響應(yīng)式數(shù)據(jù)的原理是什么路由守衛(wèi)在什么場(chǎng)景下用到怎么實(shí)現(xiàn)登錄攔截?cái)?shù)據(jù)庫(kù)的索引在哪些字段上建了為什么全部能用自己的話講一遍答辯基本穩(wěn)過(guò)。再補(bǔ)充一個(gè)經(jīng)驗(yàn)別忘了在GitHub上建個(gè)倉(cāng)庫(kù)從第一天就把代碼推上去。這不僅是版本管理的問(wèn)題論文里你可以寫(xiě)“項(xiàng)目使用Git進(jìn)行版本控制”然后截一張?zhí)峤粴v史圖放進(jìn)開(kāi)發(fā)工具章節(jié)真實(shí)感直接拉滿。而且萬(wàn)一代碼寫(xiě)崩了能隨時(shí)回滾到前一天能跑的版本這個(gè)安全感是任何本地備份都給不了的。我個(gè)人最后一次完整做這套系統(tǒng)的時(shí)候最有感觸的一點(diǎn)是真正花費(fèi)時(shí)間的不是寫(xiě)代碼本身而是前后端的接口定義和字段對(duì)齊。所以我會(huì)建議你們?cè)趧?dòng)手之前先把所有接口的請(qǐng)求參數(shù)、響應(yīng)結(jié)構(gòu)用表格列出來(lái)比如“POST /api/user/login 參數(shù)username,password 返回token,role,userInfo”。前端按這張表調(diào)開(kāi)發(fā)后端按這張表做實(shí)現(xiàn)兩邊各干各的也不會(huì)跑偏聯(lián)調(diào)時(shí)間能縮到極短。這個(gè)習(xí)慣哪怕畢業(yè)之后進(jìn)了團(tuán)隊(duì)做開(kāi)發(fā)一樣是剛需。最后說(shuō)一句實(shí)在話畢設(shè)的意義不在于做出一個(gè)完美無(wú)缺的商業(yè)系統(tǒng)而在于讓你完整體驗(yàn)一遍“從需求分析到設(shè)計(jì)、開(kāi)發(fā)、測(cè)試、部署”的全流程。這個(gè)SpringBootVue停車(chē)場(chǎng)管理系統(tǒng)恰好能把這些環(huán)節(jié)都覆蓋到而且每一環(huán)都有足夠深的技術(shù)點(diǎn)可以挖。把這篇里面提到的細(xì)節(jié)吃透你不僅是在完成一個(gè)題目還在畢業(yè)前把一名后端開(kāi)發(fā)最核心的技能棧扎扎實(shí)實(shí)過(guò)了一遍。這套積累的含金量比成績(jī)單上的那個(gè)分?jǐn)?shù)值錢(qián)多了。