居民醫(yī)保管理系統(tǒng)源碼解析:SpringBoot+Vue+MySQL實(shí)戰(zhàn))
作為一個(gè)常年泡在項(xiàng)目堆里的技術(shù)博主我最近在好幾個(gè)技術(shù)群里看到不少人貼出同一個(gè)項(xiàng)目的名字城鄉(xiāng)居民基本醫(yī)療信息管理系統(tǒng)源碼SpringBoot后端加Vue前端配MySQL還特別標(biāo)注了“可直接運(yùn)行”。很多同學(xué)私信問(wèn)我這東西到底怎么樣、值不值得下載來(lái)研究甚至有人上來(lái)就問(wèn)“能不能幫我跑通”。說(shuō)真的我看了那么多號(hào)稱畢業(yè)設(shè)計(jì)、課程設(shè)計(jì)的開(kāi)源項(xiàng)目這一個(gè)方向偏冷門但業(yè)務(wù)邏輯一點(diǎn)都不簡(jiǎn)單。借著這個(gè)機(jī)會(huì)我把這套城鄉(xiāng)居民基本醫(yī)療信息管理系統(tǒng)完整拆一遍講清楚代碼背后到底藏著什么設(shè)計(jì)思路怎么在本地把它跑起來(lái)以及真正容易踩坑的地方在哪兒。這個(gè)系統(tǒng)本質(zhì)上是一個(gè)典型的行政區(qū)劃級(jí)基層政務(wù)業(yè)務(wù)系統(tǒng)面向的是城鄉(xiāng)居民基本醫(yī)療的參保登記、繳費(fèi)核定、報(bào)銷結(jié)算、待遇查詢等場(chǎng)景。跟常見(jiàn)的電商后臺(tái)、博客系統(tǒng)不一樣它的數(shù)據(jù)模型里充滿了居民身份屬性、醫(yī)保批次參數(shù)、報(bào)銷比例規(guī)則這類業(yè)務(wù)字段對(duì)表結(jié)構(gòu)設(shè)計(jì)、角色權(quán)限劃分、前后端數(shù)據(jù)流轉(zhuǎn)的要求都比較高。也正因?yàn)樗鼧I(yè)務(wù)不復(fù)雜、又沒(méi)有特別畸形的技術(shù)坑非常適合用來(lái)理解SpringBoot加Vue前后端分離項(xiàng)目的完整閉環(huán)從頁(yè)面錄入、接口請(qǐng)求、服務(wù)端校驗(yàn)、數(shù)據(jù)庫(kù)讀寫一直到統(tǒng)計(jì)匯總。適合的人群也很明確準(zhǔn)備做畢設(shè)的在校生、剛學(xué)完框架想找個(gè)中小型實(shí)戰(zhàn)項(xiàng)目練手的新人以及想快速搭一個(gè)行政管理域業(yè)務(wù)原型給領(lǐng)導(dǎo)或客戶演示的開(kāi)發(fā)者。這個(gè)項(xiàng)目跑起來(lái)只需要一臺(tái)普通開(kāi)發(fā)電腦不需要高配置。但跑通只是第一步把里面的業(yè)務(wù)邏輯吃透才是真收獲。1. 一個(gè)看著不起眼的項(xiàng)目為什么值得好好拆一遍很多人第一眼看到“城鄉(xiāng)居民基本醫(yī)療信息管理系統(tǒng)”這種命名第一反應(yīng)是“這是不是那種很老套的管理系統(tǒng)是不是就用表格堆一堆增刪改查”。我只能說(shuō)這個(gè)刻板印象害人不淺。表面上它確實(shí)是一個(gè)后臺(tái)管理項(xiàng)目但只要你打開(kāi)數(shù)據(jù)庫(kù)表結(jié)構(gòu)看到參保人、家庭成員、繳費(fèi)批次、報(bào)銷申請(qǐng)、補(bǔ)償比例、醫(yī)療機(jī)構(gòu)這幾個(gè)核心實(shí)體之間的關(guān)系你就會(huì)明白這類系統(tǒng)的信息量遠(yuǎn)大于普通的商品管理。1.1 城鄉(xiāng)居民基本醫(yī)療信息管理系統(tǒng)到底在管什么城鄉(xiāng)居民基本醫(yī)療主要面向的是沒(méi)有參加職工基本醫(yī)療的人群比如農(nóng)村居民、城鄉(xiāng)失業(yè)人員、老人和未成年人。這類人群的特點(diǎn)是數(shù)量大、分布散、動(dòng)態(tài)變化頻繁而且參保狀態(tài)跟戶籍屬性、人員類別、繳費(fèi)檔次是強(qiáng)綁定的。具體到信息管理系統(tǒng)里面你需要處理的數(shù)據(jù)大體就是幾類參保登記數(shù)據(jù)個(gè)人基本信息、身份證號(hào)、戶籍地址、參保狀態(tài)、人員類別普通居民、低保對(duì)象、特困人員等、繳費(fèi)檔次。繳費(fèi)核定數(shù)據(jù)年度繳費(fèi)批次、每人繳費(fèi)金額、財(cái)政補(bǔ)助金額、繳費(fèi)時(shí)間、是否到賬。報(bào)銷結(jié)算數(shù)據(jù)門診記錄、住院記錄、發(fā)票金額、目錄內(nèi)費(fèi)用、報(bào)銷比例、實(shí)際報(bào)銷金額、個(gè)人自付金額?;A(chǔ)配置數(shù)據(jù)各級(jí)醫(yī)療機(jī)構(gòu)的報(bào)銷比例參數(shù)、藥品目錄/診療項(xiàng)目目錄、年度封頂線、起付線。對(duì)比一下常見(jiàn)的電商項(xiàng)目商品、訂單、支付這種模型在業(yè)務(wù)上相對(duì)通用但醫(yī)保繳費(fèi)和報(bào)銷結(jié)算的數(shù)據(jù)規(guī)則往往帶有很強(qiáng)的地區(qū)性參數(shù)而且審批、校驗(yàn)、記錄留痕的環(huán)節(jié)更多。比如報(bào)銷申請(qǐng)就要經(jīng)過(guò)提交材料、審核錄入、核定金額、財(cái)務(wù)發(fā)放等多個(gè)狀態(tài)節(jié)點(diǎn)。因此這個(gè)系統(tǒng)雖然叫“信息管理”本質(zhì)上是一個(gè)帶流程性質(zhì)的業(yè)務(wù)系統(tǒng)做起來(lái)比普通CRUD要復(fù)雜一層。1.2 這套技術(shù)組合的背后邏輯為什么用SpringBoot加Vue加MySQL而不是其他組合這個(gè)問(wèn)題不只是選型偏好背后有一套很實(shí)際的理由。先從后端說(shuō)。SpringBoot能夠成為這類中小型管理系統(tǒng)最常被選擇的基礎(chǔ)框架核心在于幾個(gè)能力Spring IoC容器管理對(duì)象Starter機(jī)制把常用功能做成開(kāi)箱即用的依賴內(nèi)嵌Tomcat讓部署變成一條java -jar命令。相較早期用SSH或者JSP做的老系統(tǒng)SpringBoot項(xiàng)目里不需要你手工寫一堆XML配置也不需要你把頁(yè)面模板和后端邏輯攪在一起。再配合MyBatis或JPA操作MySQL整個(gè)后端可以拆成清晰的層級(jí)控制層接收請(qǐng)求、服務(wù)層處理業(yè)務(wù)、數(shù)據(jù)訪問(wèn)層操作數(shù)據(jù)庫(kù)。再?gòu)腣ue的角度看。Vue在整個(gè)前端技術(shù)圈里一直以上手平滑、中文資料多著稱它的雙向綁定和組件化非常適合這類頁(yè)面多、表單多的管理系統(tǒng)。管理端要做幾十個(gè)頁(yè)面如果全部采用傳統(tǒng)模板渲染光JS文件的維護(hù)就夠喝一壺。Vue把頁(yè)面拆成組件左邊菜單欄、頂部導(dǎo)航、權(quán)限路由、表格分頁(yè)、表單彈窗這些都是可以復(fù)用的組件開(kāi)發(fā)效率和可讀性都能往上走一大截。最后是MySQL。有人說(shuō)MySQL是不是太基礎(chǔ)了要不要換PostgreSQL。我的看法是這類系統(tǒng)的數(shù)據(jù)量通??刂圃趲资f(wàn)條以內(nèi)MySQL無(wú)論是在安裝維護(hù)、備份遷移、還是新手上手成本上都會(huì)比PostgreSQL更順一些。它自帶的數(shù)據(jù)類型、索引機(jī)制以及事務(wù)保證已經(jīng)足以應(yīng)對(duì)參保記錄、繳費(fèi)記錄、報(bào)銷記錄的存儲(chǔ)和查詢場(chǎng)景。在真實(shí)的政務(wù)系統(tǒng)選型里你確實(shí)能看到Oracle或者國(guó)產(chǎn)數(shù)據(jù)庫(kù)但那是出于采購(gòu)規(guī)范和歷史原因?qū)σ惶组_(kāi)源學(xué)習(xí)項(xiàng)目來(lái)說(shuō)MySQL是性價(jià)比最高的存在。用一個(gè)不那么精確但很好懂的類比SpringBoot是店里的后廚負(fù)責(zé)接單、洗菜、炒菜Vue是前廳的點(diǎn)餐屏幕和餐桌顧客直接看到菜單、下單、等菜MySQL是后廚的儲(chǔ)物倉(cāng)庫(kù)所有食材和半成品都按規(guī)則碼放在不同的貨架上。前后端分離就是前廳只管展示和交互后廚只管把菜品做好兩個(gè)環(huán)節(jié)之間通過(guò)一道道菜單傳遞信息。2. 技術(shù)選型與系統(tǒng)架構(gòu)逐個(gè)盤搞清楚系統(tǒng)要解決什么問(wèn)題后再看源碼的時(shí)候就要把注意力放在結(jié)構(gòu)上。很多同學(xué)拿到項(xiàng)目壓縮包直接就是一頓點(diǎn)擊啟動(dòng)遇到報(bào)錯(cuò)就發(fā)在群里問(wèn)。我非常不建議這種跑法。你要先在一個(gè)整體的高度上知道每個(gè)目錄、每個(gè)配置文件是干什么用的這樣即便報(bào)錯(cuò)了也能自己判斷問(wèn)題出在哪個(gè)環(huán)節(jié)。2.1 后端SpringBoot的分層與核心模塊這套系統(tǒng)的后端部分典型的目錄結(jié)構(gòu)大概長(zhǎng)這樣src/main/java ├── com.xxx.medical │ ├── controller // 控制層接收HTTP請(qǐng)求 │ ├── service // 業(yè)務(wù)層處理核心邏輯 │ ├── mapper // 數(shù)據(jù)訪問(wèn)層對(duì)接數(shù)據(jù)庫(kù) │ ├── entity // 實(shí)體類對(duì)應(yīng)數(shù)據(jù)表 │ ├── config // 攔截器、跨域配置、數(shù)據(jù)源配置 │ ├── common // 統(tǒng)一返回結(jié)構(gòu)、異常處理、工具類 │ └── MedicalApplication.java // 啟動(dòng)類controller這一層做得非常薄只負(fù)責(zé)接到前端傳過(guò)來(lái)的參數(shù)、調(diào)用service、把結(jié)果包裝成統(tǒng)一結(jié)構(gòu)返回。真正的業(yè)務(wù)判斷都在service層比如報(bào)銷金額怎么算、參保狀態(tài)能不能變更、重復(fù)繳費(fèi)怎么攔截這些邏輯都不應(yīng)該塞在controller里面。數(shù)據(jù)層則通過(guò)MyBatis的接口跟實(shí)體對(duì)應(yīng)如果你拿到的是普通Maven項(xiàng)目你會(huì)看到resources目錄下還有一堆mapper XML文件里面寫的是SQL這是MyBatis風(fēng)格最常見(jiàn)的組織方式。再來(lái)看后端提供哪些模塊。核心模塊大致可以分為用戶登錄與權(quán)限、參保人信息管理、家庭關(guān)系管理、繳費(fèi)批次管理、繳費(fèi)記錄管理、報(bào)銷申請(qǐng)與審核、參保統(tǒng)計(jì)報(bào)表。其中每個(gè)模塊都可以獨(dú)立出控制器和服務(wù)這樣代碼可讀性高后期如果要做微服務(wù)拆分也能照著模塊邊界去切。關(guān)于統(tǒng)一返回結(jié)構(gòu)我多說(shuō)一句。很多自己寫的demo項(xiàng)目返回給前端的東西非常隨意有時(shí)直接返回一個(gè)實(shí)體對(duì)象有時(shí)返回一個(gè)Map有時(shí)又直接返回字符串這樣的話聯(lián)調(diào)階段會(huì)被反復(fù)扯皮。這類系統(tǒng)一般會(huì)封裝一個(gè)Result對(duì)象里面放著code、message、data三個(gè)字段。前端拿到響應(yīng)之后先判斷code是否等于200再做后續(xù)操作。有一個(gè)統(tǒng)一的返回約定前后端協(xié)作的溝通成本會(huì)低很多。2.2 前端Vue的雙角色界面前端部分基于Vue開(kāi)發(fā)的話常見(jiàn)的項(xiàng)目結(jié)構(gòu)是使用Vue CLI或Vite創(chuàng)建的單頁(yè)應(yīng)用。管理系統(tǒng)的頁(yè)面通常分成兩類使用場(chǎng)景一類是系統(tǒng)管理員/經(jīng)辦人員使用的管理后臺(tái)另一類是參保居民自己使用的查詢與申請(qǐng)界面。這兩類角色的菜單、頁(yè)面、功能都有很大的區(qū)別。管理后臺(tái)端一般包括登錄頁(yè)、首頁(yè)工作臺(tái)、參保人管理、保障家庭管理、繳費(fèi)管理、報(bào)銷審核管理、系統(tǒng)用戶管理和系統(tǒng)配置。頁(yè)面以表格、搜索表單、狀態(tài)篩選為主并配合新增、編輯、刪除、導(dǎo)出的操作彈窗。這個(gè)角色的核心訴求是“錄入、批量處理、審核、留痕”。居民用戶端相對(duì)簡(jiǎn)單一般包括個(gè)人參保信息查看、家庭成員綁定、繳費(fèi)記錄查看、報(bào)銷進(jìn)度查詢、報(bào)銷申請(qǐng)發(fā)起。這個(gè)角色的核心訴求是“能不能少跑腿、能不能查清楚”。這樣一套系統(tǒng)在設(shè)計(jì)的時(shí)候會(huì)把前端路由做權(quán)限控制管理員登錄后能看到管理菜單普通居民登錄后只能看到個(gè)人相關(guān)頁(yè)面。有一個(gè)很容易被忽略的細(xì)節(jié)前端頁(yè)面的信息展示和后端接口字段一定要對(duì)齊。如果后端返回的日期字段是時(shí)間戳前端就要做格式化如果后端返回的金額單位是“分”前端展示就要除以100。這類項(xiàng)目跑通之后你最常遇到的就是“接口沒(méi)報(bào)錯(cuò)、但頁(yè)面顯示不對(duì)”的情況基本都是字段名不一致或者單位沒(méi)轉(zhuǎn)換。2.3 數(shù)據(jù)庫(kù)表結(jié)構(gòu)設(shè)計(jì)的關(guān)鍵數(shù)據(jù)庫(kù)設(shè)計(jì)是這類系統(tǒng)能不能做好的一半。打開(kāi)SQL腳本或者數(shù)據(jù)庫(kù)后你需要重點(diǎn)關(guān)注幾張核心表和表之間的關(guān)系。第一張是系統(tǒng)用戶表用來(lái)存登錄賬號(hào)和密碼注意不要把參保人和登錄用戶混成一張表。系統(tǒng)用戶表管的是“能進(jìn)系統(tǒng)的人”而參保人信息表存的是“醫(yī)療保障的對(duì)象”一個(gè)人可能是管理員同時(shí)也可能是參保家屬但這是兩個(gè)維度必須拆開(kāi)。第二張是參保人表核心字段包括姓名、身份證號(hào)、性別、出生日期、戶籍地址、參保狀態(tài)、人員類別、聯(lián)系方式等。身份證號(hào)是一個(gè)高權(quán)重字段由于居民基本醫(yī)療按人管理同一人重復(fù)參?;蛘呖绲貐^(qū)重復(fù)參保都是要避免的事情所以這張表上通常會(huì)對(duì)身份證號(hào)建唯一索引。第三張是繳費(fèi)記錄表關(guān)聯(lián)字段包括參保人ID、繳費(fèi)年度、繳費(fèi)檔次、個(gè)人繳費(fèi)金額、財(cái)政補(bǔ)助金額、繳費(fèi)狀態(tài)、繳費(fèi)時(shí)間。養(yǎng)老保險(xiǎn)和醫(yī)療保險(xiǎn)的繳費(fèi)體系在細(xì)節(jié)上各有不同但在管理系統(tǒng)層面都要能實(shí)現(xiàn)“按年度繳費(fèi)、按人核銷”的邏輯。第四張是報(bào)銷申請(qǐng)表這個(gè)表是整個(gè)系統(tǒng)業(yè)務(wù)鏈條最長(zhǎng)的表。字段包括參保人ID、醫(yī)院名稱、就診開(kāi)始時(shí)間和結(jié)束時(shí)間、總費(fèi)用、目錄內(nèi)費(fèi)用、起付線、報(bào)銷比例、報(bào)銷金額、個(gè)人自付金額、審核狀態(tài)、審核人等。報(bào)銷涉及的字段非常多經(jīng)常有人把本地自付和目錄內(nèi)費(fèi)用搞混這個(gè)在數(shù)據(jù)庫(kù)層面就要保證計(jì)算邏輯正確。另外還有字典表比如醫(yī)院等級(jí)字典、費(fèi)用類別字典、人員類別字典。字典表的存在意義在于避免在前端頁(yè)面上寫死各種下拉選項(xiàng)。你以后要改報(bào)銷比例分類或人員類別名稱不需要?jiǎng)哟a改數(shù)據(jù)庫(kù)里一條記錄就行了。2.4 前后端聯(lián)調(diào)的核心約定前后端分離項(xiàng)目的開(kāi)發(fā)模式里接口約定決定了整個(gè)團(tuán)隊(duì)的工作效率。這套源碼里后端接口一般會(huì)統(tǒng)一以/api開(kāi)頭比如/api/insured/list、/api/payment/save、/api/reimburse/audit。前端在發(fā)起請(qǐng)求的時(shí)候會(huì)統(tǒng)一指定baseURL開(kāi)發(fā)環(huán)境通過(guò)代理轉(zhuǎn)發(fā)生產(chǎn)環(huán)境通過(guò)Nginx反向代理。這里特別想提醒一句很多新手在開(kāi)發(fā)環(huán)境里直接把前端的請(qǐng)求地址寫成http://localhost:8080這樣確實(shí)能通但如果后端接口部署的地址以后變了前端所有請(qǐng)求地址都要改非常麻煩。正確做法是前端用相對(duì)路徑/api開(kāi)頭在vue.config.js或者Vite配置里加一個(gè)開(kāi)發(fā)代理把/api轉(zhuǎn)發(fā)到后端的localhost:8080。這樣你以后把前端打包扔到任何一臺(tái)服務(wù)器上只需要配置Nginx轉(zhuǎn)發(fā)規(guī)則不用改前端代碼。登錄態(tài)的保持也是前后端協(xié)作的核心之一。常見(jiàn)方案是基于SpringBoot的攔截器加Token校驗(yàn)前端登錄成功后把Token存到本地或者請(qǐng)求頭里。需要注意Token過(guò)期后前端要攔截到401狀態(tài)碼自動(dòng)跳回登錄頁(yè)不要讓用戶一直停留在看起來(lái)還在線的界面上。3. 本地跑通這套源碼的完整步驟很多文章到了架構(gòu)講解就停了但我覺(jué)得真正費(fèi)時(shí)間的是“怎么把它跑起來(lái)”。這里我按照我自己的實(shí)踐把從零到一跑通這套城鄉(xiāng)居民基本醫(yī)療信息管理系統(tǒng)的過(guò)程拆成具體步驟每個(gè)步驟里都有版本要求和注意事項(xiàng)。3.1 環(huán)境準(zhǔn)備版本對(duì)齊是第一個(gè)坑先確認(rèn)你本機(jī)已經(jīng)安裝好了下面這些環(huán)境版本不一定完全一致但至少不能差得太離譜。環(huán)境建議版本說(shuō)明JDK1.8或11SpringBoot 2.x用1.8沒(méi)問(wèn)題3.x需要17以上先看pom依賴再定Maven3.6用于拉取后端依賴或用IDE內(nèi)置MavenNode.js14.x~18.x前端依賴安裝和構(gòu)建需要版本太高可能出現(xiàn)兼容問(wèn)題npm/yarnnpm 6 / yarn 1.x安裝Vue項(xiàng)目依賴MySQL5.7或8.0數(shù)據(jù)庫(kù)建議8.0字符集要選utf8mb4這些版本號(hào)里面最容易出問(wèn)題的就是JDK和Node.js。有的電腦上裝著JDK 17但后端項(xiàng)目pom文件里編譯版本還寫的1.8一啟動(dòng)就會(huì)報(bào)錯(cuò)說(shuō)“無(wú)效的目標(biāo)發(fā)行版”。還有的電腦上Node版本到了20執(zhí)行npm install的時(shí)候會(huì)報(bào)一些內(nèi)部依賴錯(cuò)誤這種情況要么換Node 16左右的LTS版本要么把npm源切到國(guó)內(nèi)鏡像再試。3.2 后端啟動(dòng)從建庫(kù)到登錄第一步是建庫(kù)。打開(kāi)MySQL命令行或者圖形化客戶端執(zhí)行項(xiàng)目里的SQL腳本。這個(gè)SQL腳本可能是單個(gè).sql文件也可能是一堆SQL文件按模塊拆開(kāi)。執(zhí)行之前先檢查腳本開(kāi)頭有沒(méi)有建庫(kù)語(yǔ)句比如CREATE DATABASE medical;有的話你就只需要在客戶端里選中作為默認(rèn)數(shù)據(jù)庫(kù)執(zhí)行沒(méi)有的話你先手工建一個(gè)空庫(kù)然后切換過(guò)來(lái)執(zhí)行。第二步是改配置。找到后端項(xiàng)目里的application.yml或application.properties文件把數(shù)據(jù)庫(kù)地址、賬號(hào)、密碼改成你自己的。這里多說(shuō)一句如果你MySQL的密碼含有特殊字符比如或者#在YAML文件里不加引號(hào)會(huì)導(dǎo)致解析失敗。建議密碼里盡量不要用特殊字符或者在YAML里給密碼加單引號(hào)。還要注意URL里的時(shí)區(qū)參數(shù)常見(jiàn)寫法是spring: datasource: url: jdbc:mysql://localhost:3306/medical?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456有人不寫serverTimezone也能跑但有可能會(huì)在時(shí)間字段讀寫的時(shí)候產(chǎn)生8小時(shí)的偏差。加上Asia/Shanghai就是告訴驅(qū)動(dòng)用東八區(qū)時(shí)間算是最省心的做法。第三步是啟動(dòng)后端。用IDEA打開(kāi)項(xiàng)目之后等Maven把依賴?yán)曛苯舆\(yùn)行啟動(dòng)類。如果你的項(xiàng)目不是IDEA打開(kāi)而是命令行運(yùn)行可以在項(xiàng)目根目錄執(zhí)行mvn spring-boot:run啟動(dòng)成功之后控制臺(tái)會(huì)出現(xiàn)Spring Boot的啟動(dòng)橫幅和Tomcat端口號(hào)默認(rèn)是8080。為了確認(rèn)接口真的可用可以在瀏覽器里訪問(wèn)http://localhost:8080/api/user/info這類接口如果返回的是JSON而不是404說(shuō)明后端業(yè)務(wù)接口已經(jīng)在線了。3.3 前端啟動(dòng)代理與端口前端部分用IDEA或VS Code打開(kāi)以后先看目錄里有沒(méi)有package.json。在項(xiàng)目根目錄執(zhí)行安裝依賴npm install如果網(wǎng)絡(luò)慢建議臨時(shí)切換npm源在國(guó)內(nèi)環(huán)境下速度提升會(huì)非常明顯npm config set registry https://registry.npmmirror.com依賴安裝完以后執(zhí)行npm run serve如果是Vite項(xiàng)目執(zhí)行npm run dev啟動(dòng)成功以后控制臺(tái)會(huì)顯示一個(gè)本地訪問(wèn)地址比如http://localhost:8081。注意前端端口和后端端口不要沖突如果前端用了8080后端也用了8080那就是端口起了沖突啟動(dòng)其中一個(gè)就會(huì)報(bào)Address already in use。前后端配置里通常會(huì)有一個(gè)用8080另一個(gè)用8081或者8082不要強(qiáng)行都改成一個(gè)地址。打開(kāi)前端地址能看到登錄頁(yè)就說(shuō)明前端起來(lái)了。輸入系統(tǒng)初始化賬號(hào)密碼如果后端也正常就能跳轉(zhuǎn)到首頁(yè)。如果登錄按鈕一直轉(zhuǎn)圈或者提示網(wǎng)絡(luò)錯(cuò)誤先按F12打開(kāi)瀏覽器的開(kāi)發(fā)者工具看Network面板檢查請(qǐng)求是不是真的發(fā)出去、狀態(tài)碼是不是200、響應(yīng)體里有沒(méi)有報(bào)錯(cuò)。這一步是整個(gè)聯(lián)調(diào)過(guò)程中排查問(wèn)題最高頻的手段。3.4 目錄結(jié)構(gòu)速覽與啟動(dòng)順序總結(jié)拿到源碼后不要上來(lái)就跑先把目錄結(jié)構(gòu)和依賴配置看一遍。我這里給出一個(gè)常見(jiàn)的目錄速覽medical-manage ├── backend // SpringBoot后端 │ ├── src │ ├── pom.xml │ └── sql │ └── medical.sql └── frontend // Vue前端 ├── src ├── package.json └── vue.config.js啟動(dòng)順序一定是先MySQL再后端最后前端。MySQL沒(méi)起來(lái)就啟動(dòng)后端后端會(huì)報(bào)數(shù)據(jù)庫(kù)連接失敗后端沒(méi)起來(lái)就啟動(dòng)前端前端頁(yè)面能打開(kāi)但所有接口都會(huì)失敗。整個(gè)過(guò)程做一遍之后你就能在本地跑通一套完整體驗(yàn)的前后端分離項(xiàng)目。提示首次啟動(dòng)不要糾結(jié)“為什么后端啟動(dòng)這么慢”SpringBoot要掃描的組件比較多加上Maven還會(huì)做依賴加載這很正常。真正需要警覺(jué)的是日志里出現(xiàn)紅字異常之后再判斷問(wèn)題類型。4. 核心業(yè)務(wù)實(shí)現(xiàn)里的關(guān)鍵細(xì)節(jié)項(xiàng)目能跑通之后真正有價(jià)值的工作才開(kāi)始。這里我挑幾個(gè)我認(rèn)為在這個(gè)項(xiàng)目中值得深挖的細(xì)節(jié)也是面試或者答辯時(shí)容易被問(wèn)到的點(diǎn)。4.1 登錄與權(quán)限密碼加密和攔截要一起看登錄模塊是每個(gè)系統(tǒng)都有的但很多項(xiàng)目在密碼安全上做得不合格。這套系統(tǒng)的后端通常不會(huì)用明文存儲(chǔ)密碼而是用MD5加鹽或者BCrypt格式存儲(chǔ)。最簡(jiǎn)單的觀察方式是打開(kāi)數(shù)據(jù)庫(kù)用戶表看一下密碼字段的值是不是一串固定長(zhǎng)度且不規(guī)則的字符串。如果是說(shuō)明做了加密如果直接是123456這種說(shuō)明沒(méi)做加密這是安全上的一個(gè)明顯缺陷。權(quán)限控制上后端一般用攔截器統(tǒng)一處理。SpringBoot中的攔截器要注冊(cè)到WebMvcConfigurer里去常見(jiàn)做法是寫一個(gè)LoginInterceptor在里面判斷請(qǐng)求頭里的Token是否存在以及是否有效。對(duì)于免登錄接口比如前端獲取驗(yàn)證碼、用戶登錄要在配置文件里排除掉對(duì)應(yīng)的路徑。這里有一個(gè)很容易踩的坑攔截器路徑寫得太寬把靜態(tài)資源也攔截了導(dǎo)致前端頁(yè)面能打開(kāi)但圖片加載不出來(lái)寫得太窄又會(huì)導(dǎo)致有些需要鑒權(quán)的接口被任意訪問(wèn)。如果你要改進(jìn)這套項(xiàng)目把Token從固定字符串換成JWT是一個(gè)很好的升級(jí)點(diǎn)。JWT的好處是服務(wù)端不需要保存會(huì)話狀態(tài)Token本身就帶著用戶的ID和過(guò)期時(shí)間但缺點(diǎn)是如果用戶修改密碼之前簽發(fā)的Token無(wú)法立刻失效。對(duì)這個(gè)項(xiàng)目來(lái)說(shuō)也可以選擇把Token存到Redis里方便主動(dòng)踢人或統(tǒng)一失效但需要額外引入Redis依賴從零開(kāi)始改造會(huì)有一點(diǎn)工作量。4.2 報(bào)銷結(jié)算的計(jì)算邏輯比例參數(shù)不能寫死在代碼里城鄉(xiāng)居民基本醫(yī)療的報(bào)銷計(jì)算是整套系統(tǒng)里最有業(yè)務(wù)含金量的一段。報(bào)銷金額并不是簡(jiǎn)單的“總費(fèi)用乘以統(tǒng)一比例”。真實(shí)的核算過(guò)程要考慮起付線、封頂線、目錄內(nèi)費(fèi)用、報(bào)銷比例等多個(gè)因素。用公式表示大概是報(bào)銷額 (目錄內(nèi)費(fèi)用 - 起付線) × 報(bào)銷比例 個(gè)人自付 總費(fèi)用 - 報(bào)銷額而且報(bào)銷比例不是固定的跟醫(yī)院等級(jí)有關(guān)一級(jí)醫(yī)院可能報(bào)銷85%三級(jí)醫(yī)院可能報(bào)銷70%跟人員類別也有關(guān)低保對(duì)象、特困人員往往有更高比例。設(shè)計(jì)的合理做法是把這些比例配置放到系統(tǒng)參數(shù)表或者字典表里后端在計(jì)算時(shí)從配置中去取而不是在代碼里硬編碼。很多剛?cè)腴T的開(kāi)發(fā)者拿到這類項(xiàng)目時(shí)會(huì)覺(jué)得“報(bào)銷金額直接調(diào)一個(gè)接口算出來(lái)就行”。實(shí)際上如果在代碼里直接寫if (hospitalLevel 3) return 0.7那以后醫(yī)院等級(jí)調(diào)整比例、或者新增一檔醫(yī)院級(jí)別都要改代碼重新部署這在真實(shí)項(xiàng)目里是不可接受的。你要做的應(yīng)該是給報(bào)銷比例增加一個(gè)可配置的表前端管理員在后臺(tái)能修改參數(shù)后端計(jì)算時(shí)動(dòng)態(tài)讀取。這樣既鍛煉了你對(duì)數(shù)據(jù)建模的理解也讓項(xiàng)目在答辯時(shí)更有亮點(diǎn)。4.3 數(shù)據(jù)權(quán)限與身份綁定這個(gè)系統(tǒng)還有一個(gè)容易被忽略的問(wèn)題數(shù)據(jù)權(quán)限。系統(tǒng)中的用戶分為管理員和居民。管理員可以查看所有參保人的信息但居民登錄后應(yīng)該只能看到自己及家庭成員的記錄。如果后端接口只做了“登錄驗(yàn)證”而沒(méi)做“數(shù)據(jù)歸屬校驗(yàn)”那一個(gè)居民用戶就能遍歷出所有人的身份證號(hào)和報(bào)銷記錄這是嚴(yán)重的數(shù)據(jù)泄露風(fēng)險(xiǎn)。正確做法是查詢參保人詳情的接口除了要求登錄還要比對(duì)當(dāng)前登錄用戶的ID是否等于被查詢的參保人ID或者是該參保人家庭關(guān)系中的成員。具體實(shí)現(xiàn)方式是在查詢SQL里帶上一個(gè)當(dāng)前用戶ID的過(guò)濾條件。這個(gè)細(xì)節(jié)在很多SSM項(xiàng)目中都沒(méi)做但它在真實(shí)的系統(tǒng)中是一個(gè)硬性需求。如果你是拿這個(gè)項(xiàng)目去面試或做畢業(yè)設(shè)計(jì)把這個(gè)點(diǎn)做好絕對(duì)是一個(gè)明顯的加分項(xiàng)。4.4 前端表格、校驗(yàn)與狀態(tài)轉(zhuǎn)換再?gòu)那岸私嵌日f(shuō)兩個(gè)操作細(xì)節(jié)。表格頁(yè)面是一眼就能看到的普通管理頁(yè)面但真正寫得好的表格遠(yuǎn)不止調(diào)一個(gè)后端列表接口、然后用循環(huán)渲染出來(lái)。你需要處理分頁(yè)參數(shù)、加載狀態(tài)、空數(shù)據(jù)展示、操作按鈕的權(quán)限開(kāi)關(guān)、搜索表單的重置。這套系統(tǒng)里我建議你先重點(diǎn)看繳費(fèi)記錄列表頁(yè)和報(bào)銷審核列表頁(yè)這兩個(gè)頁(yè)面涉及最多的狀態(tài)字段比如待繳費(fèi)、已繳費(fèi)、待審核、已審核、已駁回不同狀態(tài)下按鈕和標(biāo)簽顏色都不一樣。表單校驗(yàn)也是一個(gè)細(xì)節(jié)密集的地方。身份證號(hào)校驗(yàn)要兼容15位和18位還要能識(shí)別最后一位X手機(jī)號(hào)校驗(yàn)要符合國(guó)內(nèi)號(hào)碼規(guī)則日期選擇器要限制結(jié)束日期不能早于開(kāi)始日期。這個(gè)系統(tǒng)里身份證號(hào)是一個(gè)核心主鍵性質(zhì)的字段前端校驗(yàn)只是第一步后端也必須做同樣的校驗(yàn)不能只信任前端傳過(guò)來(lái)的數(shù)據(jù)。5. 啟動(dòng)運(yùn)行中的常見(jiàn)問(wèn)題排查實(shí)錄我在這類項(xiàng)目上幫不少人排查過(guò)問(wèn)題下面這些場(chǎng)景是出現(xiàn)頻率最高的我把現(xiàn)象、原因和解決辦法整理成一份速查表?,F(xiàn)象可能的根因處理方式后端啟動(dòng)報(bào)數(shù)據(jù)庫(kù)連接失敗MySQL沒(méi)啟動(dòng)、賬號(hào)密碼錯(cuò)誤、URL寫錯(cuò)、服務(wù)端口不對(duì)先用客戶端工具測(cè)試連通性確認(rèn)后改application配置后端啟動(dòng)報(bào)端口被占用8080被其他程序占用在配置文件中改用server.port或找占用端口進(jìn)程殺掉前端頁(yè)面能打開(kāi)登錄請(qǐng)求404前端代理沒(méi)配好、后端上下文路徑不一致檢查vue.config.js里的proxy確認(rèn)是否指向正確的后端地址登錄后返回401Token過(guò)期、請(qǐng)求頭沒(méi)帶Token、攔截器配置過(guò)寬重新登錄檢查axios請(qǐng)求攔截器里的Token設(shè)置中文亂碼數(shù)據(jù)庫(kù)字符集不是utf8mb4、連接URL沒(méi)指定編碼建庫(kù)時(shí)就指定utf8mb4連接URL加useUnicodetrue和characterEncodingutf8分頁(yè)不生效前后端分頁(yè)參數(shù)名字不統(tǒng)一檢查pageNum/pageSize/current/size這幾種命名時(shí)間顯示差了8小時(shí)數(shù)據(jù)庫(kù)時(shí)區(qū)與JVM時(shí)區(qū)不一致連接URL加serverTimezoneAsia/Shanghai前端再格式化npm install報(bào)錯(cuò)Node版本太高或缺少依賴源后端切換到16版本換用npmmirror源再執(zhí)行再補(bǔ)充一個(gè)我在實(shí)際過(guò)程中遇到過(guò)的“經(jīng)典陷阱”執(zhí)行SQL腳本時(shí)如果MySQL版本是8.0而你的腳本里用了老版本的字段類型或索引寫法執(zhí)行到一半會(huì)報(bào)錯(cuò)。解決辦法是先確認(rèn)腳本針對(duì)的MySQL版本如果腳本里出現(xiàn)了ENGINEInnoDB DEFAULT CHARSETutf8在8.0里也能執(zhí)行但如果有PASSWORD()函數(shù)這類老語(yǔ)法就需要手工調(diào)整。還有一次我看到一個(gè)同學(xué)的數(shù)據(jù)庫(kù)里所有表都建好了但頁(yè)面查詢時(shí)報(bào)“表不存在”原因是SQL腳本執(zhí)行時(shí)連接到了默認(rèn)的test庫(kù)建表都建到了另一個(gè)庫(kù)里后端配置的connection庫(kù)和建表的庫(kù)不是同一個(gè)。這種問(wèn)題排查起來(lái)特別容易讓人崩潰所以建庫(kù)之前一定要看清當(dāng)前選中的數(shù)據(jù)庫(kù)。前端還有一個(gè)非常高發(fā)的點(diǎn)頁(yè)面一直白屏打開(kāi)控制臺(tái)顯示Cannot read properties of undefined。這種報(bào)錯(cuò)大部分是某個(gè)接口返回的數(shù)據(jù)結(jié)構(gòu)和前端預(yù)期不匹配比如后端返回了一條空數(shù)組但前端還是直接取data.list再取第一項(xiàng)data.list[0].name。解決方式是前端做防御性判斷或者梳理接口文檔統(tǒng)一字段。6. 從“能跑”到“會(huì)用”后續(xù)擴(kuò)展建議和個(gè)人經(jīng)驗(yàn)項(xiàng)目跑通、代碼也看明白了并不是終點(diǎn)。我建議你在這個(gè)基礎(chǔ)上做一點(diǎn)自己的東西哪怕只是很小的改動(dòng)也能讓這個(gè)項(xiàng)目變成真正屬于你的作品。6.1 基于這套源碼最值得加的三個(gè)功能第一個(gè)值得加的是數(shù)據(jù)可視化看板?,F(xiàn)在的系統(tǒng)里肯定有參保人數(shù)、繳費(fèi)金額、報(bào)銷金額這些數(shù)據(jù)在首頁(yè)用ECharts做幾個(gè)統(tǒng)計(jì)圖比如按年月展示繳費(fèi)趨勢(shì)、按鄉(xiāng)鎮(zhèn)展示參保人數(shù)、按醫(yī)院等級(jí)展示報(bào)銷金額占比項(xiàng)目的展示效果立刻上一個(gè)檔次。數(shù)據(jù)可視化在前后端分離項(xiàng)目中不需要額外引入太重的內(nèi)容只需要后端寫一個(gè)統(tǒng)計(jì)接口返回聚合數(shù)據(jù)前端用圖表組件渲染。第二個(gè)值得加的是批量導(dǎo)入導(dǎo)出。醫(yī)保系統(tǒng)中從Excel導(dǎo)入?yún)⒈H诵畔⑹欠浅傂璧牟僮?。你可以用后端解析Excel逐行校驗(yàn)數(shù)據(jù)格式再批量插入數(shù)據(jù)庫(kù)。同時(shí)把查詢結(jié)果導(dǎo)出為Excel文件這個(gè)功能在答辯現(xiàn)場(chǎng)演示的時(shí)候效果很好。用EasyExcel或者POI都可以實(shí)現(xiàn)建議優(yōu)先用EasyExcel內(nèi)存占用低且API更簡(jiǎn)單。第三個(gè)值得加的是消息通知。繳費(fèi)提醒、報(bào)銷進(jìn)度通知這類場(chǎng)景可以通過(guò)系統(tǒng)站內(nèi)信或者短信通知實(shí)現(xiàn)。作為學(xué)習(xí)項(xiàng)目可以先做一個(gè)站內(nèi)信模塊的新增和已讀功能把通知表接到業(yè)務(wù)狀態(tài)變更點(diǎn)上比如報(bào)銷審核通過(guò)之后自動(dòng)寫入一條站內(nèi)信。這樣你就把“流程”和“通知”結(jié)合起來(lái)了而不只是做一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)寫入。6.2 換業(yè)務(wù)域復(fù)用的思路這套系統(tǒng)的開(kāi)發(fā)模式也可以快速遷移到其他業(yè)務(wù)場(chǎng)景。把“參保人”換成“業(yè)主”把“繳費(fèi)記錄”換成“物業(yè)費(fèi)流水”把“報(bào)銷申請(qǐng)”換成“維修工單”再把字段名稱改一下你就能很快搭出來(lái)一套物業(yè)管理系統(tǒng)。同理?yè)Q上教師、學(xué)生和課程數(shù)據(jù)也能變成校園管理平臺(tái)。原因在于這種系統(tǒng)的骨架是通用的一套登錄權(quán)限體系加一套主數(shù)據(jù)和從屬記錄的管理模型再加上一條狀態(tài)流程。真正需要重寫的是業(yè)務(wù)規(guī)則計(jì)算比如報(bào)銷比例、起付線、封頂線這種東西換成其他行業(yè)就完全不通用了。所以我在學(xué)習(xí)任何一套系統(tǒng)源碼的時(shí)候都會(huì)刻意把“通用骨架”和“行業(yè)規(guī)則”分開(kāi)看。骨架幫你提升開(kāi)發(fā)效率而行業(yè)規(guī)則才是你和別人拉開(kāi)差距的地方。6.3 一點(diǎn)個(gè)人體會(huì)做這類前后端分離的管理系統(tǒng)我個(gè)人最大的體會(huì)是跑通一個(gè)項(xiàng)目不等于學(xué)會(huì)一個(gè)項(xiàng)目。很多同學(xué)第一件事就是一鍵啟動(dòng)看到界面出來(lái)就覺(jué)得完事了。但如果你真的把數(shù)據(jù)表一張接一張看過(guò)去把報(bào)銷計(jì)算的步驟用代碼走讀一遍再跟著前端的路由和API請(qǐng)求把一條業(yè)務(wù)流串起來(lái)這個(gè)項(xiàng)目對(duì)你的價(jià)值會(huì)翻好幾倍。我在實(shí)際做過(guò)的類似系統(tǒng)里踩過(guò)不少坑其中印象最深的一次是上線時(shí)沒(méi)留意前端代理配置導(dǎo)致打包后的靜態(tài)文件發(fā)到服務(wù)器上之后所有請(qǐng)求都還在往localhost發(fā)。這個(gè)問(wèn)題看起來(lái)低級(jí)但在聯(lián)調(diào)和部署階段非常常見(jiàn)。后來(lái)我的習(xí)慣是前端代碼里永遠(yuǎn)不寫死完整后端地址本地用代理生產(chǎn)用Nginx轉(zhuǎn)發(fā)。這個(gè)習(xí)慣在這套源碼上同樣適用——你以后把它部署到任何環(huán)境只要專配好反向代理就不用翻著前端代碼改請(qǐng)求地址。這套項(xiàng)目本身不算大但它把SpringBoot、Vue和MySQL的組合用法完整串聯(lián)起來(lái)了。對(duì)想要打牢基礎(chǔ)的人來(lái)說(shuō)靜下心來(lái)把里面每一張表、每一個(gè)接口、每一步業(yè)務(wù)流轉(zhuǎn)都吃透比急著去學(xué)微服務(wù)和分布式架構(gòu)要實(shí)在得多。最后再分享一個(gè)小技巧拿到這類帶源碼的項(xiàng)目先在SQL腳本里看表結(jié)構(gòu)再順著“登錄-參保登記-繳費(fèi)-報(bào)銷”這條主線去讀代碼你的理解速度要比從代碼逐行看快得多。