商鋪管理系統(tǒng):Spring Boot+Vue+MySQL全棧實(shí)戰(zhàn)解析)
社區(qū)商鋪管理系統(tǒng)這類畢設(shè)題目每年都能見到不少但真正做扎實(shí)的其實(shí)不多。我前后參與評審和帶過幾個類似的畢業(yè)設(shè)計(jì)項(xiàng)目這套“社區(qū)商鋪管理系統(tǒng)”算是比較典型的一套完整方案后端是Spring Boot前端是Vue加Element UI數(shù)據(jù)庫用的MySQL覆蓋商鋪檔案、租戶管理、合同臺賬、費(fèi)用收繳、報(bào)修跟蹤等核心模塊。不管你是正在做畢設(shè)需要參考還是想快速搭一套輕量級的商鋪管理后臺這篇文章會把設(shè)計(jì)思路、數(shù)據(jù)庫結(jié)構(gòu)、關(guān)鍵實(shí)現(xiàn)、部署步驟和答辯常見追問一次講清楚。這系統(tǒng)解決的核心問題很具體社區(qū)里幾十上百間商鋪如果只靠Excel表格管理租期、租金、水電費(fèi)動不動就對不上賬。把商鋪信息、租戶關(guān)系、合同狀態(tài)、費(fèi)用記錄全部落到一個系統(tǒng)里誰在租、租到什么時候、欠了多少錢、哪間鋪?zhàn)涌罩蜷_后臺一眼就能看清。對做畢設(shè)的同學(xué)來說這類系統(tǒng)的業(yè)務(wù)邏輯不復(fù)雜但鏈條完整特別適合用來展示從需求分析到數(shù)據(jù)庫設(shè)計(jì)再到前后端編碼的完整能力。下面我按實(shí)際動手做這個項(xiàng)目的思路來講不繞彎子。1. 項(xiàng)目定位與需求拆解1.1 這類系統(tǒng)到底在管什么先說場景。社區(qū)商鋪的形態(tài)很多有小區(qū)底商、社區(qū)商業(yè)街、產(chǎn)業(yè)園區(qū)配套商業(yè)區(qū)少則十幾間多則上百間。管理方往往是物業(yè)公司或者社區(qū)運(yùn)營方他們?nèi)粘R幚淼牟皇恰百u東西”而是“管鋪?zhàn)印蹦拈g鋪?zhàn)幼獬鋈チ?、租給了誰、合同什么時候到期、這個月該收多少租金和水電費(fèi)、租戶提交的報(bào)修單處理到什么程度。這些信息如果散落在Excel和微信聊天記錄里時間一長必定對不上賬尤其是同時存在“已退租未結(jié)清”“合同已到期但租戶還在用”這類特殊情況時純粹靠人腦記根本沒有可行性。所以這套系統(tǒng)的核心價值就是把商鋪全生命周期管起來從錄入一間鋪?zhàn)拥幕拘畔㈤_始到找到租戶、簽合同收押金、每月自動生成應(yīng)收賬單、收款登記、續(xù)租或退租結(jié)算整個鏈條每個環(huán)節(jié)都有對應(yīng)的數(shù)據(jù)記錄和狀態(tài)變更。對做畢設(shè)而言這個業(yè)務(wù)域特別合適因?yàn)樗恍枰獜?fù)雜算法但涉及多個實(shí)體的關(guān)聯(lián)、狀態(tài)的扭轉(zhuǎn)、統(tǒng)計(jì)報(bào)表的輸出能很好地體現(xiàn)一個開發(fā)者對業(yè)務(wù)建模和工程化的理解。1.2 核心功能模塊與業(yè)務(wù)流程我拆解這個系統(tǒng)時習(xí)慣先畫業(yè)務(wù)流程圖再動手寫代碼。整體上分為六大模塊商鋪管理維護(hù)商鋪檔案包括鋪號、樓層、面積、租金單價、物業(yè)費(fèi)單價、當(dāng)前狀態(tài)空閑/已租/裝修中/停用。租戶管理管理租戶基本信息包括姓名、手機(jī)號、證件號、緊急聯(lián)系人支持按名稱或手機(jī)號快速檢索。合同管理商鋪與租戶之間簽約、續(xù)租、退租、到期提醒。合同里記錄租期、月租金、押金、遞增規(guī)則。收費(fèi)管理按合同生成月度賬單收費(fèi)類型涵蓋租金、物業(yè)費(fèi)、水費(fèi)、電費(fèi)、滯納金支持線下收款后登記核銷、逾期標(biāo)記。報(bào)修管理租戶發(fā)起報(bào)修管理員派單、回訪、完結(jié)記錄處理人、費(fèi)用。系統(tǒng)管理登錄認(rèn)證、用戶管理、角色權(quán)限、操作日志。業(yè)務(wù)流程的主線是“建檔→出租→收費(fèi)→結(jié)算”。一間新鋪?zhàn)酉扔晒芾韱T建檔狀態(tài)設(shè)為“空閑”找到租戶后簽訂合同合同一旦生效系統(tǒng)自動把商鋪狀態(tài)改為“已租”同時根據(jù)合同起止日期生成首月的應(yīng)收賬單此后每月由定時任務(wù)或管理員手動觸發(fā)生成當(dāng)月的租金加物業(yè)費(fèi)賬單租戶繳費(fèi)后做收款登記賬單狀態(tài)變?yōu)椤耙牙U”合同到期時如果續(xù)簽就延長合同并調(diào)整租金如果不續(xù)簽辦理退租結(jié)算把押金抵扣未繳費(fèi)用后退還商鋪狀態(tài)改回“空閑”。這條鏈路把核心業(yè)務(wù)全部串起來了也是后面設(shè)計(jì)表結(jié)構(gòu)和接口的主線。1.3 選擇這個題目的優(yōu)勢與難點(diǎn)從畢設(shè)選題角度看“社區(qū)商鋪管理系統(tǒng)”的優(yōu)勢很明顯業(yè)務(wù)不冷門任何人一看題目就知道要做什么技術(shù)??梢赃x主流水準(zhǔn)前端Vue、后端Spring Boot、數(shù)據(jù)庫MySQL工作量大但完全可控演示效果好后臺界面上有表格、表單、圖表甚至可以做ECharts出租率趨勢圖答辯時能展示出很完整的項(xiàng)目成果。難度則集中在幾處一是合同與賬單的關(guān)聯(lián)邏輯尤其是跨月、跨年的賬單生成和費(fèi)用統(tǒng)計(jì)容易算錯二是權(quán)限控制要做到不同角色看不到不同菜單而不是只在頁面里隱藏按鈕三是時間字段的處理容易踩時區(qū)坑后面我會單獨(dú)講。明確這三個難點(diǎn)之后開發(fā)時就能有意識地避開。2. 技術(shù)選型與架構(gòu)設(shè)計(jì)2.1 為什么選Spring Boot加Vue前后端分離畢業(yè)設(shè)計(jì)的主流技術(shù)方案我對比過幾類純Servlet加JSP的傳統(tǒng)模式、SSM配合JSP、Spring Boot加Thymeleaf服務(wù)端渲染、Spring Boot加Vue前后端分離。我的建議是直接用Spring Boot加Vue這套理由很實(shí)際。第一前后端分離讓代碼組織更清晰。后端只暴露REST接口前端只做頁面交互兩邊都能單獨(dú)調(diào)試、單獨(dú)部署。答辯時你能夠把前后端分開展示講接口設(shè)計(jì)、講頁面組件每個部分都有自己的工作量不會被質(zhì)疑“是不是只有CRUD”。第二Spring Boot把配置簡化了很多。內(nèi)嵌Tomcat不用單獨(dú)裝容器一個jar包就能跑起來數(shù)據(jù)庫連接池、MyBatis、校驗(yàn)框架通過starter引入即可省去大量XML配置。對一個半年周期內(nèi)還要寫論文、畫圖、調(diào)格式的畢設(shè)來說時間要花在業(yè)務(wù)上而不是環(huán)境折騰上。第三Vue的生態(tài)成熟Element UI的組件覆蓋了后臺管理系統(tǒng)需要的表單、表格、彈窗、分頁、菜單、面包屑套上就能出一套像樣的界面。對非設(shè)計(jì)出身的開發(fā)者來說這是性價比最高的選擇。當(dāng)然如果你對Java不熟備選方案是Python的Django/Flask搭配Vue或者Node.js的Express前后端同構(gòu)但考慮到答辯時老師普遍更認(rèn)可Java系列而且社區(qū)里能找到的參考源碼和文檔最多我還是推薦Spring Boot。2.2 整體架構(gòu)與請求鏈路系統(tǒng)整體采用標(biāo)準(zhǔn)的前后端分離架構(gòu)。前端是Vue加Vue Router加Vuex加Axios加Element UI使用Vite構(gòu)建后端是Spring Boot加MyBatis-Plus加MySQL接口遵循RESTful風(fēng)格開發(fā)階段前端通過Vite代理轉(zhuǎn)發(fā)請求到后端的8080端口生產(chǎn)環(huán)境則把前后端分別部署。一條典型的請求鏈路是這樣用戶打開登錄頁輸入賬號密碼前端調(diào)用/api/login接口后端認(rèn)證成功后返回一個Token前端把Token存到localStorageAxios攔截器里每次請求都帶上Authorization頭后端通過攔截器校驗(yàn)Token解析出當(dāng)前用戶和角色再到Service層做業(yè)務(wù)判斷Mapper層執(zhí)行SQL最終把結(jié)果以統(tǒng)一返回結(jié)構(gòu){ code, message, data }返回給前端。這個統(tǒng)一返回結(jié)構(gòu)很重要前端可以直接根據(jù)code判斷成功與失敗不需要在每一個請求里單獨(dú)處理異常。架構(gòu)上分層做得很清楚Controller層只做參數(shù)接收和結(jié)果封裝不寫業(yè)務(wù)Service層寫核心邏輯比如賬單生成、合同狀態(tài)流轉(zhuǎn)Mapper層只負(fù)責(zé)SQL與實(shí)體映射。這樣做的好處是答辯時老師問“你的事務(wù)怎么控制的”你就能直接說賬單生成是多表操作在Service上加Transactional保證合同狀態(tài)和賬單數(shù)據(jù)不會出現(xiàn)一半成功一半失敗。2.3 數(shù)據(jù)庫設(shè)計(jì)思路與核心表結(jié)構(gòu)數(shù)據(jù)庫設(shè)計(jì)是整個項(xiàng)目的地基我把核心表結(jié)構(gòu)列出來并把設(shè)計(jì)原因講清楚。商鋪表shop字段類型說明idbigint主鍵自增shop_novarchar(20)鋪號唯一索引shop_namevarchar(100)商鋪名稱floorvarchar(10)所在樓層areadecimal(10,2)建筑面積平方米rent_unit_pricedecimal(10,2)月租金單價元/平米property_fee_unit_pricedecimal(10,2)物業(yè)費(fèi)單價元/平米/月statustinyint狀態(tài)0空閑 1已租 2裝修中 3停用remarkvarchar(255)備注租戶表tenantid、tenant_name、phone、id_card、emergency_contact、emergency_phone、create_time。合同表contract設(shè)計(jì)時要注意冗余字段id、contract_no、shop_id、tenant_id、start_date、end_date、monthly_rent、monthly_property_fee、deposit、rent_increase_rate、status1生效 2已到期 3已解約、sign_date、remark。收費(fèi)記錄表charge_recordid、contract_id、charge_type1租金 2物業(yè)費(fèi) 3水費(fèi) 4電費(fèi) 5滯納金、amount、period賬期如2025-03、status0未繳 1已繳 2逾期、pay_date、pay_method、operator_id。報(bào)修表repair_record、公告表notice、用戶表sys_user、角色表sys_role、用戶角色關(guān)聯(lián)表sys_user_role、操作日志表sys_log。設(shè)計(jì)上的幾個關(guān)鍵點(diǎn)要單獨(dú)講外鍵關(guān)系和查詢效率。業(yè)務(wù)表之間有關(guān)聯(lián)比如合同表關(guān)聯(lián)shop_id和tenant_id但我在數(shù)據(jù)庫層面不建物理外鍵而是在代碼里保證引用關(guān)系。物理外鍵在刪數(shù)據(jù)時會帶來一堆約束麻煩線上系統(tǒng)也普遍不推薦用關(guān)鍵是索引要建對。對shop_id、tenant_id、contract_id這些關(guān)聯(lián)字段都要建普通索引否則數(shù)據(jù)量上來后聯(lián)表查詢會變慢。金額字段一律用decimal而不是float/double。Java后端用BigDecimal接收數(shù)據(jù)庫用decimal(10,2)。浮點(diǎn)數(shù)算金額0.1加0.2不等于0.3的問題在答辯現(xiàn)場非常尷尬這一點(diǎn)一定要提前規(guī)避。時間字段用datetime所有表都帶上create_time和update_time。MyBatis-Plus的自動填充功能可以讓插入和更新時自動填這兩個字段省去手動維護(hù)。賬期period字段為什么不直接用日期因?yàn)橘~單是按自然月生成的2025-03這個字段在統(tǒng)計(jì)月度收入時直接group by period就可以不需要再轉(zhuǎn)換日期格式。這個設(shè)計(jì)是業(yè)務(wù)驅(qū)動出來的不是拍腦袋決定的。3. 核心模塊實(shí)現(xiàn)深度解析3.1 商鋪管理模塊狀態(tài)流轉(zhuǎn)是關(guān)鍵商鋪管理表面上是增刪改查核心是狀態(tài)流轉(zhuǎn)。一間商鋪可以從空閑變成已租、從已租變成裝修中、再重新變成空閑也可以被停用。我在后端定義一個枚舉ShopStatus在Service層做流轉(zhuǎn)校驗(yàn)只有“空閑”狀態(tài)的商鋪才能簽新合同變成“已租”“已租”狀態(tài)的商鋪不允許直接刪除只能走退租流程“停用”狀態(tài)的商鋪不參與出租率統(tǒng)計(jì)。這里有一個好用的技巧凡是需要和業(yè)務(wù)狀態(tài)打交道的修改操作都寫專門的Service方法而不是讓前端直接調(diào)通用update接口傳一個status字段。比如簽約操作是createContract()里面做了三件事校驗(yàn)商鋪狀態(tài)、插入合同記錄、更新商鋪狀態(tài)為已租。這樣狀態(tài)流轉(zhuǎn)的邏輯只在服務(wù)端前端只能通過業(yè)務(wù)接口觸發(fā)避免了有人繞過校驗(yàn)直接改狀態(tài)的情況。代碼里用Transactional包住整個方法任何一個環(huán)節(jié)出錯都回滾。核心方法大概長這樣Transactional(rollbackFor Exception.class) public void signContract(SignContractRequest req) { Shop shop shopMapper.selectById(req.getShopId()); if (shop null || !ShopStatus.FREE.equals(shop.getStatus())) { throw new BusinessException(該商鋪當(dāng)前不可出租); } Contract contract new Contract(); contract.setShopId(req.getShopId()); contract.setTenantId(req.getTenantId()); contract.setStartDate(req.getStartDate()); contract.setEndDate(req.getEndDate()); contract.setMonthlyRent(req.getMonthlyRent()); contract.setDeposit(req.getDeposit()); contract.setStatus(ContractStatus.ACTIVE); contractMapper.insert(contract); shop.setStatus(ShopStatus.RENTED); shopMapper.updateById(shop); generateFirstMonthBill(contract.getId()); }3.2 合同管理模塊日期與關(guān)聯(lián)狀態(tài)的處理合同管理是這個項(xiàng)目里業(yè)務(wù)邏輯最重的部分我踩過不少坑。先說簽約時租金怎么算。商鋪表里存的是“租金單價元/平米/月”但合同里存的是“月租金元”。簽約時要根據(jù)面積乘以單價算出來比如一間50平米的鋪?zhàn)訂蝺r80元/平月租金就是4000元。為什么合同里要冗余一個月的租金總價而不是每次現(xiàn)算因?yàn)閷砜赡軉为?dú)對某個合同調(diào)價比如商圈整體漲價后給老租戶打折如果只存單價歷史合同的金額會跟著變化財(cái)務(wù)核算就亂了。所以合同一旦生成月租金就以合同為準(zhǔn)商鋪的單價只作為新建合同的默認(rèn)值互不影響。日期處理也是重點(diǎn)。合同的start_date和end_date用Java的LocalDate來接收向前端返回時格式化成yyyy-MM-dd。計(jì)算租期內(nèi)到底要生成幾個月的賬單有一種常見做法是用ChronoUnit.MONTHS.between(start, end)再加1比如3月1日到5月31日是3個月。這里有個坑ChronoUnit計(jì)算的是兩個日期之間的完整月數(shù)如果合同是3月15日到5月14日算出來是2個月但實(shí)際按自然月算賬單可能是跨了3、4、5三個月的部分月份。所以我在生成賬單時不用這個API而是按月從合同開始月份循環(huán)到結(jié)束月份每個月生成一條賬期記錄金額按整月收。雖然部分月份實(shí)際不是整月但社區(qū)商鋪的結(jié)算習(xí)慣通常都是按月整收這種約定俗成的規(guī)則在計(jì)費(fèi)邏輯里要先和需求方確認(rèn)清楚。合同到期提醒功能也建議做??梢悦刻炫芤粋€定時任務(wù)掃一遍狀態(tài)為生效、結(jié)束日期在30天內(nèi)的合同把記錄寫入提醒列表讓管理員在首頁看到。這個功能不復(fù)雜但對“系統(tǒng)有沒有用”的感知提升很明顯。3.3 收費(fèi)管理模塊賬單生成與核銷收費(fèi)模塊是核心中的核心。我設(shè)計(jì)了一個計(jì)費(fèi)方法generateMonthlyBill(contractId, yearMonth)邏輯是查詢合同是否在指定月份處于生效狀態(tài)檢查該月份是否已經(jīng)生成過賬單有則跳過避免重復(fù)生成根據(jù)合同中的月租金和月物業(yè)費(fèi)生成兩條charge_record記錄一條charge_type等于1租金一條等于2物業(yè)費(fèi)如果有水電費(fèi)抄表記錄再生成對應(yīng)的水費(fèi)、電費(fèi)記錄。賬單生成時機(jī)有兩種做法一種是在合同生效時一次性生成整個租期的所有賬單另一種是每個月生成當(dāng)月的。我推薦后者因?yàn)楹贤赡苤型咀兏?、退租或者調(diào)價一次性生成一堆賬單后面要維護(hù)很麻煩。每個月由管理員點(diǎn)一下“生成本月賬單”或者配置一個定時任務(wù)每天跑一次檢查本月是否已生成就能覆蓋絕大多數(shù)場景。SQL里用唯一索引unique key uk_contract_period_type(contract_id, period, charge_type)來保證一筆費(fèi)用在一個賬期內(nèi)只能有一條記錄這是數(shù)據(jù)庫層面的兜底。收款核銷的流程是管理員看到某條賬單狀態(tài)未繳點(diǎn)擊“收款”彈出收款窗口記錄收款方式、實(shí)收金額允許部分減免并把減免原因?qū)懭雮渥?、收款時間然后更新賬單狀態(tài)為已繳。這里業(yè)務(wù)上有個處理要點(diǎn)拖欠繳費(fèi)時要不要自動產(chǎn)生滯納金。社區(qū)商鋪通常按合同約定滯納金比例比如每日千分之三但計(jì)算起來比較復(fù)雜我的做法是在退租結(jié)算時統(tǒng)一計(jì)算未繳賬單的滯納金日常不自動累加這樣既滿足業(yè)務(wù)預(yù)期又減少了定時計(jì)算的負(fù)擔(dān)。3.4 統(tǒng)計(jì)報(bào)表與可視化統(tǒng)計(jì)報(bào)表是答辯時的加分項(xiàng)我建議至少做三個視圖。出租率趨勢圖按月統(tǒng)計(jì)每個月的出租商鋪數(shù)除以總商鋪數(shù)前端用ECharts畫折線圖。SQL上可以從合同表里查出每個月份有效合同的數(shù)量也可以用商鋪表中status等于已租的數(shù)量除以總數(shù)。前一種能看歷史趨勢后一種只能看當(dāng)前快照答辯展示建議用前一種更有“數(shù)據(jù)感”。月度收入趨勢圖按實(shí)收月份統(tǒng)計(jì)charge_record里status等于已繳的賬單金額合計(jì)能看出收租高峰和低谷。這里注意統(tǒng)計(jì)口徑是“實(shí)收月份”而不是“賬期月份”因?yàn)榭赡艽嬖谘a(bǔ)繳以前月份款項(xiàng)的情況兩個口徑的差別在答辯時講清楚了老師會認(rèn)可你對統(tǒng)計(jì)口徑的敏感度。費(fèi)用類型占比餅圖統(tǒng)計(jì)一個時間段內(nèi)租金、物業(yè)費(fèi)、水電費(fèi)各自的總額和占比。這個圖最直觀非技術(shù)背景的人也能一眼看懂。做報(bào)表時SQL層建議直接寫分組統(tǒng)計(jì)不要先把明細(xì)查出來然后在Java里for循環(huán)累加。雖然對數(shù)據(jù)量小的畢設(shè)來說性能差別不大但寫一手“用SQL完成聚合”的代碼在答辯時更顯得有工程素養(yǎng)。例如當(dāng)月收入統(tǒng)計(jì)select DATE_FORMAT(pay_date, %Y-%m) as month, sum(amount) as total_income from charge_record where status 1 and pay_date is not null group by DATE_FORMAT(pay_date, %Y-%m) order by month desc;4. 實(shí)操過程與部署實(shí)施4.1 從零初始化項(xiàng)目的環(huán)境準(zhǔn)備在開始寫代碼前先把環(huán)境準(zhǔn)備好我列一個清單JDK 1.8或11我用1.8兼容性最好Maven 3.6以上MySQL 5.7或8.0我用5.7如果8.0要注意驅(qū)動和時區(qū)配置Node.js 14以上IDEA、Navicat或DBeaver數(shù)據(jù)庫初始化時建一個名為community_shop的庫字符集選utf8mb4。然后執(zhí)行建表腳本我習(xí)慣把所有建表語句放進(jìn)一個schema.sql里統(tǒng)一管理再插一批測試數(shù)據(jù)。測試數(shù)據(jù)很重要不只是為了演示效果更是為了讓自己在開發(fā)階段就面對真實(shí)數(shù)據(jù)形態(tài)比如有的商鋪已經(jīng)出租多年、有的上個月剛退租、有些賬單逾期未繳這樣頁面上的圖表才不會顯得空洞。后端項(xiàng)目用Spring Initializr生成引入spring-boot-starter-web、mybatis-plus-boot-starter、mysql-connector-java、lombok、spring-boot-starter-validation、jjwt這些依賴。application.yml里配置數(shù)據(jù)源、MyBatis-Plus的mapper掃描、JWT密鑰和過期時間。注意數(shù)據(jù)庫連接URL里一定要帶serverTimezoneAsia/Shanghai否則連上庫以后時間字段會差8個小時這是個特別經(jīng)典的坑。配置示例spring: datasource: url: jdbc:mysql://localhost:3306/community_shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl jwt: secret: your-secret-key expire-hours: 244.2 核心接口與前后端聯(lián)調(diào)后端接口統(tǒng)一以/api開頭通過JWT攔截器做認(rèn)證。登錄接口、查詢商鋪列表接口、生成賬單接口分別對應(yīng)不同的權(quán)限點(diǎn)。我把主要接口列出來商鋪相關(guān)GET /api/shop/page、POST /api/shop、PUT /api/shop、DELETE /api/shop/{id}、PUT /api/shop/{id}/status租戶相關(guān)GET /api/tenant/page、POST /api/tenant、PUT /api/tenant合同相關(guān)POST /api/contract/sign、GET /api/contract/page、PUT /api/contract/renew、PUT /api/contract/terminate收費(fèi)相關(guān)POST /api/charge/generateMonthly、GET /api/charge/page、POST /api/charge/pay/{id}統(tǒng)計(jì)相關(guān)GET /api/stats/rentRate、GET /api/stats/monthlyIncome、GET /api/stats/incomeType前后端聯(lián)調(diào)時的重點(diǎn)在Token傳遞。前端Axios的request攔截器里加Authorization頭response攔截器里對code等于401的統(tǒng)一跳轉(zhuǎn)登錄頁。一開始最容易出的問題是跨域開發(fā)環(huán)境我是在Vite的配置里加了proxy把/api前綴的請求代理到http://localhost:8080這樣瀏覽器的請求是同源的就不需要后端額外開放CORS。這個方法推薦優(yōu)先用比在Spring里配CrossOrigin更干凈也不會影響生產(chǎn)部署。另外“導(dǎo)出Excel”功能我強(qiáng)烈建議做進(jìn)去。社區(qū)商鋪管理方經(jīng)常要把賬單表發(fā)出去對賬一個導(dǎo)出按鈕能生成Excel文件在畢設(shè)里既是一個實(shí)用亮點(diǎn)又能在答辯時現(xiàn)場演示成本卻很低。做法是后端引入easypoi依賴把查出來的收費(fèi)記錄列表直接轉(zhuǎn)成Excel響應(yīng)給前端前端用window.open下載或者axios接收blob。這個功能屬于“投入小、產(chǎn)出高”的典型代表。4.3 本地運(yùn)行與打包上線的完整流程整個項(xiàng)目在本地跑起來只需要幾步啟動MySQL執(zhí)行初始化腳本導(dǎo)入表結(jié)構(gòu)和測試數(shù)據(jù)修改application.yml里的數(shù)據(jù)庫賬號密碼在IDEA里啟動Spring Boot后端看到Started日志說明成功默認(rèn)端口8080在前端目錄執(zhí)行npm install安裝依賴再npm run devVite默認(rèn)5173端口會打開登錄頁面用管理員賬號登錄進(jìn)入后逐個模塊點(diǎn)一遍確認(rèn)接口和數(shù)據(jù)都正常。如果要把項(xiàng)目打包部署到服務(wù)器后端執(zhí)行mvn package會生成一個可執(zhí)行的jar包把jar、MySQL、前端構(gòu)建產(chǎn)物放到同一臺服務(wù)器上前端執(zhí)行npm run build產(chǎn)物是一個dist目錄用Nginx做靜態(tài)托管并把/api路徑反向代理到Spring Boot的8080端口。Nginx配置里server_name、root、location /api的proxy_pass是關(guān)鍵。前端路由如果是history模式需要在Nginx里配置try_files否則刷新頁面就會404這個坑幾乎每個第一次部署前后端分離項(xiàng)目的人都會遇到一次。Nginx核心配置server { listen 80; server_name your-domain.com; root /opt/community-shop/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }5. 常見問題與排查技巧實(shí)錄5.1 高頻問題速查表我在開發(fā)和帶學(xué)生做類似項(xiàng)目時遇到最多的問題基本集中在下面這些整理成一張速查表現(xiàn)象可能原因解決方案登錄頁驗(yàn)證碼不顯示或接口404后端沒啟動或路徑不對檢查控制臺日志確認(rèn)端口和上下文路徑前端請求接口報(bào)CORS錯誤未配置代理或未開CORS用Vite proxy代理或后端配置跨域過濾器數(shù)據(jù)庫時間差8小時連接URL缺serverTimezone加上serverTimezoneAsia/Shanghai中文亂碼連接字符集不是utf8mb4連接URL加characterEncodingutf8庫表字符集統(tǒng)一npm install卡住依賴源太慢設(shè)置鏡像源加速賬單生成重復(fù)唯一索引沒建對contract_id、period、charge_type加唯一索引刷新頁面404前端history路由部署未配置Nginx加try_files規(guī)則導(dǎo)出Excel中文亂碼瀏覽器下載文件名編碼文件名URLEncode后放入Content-Disposition修改數(shù)據(jù)后列表不刷新前端緩存或查詢條件問題檢查分頁參數(shù)和響應(yīng)數(shù)據(jù)清緩存調(diào)試5.2 答辯時如何把項(xiàng)目講清楚畢業(yè)設(shè)計(jì)的最后一道坎是答辯。很多同學(xué)代碼寫完了但講的時候只能念PPT很吃虧。我給幾個建議一定要準(zhǔn)備一張“業(yè)務(wù)流程圖”或者“系統(tǒng)功能結(jié)構(gòu)圖”答辯時第一件事就是講清楚這個系統(tǒng)是給誰用的、解決什么問題然后順著“商鋪建檔→簽約→月度收費(fèi)→退租結(jié)算”這條主線講把每個環(huán)節(jié)對應(yīng)的頁面和接口指出來。時間控制在5分鐘內(nèi)重點(diǎn)是體現(xiàn)出你理解業(yè)務(wù)而不是只會敲代碼。準(zhǔn)備幾個“為什么”答案。老師必問的問題包括為什么選MySQL不選Oracle為什么金額用decimal為什么不用物理外鍵合同到期提醒是怎么做的權(quán)限控制怎么實(shí)現(xiàn)的這些問題我在前面的內(nèi)容里都已經(jīng)把答案給了關(guān)鍵是要自己理解、用自己的話表達(dá)出來。還有一個小技巧答辯演示時準(zhǔn)備好一組完整的演示數(shù)據(jù)包括一間已經(jīng)出租一年多的商鋪、一間空鋪、幾條不同狀態(tài)的電費(fèi)賬單以及兩個不同權(quán)限的賬號。演示時先講普通操作再登錄管理員賬號演示權(quán)限差異最后打開統(tǒng)計(jì)圖表頁面連續(xù)操作下來整套系統(tǒng)給人的印象就是完整、有深度的而不是只做了個登錄加兩個列表的“半成品”。5.3 實(shí)際開發(fā)過程中我踩過的三個坑第一個坑是分頁插件導(dǎo)致的SQL異常。MyBatis-Plus的分頁查詢需要單獨(dú)配置一個分頁攔截器如果沒配置Page對象返回的total始終為0你以為是自己代碼寫錯了其實(shí)只是少裝了一個插件。第二個坑是Element UI的日期選擇器返回的格式問題如果你把DatePicker的value-format不設(shè)成yyyy-MM-dd前端拿到的就是標(biāo)準(zhǔn)時間對象直接傳給后端會導(dǎo)致格式解析報(bào)錯。第三個坑是權(quán)限框架的引入順序一旦引入攔截器后發(fā)現(xiàn)靜態(tài)資源和登錄接口都被攔截排查的思路是先看攔截器排除列表是否包含了登錄接口和靜態(tài)路徑這個順序問題我在做項(xiàng)目時調(diào)試了整整一個下午。排查思路分享遇到問題不要慌先看后端日志再看瀏覽器Network面板。后端日志定位是參數(shù)問題、SQL問題還是業(yè)務(wù)異常Network面板確認(rèn)請求有沒有發(fā)出去、響應(yīng)碼是多少、返回體是什么。90%的聯(lián)調(diào)問題靠這兩個工具就能解決。6. 經(jīng)驗(yàn)總結(jié)與擴(kuò)展建議整套系統(tǒng)做下來我個人最大的體會是畢設(shè)項(xiàng)目的難不在“代碼量”而在“業(yè)務(wù)想清楚”。社區(qū)商鋪管理系統(tǒng)的每個模塊單看都不難但把它們連成一條完整的業(yè)務(wù)鏈讓數(shù)據(jù)在商鋪、租戶、合同、賬單之間保持一致這個過程才是真正的鍛煉。如果這個項(xiàng)目你想繼續(xù)擴(kuò)展方向其實(shí)很多。可以加一個租戶端小程序讓租戶在手機(jī)上查看賬單并在線繳費(fèi)、提交報(bào)修申請可以在收費(fèi)模塊里加入對接微信支付或支付寶的沙箱環(huán)境做成在線支付閉環(huán)也可以加入更細(xì)的權(quán)限粒度比如每個管理員只能管理自己負(fù)責(zé)的樓棟。想走數(shù)據(jù)分析方向的話還可以把出租率、回款率、空置率做成大屏看板展示效果會更有沖擊力。最后分享一個做畢設(shè)時很實(shí)用的小習(xí)慣每寫完一個模塊就寫一小段測試代碼或者用Postman把接口驗(yàn)證一遍并把驗(yàn)證記錄貼在項(xiàng)目的README里。這樣到寫論文的時候“系統(tǒng)測試”這一章的內(nèi)容自然就有了不用最后幾天熬夜補(bǔ)材料。希望這篇文章能幫你少走一些彎路。