院門診管理系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)全解析)
簡(jiǎn)介在Java Web開發(fā)與數(shù)據(jù)庫(kù)設(shè)計(jì)的學(xué)習(xí)路徑中如何將一個(gè)真實(shí)的業(yè)務(wù)場(chǎng)景落地為可運(yùn)行的系統(tǒng)是開發(fā)者從理論走向工程實(shí)踐的關(guān)鍵一步。醫(yī)院門診管理系統(tǒng)正是這樣一個(gè)典型范例它涵蓋了掛號(hào)、分診、醫(yī)生接診、開方、收費(fèi)、發(fā)藥等完整業(yè)務(wù)鏈路涉及多角色權(quán)限控制與業(yè)務(wù)狀態(tài)流轉(zhuǎn)。從技術(shù)原理看這類系統(tǒng)通?;贘ava技術(shù)棧采用分層架構(gòu)設(shè)計(jì)通過(guò)JSP/Servlet或Spring Boot等框架實(shí)現(xiàn)前后端交互并依靠MySQL進(jìn)行數(shù)據(jù)持久化。其中數(shù)據(jù)庫(kù)設(shè)計(jì)尤為核心主從表結(jié)構(gòu)、外鍵約束、狀態(tài)字段的合理規(guī)劃直接影響系統(tǒng)的擴(kuò)展性與穩(wěn)定性。此類系統(tǒng)的應(yīng)用場(chǎng)景廣泛既可作為高校課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)的參考項(xiàng)目也可作為開發(fā)者理解企業(yè)級(jí)業(yè)務(wù)系統(tǒng)建模的入門案例。本文圍繞一套Java實(shí)現(xiàn)的智慧醫(yī)院門診管理系統(tǒng)從業(yè)務(wù)流程梳理、數(shù)據(jù)庫(kù)設(shè)計(jì)、核心代碼實(shí)現(xiàn)到部署排錯(cuò)系統(tǒng)性復(fù)盤了項(xiàng)目從零搭建到運(yùn)行的全過(guò)程為同類項(xiàng)目的開發(fā)與優(yōu)化提供了詳實(shí)的實(shí)踐經(jīng)驗(yàn)。 在醫(yī)院信息化建設(shè)這個(gè)大背景下門診管理系統(tǒng)一直是個(gè)繞不開的經(jīng)典項(xiàng)目。最近正好在整理一套基于Java實(shí)現(xiàn)的智慧醫(yī)院門診管理系統(tǒng)包括完整源碼、設(shè)計(jì)文檔、實(shí)驗(yàn)報(bào)告、數(shù)據(jù)庫(kù)SQL文件。我把它完整復(fù)盤了一遍從業(yè)務(wù)流程梳理到數(shù)據(jù)庫(kù)設(shè)計(jì)再到核心代碼實(shí)現(xiàn)把整個(gè)思路和踩過(guò)的坑都整理出來(lái)希望對(duì)正在做同類項(xiàng)目的朋友有所幫助。這套系統(tǒng)涵蓋了門診業(yè)務(wù)的主線流程掛號(hào)、分診、醫(yī)生接診、開立處方、收費(fèi)結(jié)算、藥房發(fā)藥。它面向的是三類典型用戶掛號(hào)收費(fèi)員、門診醫(yī)生、系統(tǒng)管理員。如果你是剛學(xué)完JavaSE或者SSM框架想找一個(gè)能落地、能答辯、能寫進(jìn)簡(jiǎn)歷的完整項(xiàng)目這份資料應(yīng)該是比較合適的參考對(duì)象。如果你只是想快速讀懂“醫(yī)院門診系統(tǒng)到底怎么設(shè)計(jì)”這篇復(fù)盤也能幫你省掉不少自己摸索引擎、翻文檔的時(shí)間。1. 項(xiàng)目整體設(shè)計(jì)與需求拆解1.1 門診業(yè)務(wù)流程梳理開始編碼之前一定要先把業(yè)務(wù)鏈路想清楚。醫(yī)院門診的數(shù)據(jù)流其實(shí)是環(huán)環(huán)相扣的一個(gè)環(huán)節(jié)斷了后面全亂。一個(gè)典型的門診就診流程大概是這樣的患者到院后先到掛號(hào)窗口掛號(hào)選擇科室和醫(yī)生系統(tǒng)生成掛號(hào)記錄隨后患者到相應(yīng)科室候診醫(yī)生在系統(tǒng)中看到待診患者列表按順序叫號(hào)接診接診過(guò)程中醫(yī)生詢問(wèn)病情、記錄診斷結(jié)果并開立檢查單或藥品處方患者拿著處方去收費(fèi)窗口繳費(fèi)系統(tǒng)更新繳費(fèi)狀態(tài)繳費(fèi)完成后藥房看到已繳費(fèi)的處方信息進(jìn)行配藥發(fā)藥整個(gè)流程閉環(huán)。從系統(tǒng)設(shè)計(jì)的角度看核心實(shí)體有患者、科室、醫(yī)生、掛號(hào)單、處方單、藥品。實(shí)體間的關(guān)系并不復(fù)雜難點(diǎn)在于狀態(tài)流轉(zhuǎn)。掛號(hào)單有“已掛號(hào)、已就診、已繳費(fèi)、已發(fā)藥”等狀態(tài)每一次操作都會(huì)改變狀態(tài)而且這些狀態(tài)之間有嚴(yán)格的先后約束。開發(fā)時(shí)我的建議是給每張業(yè)務(wù)單子都維護(hù)一個(gè)狀態(tài)字段并用狀態(tài)機(jī)思維去控制流轉(zhuǎn)而不是散落在各個(gè)方法里硬編碼判斷。1.2 核心功能模塊劃分這套系統(tǒng)的功能模塊可以拆成四大塊每塊對(duì)應(yīng)一個(gè)業(yè)務(wù)場(chǎng)景。第一塊是掛號(hào)管理完成科室列表查詢、醫(yī)生出診信息維護(hù)、患者信息登記、掛號(hào)單生成。第二塊是醫(yī)生工作站醫(yī)生登錄后查看待診列表選擇患者進(jìn)行接診記錄診斷結(jié)果開立處方和檢查申請(qǐng)。第三塊是收費(fèi)管理收費(fèi)員根據(jù)處方單進(jìn)行費(fèi)用結(jié)算支持現(xiàn)金、銀聯(lián)等支付方式收費(fèi)完成后自動(dòng)更新處方狀態(tài)。第四塊是藥房管理藥房人員查看已繳費(fèi)處方進(jìn)行藥品出庫(kù)和發(fā)藥操作。除了這四大業(yè)務(wù)模塊系統(tǒng)還必須有基礎(chǔ)數(shù)據(jù)管理和系統(tǒng)管理模塊維護(hù)科室信息、藥品字典、醫(yī)生排班、用戶賬號(hào)和角色權(quán)限。很多初學(xué)者容易忽略權(quán)限控制但醫(yī)院系統(tǒng)對(duì)權(quán)限的要求是比較嚴(yán)格的醫(yī)生只能看到自己的患者和處方收費(fèi)員只能操作收費(fèi)不能看到藥品庫(kù)存明細(xì)。1.3 技術(shù)選型為什么選Java技術(shù)棧這個(gè)項(xiàng)目的技術(shù)棧用的是Java JSP/Servlet MySQL屬于比較經(jīng)典的組合??赡苡信笥褧?huì)問(wèn)現(xiàn)在都用Spring Boot了為什么還選這么老的技術(shù)說(shuō)實(shí)話這個(gè)選型是有明確目的的。第一作為一個(gè)教學(xué)和課程設(shè)計(jì)性質(zhì)的項(xiàng)目JSP/Servlet能讓人更直觀地理解HTTP請(qǐng)求處理、Session管理和MVC分層原理不會(huì)被框架的自動(dòng)配置掩蓋掉底層邏輯。第二這套系統(tǒng)不需要微服務(wù)架構(gòu)也沒(méi)有高并發(fā)壓力單體應(yīng)用配合簡(jiǎn)單的分層設(shè)計(jì)已經(jīng)非常夠用。第三如果你后續(xù)想升級(jí)到Spring Boot業(yè)務(wù)邏輯和數(shù)據(jù)庫(kù)設(shè)計(jì)完全可以直接遷移Dao層換成MyBatis或者Spring Data JPA就可以了改造成本很低。我個(gè)人在實(shí)際開發(fā)中的習(xí)慣是不管用什么框架先把業(yè)務(wù)邏輯層Service的接口設(shè)計(jì)好把事務(wù)邊界劃清楚。這樣以后再換技術(shù)棧Service層幾乎不用動(dòng)只動(dòng)Controller和Dao層。2. 數(shù)據(jù)庫(kù)設(shè)計(jì)與SQL文件解析2.1 核心表結(jié)構(gòu)設(shè)計(jì)數(shù)據(jù)庫(kù)設(shè)計(jì)是這套系統(tǒng)的重頭戲也是設(shè)計(jì)文檔和實(shí)驗(yàn)報(bào)告里最容易出彩的部分。我設(shè)計(jì)的核心表一共有10張左右這里挑幾張有代表性的來(lái)說(shuō)。患者表主要字段包括患者編號(hào)、姓名、性別、出生日期、身份證號(hào)、聯(lián)系電話、家庭住址。掛號(hào)單表是這個(gè)系統(tǒng)的核心業(yè)務(wù)表字段包括掛號(hào)單號(hào)、患者ID、科室ID、醫(yī)生ID、掛號(hào)時(shí)間、就診狀態(tài)、掛號(hào)費(fèi)用。需要注意掛號(hào)單號(hào)建議用自增主鍵加日期前綴組合生成比如20251201_001這種格式這樣光看單號(hào)就能知道是哪天的業(yè)務(wù)排查問(wèn)題非常方便。處方表是整個(gè)系統(tǒng)中業(yè)務(wù)邏輯最復(fù)雜的表。一個(gè)患者一次就診可能開出多條藥品處方所以需要設(shè)計(jì)成主表和明細(xì)表的結(jié)構(gòu)處方主表記錄處方號(hào)、掛號(hào)單號(hào)、醫(yī)生ID、開方時(shí)間、總金額處方明細(xì)表記錄每條藥品信息包括藥品ID、藥品名稱、單價(jià)、數(shù)量、金額。這種設(shè)計(jì)就叫“主從表”或“父子表”結(jié)構(gòu)是實(shí)際業(yè)務(wù)系統(tǒng)中最常用的建模方式。藥品表要特別注意兩個(gè)字段庫(kù)存數(shù)量和預(yù)警閾值。藥房發(fā)藥的時(shí)候程序要判斷庫(kù)存是否充足是否達(dá)到預(yù)警線。我當(dāng)時(shí)的做法是給藥品表加了stock_quantity和warn_threshold兩個(gè)字段發(fā)藥時(shí)先扣減庫(kù)存如果扣減后低于預(yù)警閾值就給出提示消息。2.2 表關(guān)系與外鍵設(shè)計(jì)表關(guān)系的設(shè)計(jì)主要圍繞掛號(hào)單展開?;颊吆蛼焯?hào)單是一對(duì)多的關(guān)系一個(gè)患者可以有多次掛號(hào)記錄??剖液蛼焯?hào)單是一對(duì)多關(guān)系醫(yī)生和掛號(hào)單也是一對(duì)多關(guān)系。掛號(hào)單和處方主表是一對(duì)一關(guān)系一次掛號(hào)就診通常對(duì)應(yīng)一張?zhí)幏街鞅?。而處方主表和處方明?xì)表是一對(duì)多關(guān)系一張主表包含多條明細(xì)記錄。外鍵約束這塊很多教材和網(wǎng)上的項(xiàng)目都有兩種聲音一種說(shuō)必須加外鍵保證數(shù)據(jù)完整性另一種說(shuō)不加外鍵靠應(yīng)用層控制。我的個(gè)人建議是教學(xué)演示項(xiàng)目一定加上外鍵約束因?yàn)閷?shí)驗(yàn)報(bào)告里可以寫得很有說(shuō)服力展示你理解了數(shù)據(jù)庫(kù)的完整性約束機(jī)制。但在實(shí)際生產(chǎn)環(huán)境中為了高并發(fā)寫入性能很多團(tuán)隊(duì)會(huì)選擇去掉外鍵用應(yīng)用層邏輯保證數(shù)據(jù)一致性。初始化SQL文件里我建議把建庫(kù)語(yǔ)句、建表語(yǔ)句、插入基礎(chǔ)數(shù)據(jù)的語(yǔ)句分開寫清楚并且都用帶IF NOT EXISTS的寫法。這樣做的好處是重復(fù)執(zhí)行SQL腳本不會(huì)報(bào)錯(cuò)對(duì)小白非常友好。2.3 SQL文件交付的規(guī)范處理拿到這套資料的第一時(shí)間建議先看database目錄下的SQL文件。我的習(xí)慣是把這個(gè)目錄整理成四個(gè)部分01_create_database.sql負(fù)責(zé)創(chuàng)建數(shù)據(jù)庫(kù)和指定字符集02_create_tables.sql負(fù)責(zé)建表03_insert_base_data.sql負(fù)責(zé)插入科室、藥品、管理員賬號(hào)等基礎(chǔ)數(shù)據(jù)04_test_data.sql負(fù)責(zé)插入一些模擬患者和掛號(hào)記錄方便測(cè)試聯(lián)調(diào)。有一個(gè)細(xì)節(jié)值得單獨(dú)說(shuō)一下字符集的問(wèn)題。建庫(kù)語(yǔ)句里我統(tǒng)一用了utf8mb4而不是utf8。在MySQL 5.5之前utf8是夠用的但后來(lái)遇到生僻字的時(shí)候utf8會(huì)因?yàn)樽疃嘀恢С?字節(jié)而報(bào)錯(cuò)。utf8mb4是UTF-8的完整實(shí)現(xiàn)能支持4字節(jié)字符比如一些生僻字和特殊符號(hào)。醫(yī)院系統(tǒng)里患者姓名有時(shí)候會(huì)有生僻字所以統(tǒng)一用utf8mb4是更穩(wěn)妥的。3. 核心功能實(shí)現(xiàn)與代碼解析3.1 掛號(hào)模塊的實(shí)現(xiàn)思路掛號(hào)模塊是患者進(jìn)入系統(tǒng)的第一步它的核心邏輯是選擇科室查看該科室出診的醫(yī)生登記患者信息如果患者已經(jīng)存在就直接選擇生成掛號(hào)單。我寫掛號(hào)模塊時(shí)把流程分成了兩步。第一步是患者身份確認(rèn)通過(guò)輸入身份證號(hào)在患者表里查詢?nèi)绻椴坏骄妥詣?dòng)跳轉(zhuǎn)到新增患者頁(yè)面。第二步才是創(chuàng)建掛號(hào)單。這里有一個(gè)容易出錯(cuò)的地方New患者和掛號(hào)單創(chuàng)建必須是在同一個(gè)事務(wù)里完成的不然可能出現(xiàn)患者信息保存了掛號(hào)單沒(méi)生成的情況。用代碼表述大概是這樣的邏輯進(jìn)入掛號(hào)頁(yè)面先傳一個(gè)patientId參數(shù)如果為0或者空就先把患者信息插入到patient表中返回自增的patientId然后再拿著這個(gè)patientId去創(chuàng)建掛號(hào)記錄。掛號(hào)記錄里registration_status初始值為“1”表示已掛號(hào)待就診。3.2 醫(yī)生工作站的處方流程醫(yī)生工作站的邏輯是整個(gè)系統(tǒng)最復(fù)雜的模塊涉及三個(gè)表的聯(lián)動(dòng)操作接診更新掛號(hào)單狀態(tài)、寫入診斷記錄、開立處方主表和處方明細(xì)表。我從一開始就意識(shí)到如果不限制操作順序很容易出現(xiàn)“診斷記錄寫了處方?jīng)]開成”這種半截?cái)?shù)據(jù)。所以我的編碼方案是所有數(shù)據(jù)庫(kù)操作放在同一個(gè)Service方法里并且加上事務(wù)控制。Java中事務(wù)控制其實(shí)有兩種路徑一種是早期純JDBC的手動(dòng)事務(wù)管理用Connection.setAutoCommit(false)加上commit/rollback另一種是后來(lái)我們更推薦的Spring聲明式事務(wù)用一個(gè)Transactional注解搞定。這個(gè)項(xiàng)目里因?yàn)橛玫氖荢ervlet JDBC所以手動(dòng)管理事務(wù)的代碼比較多但這反而是學(xué)習(xí)事務(wù)機(jī)制的好機(jī)會(huì)。在展示處方明細(xì)時(shí)要注意數(shù)量的單位問(wèn)題。藥品表里我統(tǒng)一用“盒”作為最小單位而不是“片”或者“袋”。這樣處理的好處是處方明細(xì)里的數(shù)量和藥品庫(kù)存表里的數(shù)量可以精確對(duì)應(yīng)不用擔(dān)心單位換算的問(wèn)題。真實(shí)醫(yī)院系統(tǒng)里藥品單位管理比這個(gè)復(fù)雜得多會(huì)有拆零、換算等邏輯但教學(xué)項(xiàng)目做到統(tǒng)一單位這個(gè)程度已經(jīng)足夠。3.3 收費(fèi)結(jié)算模塊的狀態(tài)聯(lián)動(dòng)收費(fèi)模塊看起來(lái)簡(jiǎn)單做起來(lái)坑不少。收費(fèi)員查到這個(gè)患者的處方確認(rèn)金額后點(diǎn)擊“收費(fèi)”系統(tǒng)要做的事情包括更新掛號(hào)單的繳費(fèi)狀態(tài)、更新處方的繳費(fèi)狀態(tài)、記錄收費(fèi)流水包括收費(fèi)員ID、收費(fèi)金額、收費(fèi)時(shí)間、支付方式。這里有一個(gè)細(xì)節(jié)經(jīng)常被忽略收費(fèi)之后要生成一條收費(fèi)流水記錄。很多初學(xué)者只更新處方狀態(tài)沒(méi)有記錄流水導(dǎo)致后期對(duì)賬的時(shí)候發(fā)現(xiàn)金額對(duì)不上。我在這套系統(tǒng)里加了一張payment_record表每次收費(fèi)都插入一條記錄并且記錄操作員ID。實(shí)驗(yàn)報(bào)告里如果寫到“本系統(tǒng)具備完整審計(jì)追溯功能”這一張表就是最強(qiáng)的佐證。收費(fèi)的邏輯里面需要用事務(wù)確?!案聮焯?hào)單狀態(tài)”和“插入收費(fèi)流水”同時(shí)成功或同時(shí)失敗。我在實(shí)際開發(fā)中遇到過(guò)一次比較尷尬的情況第一次寫代碼時(shí)只更新了處方狀態(tài)忘了更新掛號(hào)單狀態(tài)結(jié)果患者拿著處方去藥房藥房系統(tǒng)顯示“已繳費(fèi)”但掛號(hào)單還是“待繳費(fèi)”狀態(tài)給后續(xù)統(tǒng)計(jì)帶來(lái)了很大麻煩。后來(lái)我統(tǒng)一整理業(yè)務(wù)流程時(shí)把所有狀態(tài)更新操作都集中到了Service層才徹底解決這個(gè)問(wèn)題。4. 設(shè)計(jì)文檔與實(shí)驗(yàn)報(bào)告的寫作經(jīng)驗(yàn)4.1 設(shè)計(jì)文檔應(yīng)該怎么寫很多人拿到的設(shè)計(jì)文檔模板可能都差不多但核心在于內(nèi)容是否充實(shí)。我覺(jué)得設(shè)計(jì)文檔至少要包含五個(gè)部分需求分析、總體設(shè)計(jì)、詳細(xì)設(shè)計(jì)、數(shù)據(jù)庫(kù)設(shè)計(jì)、測(cè)試用例。需求分析部分不要寫空話畫出用例圖列出每個(gè)角色的核心操作。總體設(shè)計(jì)部分給出系統(tǒng)架構(gòu)圖說(shuō)明分層思想。詳細(xì)設(shè)計(jì)部分針對(duì)每個(gè)模塊寫清楚輸入、處理流程、輸出。數(shù)據(jù)庫(kù)設(shè)計(jì)部分除了表結(jié)構(gòu)還應(yīng)該有E-R圖和數(shù)據(jù)字典。測(cè)試用例部分是很多人容易忽視的但恰恰是實(shí)驗(yàn)報(bào)告和答辯時(shí)最加分的因?yàn)檫@能證明你真正運(yùn)行過(guò)系統(tǒng)。我有一個(gè)習(xí)慣所有設(shè)計(jì)文檔里的表格都用統(tǒng)一的格式來(lái)寫字段名、類型、長(zhǎng)度、是否主鍵、是否允許為空、字段說(shuō)明。這樣不僅自己看的時(shí)候舒服老師或者面試官翻起來(lái)也一目了然。4.2 實(shí)驗(yàn)報(bào)告的亮點(diǎn)提煉實(shí)驗(yàn)報(bào)告和設(shè)計(jì)文檔在內(nèi)容上會(huì)有重疊但側(cè)重點(diǎn)不同。實(shí)驗(yàn)報(bào)告的核心是“實(shí)驗(yàn)過(guò)程”和“實(shí)驗(yàn)結(jié)果”要突出你做了什么、怎么做的、結(jié)果如何。我的建議是把實(shí)驗(yàn)報(bào)告的重心放在三個(gè)點(diǎn)系統(tǒng)運(yùn)行截圖、測(cè)試過(guò)程記錄、問(wèn)題與解決記錄。運(yùn)行截圖要按業(yè)務(wù)場(chǎng)景來(lái)截比如“掛號(hào)成功頁(yè)面”“醫(yī)生開方頁(yè)面”“收費(fèi)完成頁(yè)面”等。測(cè)試記錄要寫清楚測(cè)試數(shù)據(jù)、測(cè)試步驟和預(yù)期結(jié)果最好做成表格形式。問(wèn)題與解決記錄是報(bào)告最有含金量的部分比如“遇到JDBC連接MySQL 8.x時(shí)報(bào)錯(cuò)查資料發(fā)現(xiàn)驅(qū)動(dòng)類名改了需要添加serverTimezoneAsia/Shanghai參數(shù)”這種細(xì)節(jié)能明顯體現(xiàn)出你的動(dòng)手能力和排查問(wèn)題的能力。5. 項(xiàng)目從零跑通的全過(guò)程記錄5.1 環(huán)境準(zhǔn)備與工具選擇跑通這個(gè)項(xiàng)目的環(huán)境其實(shí)非常簡(jiǎn)單但版本細(xì)節(jié)要注意。JDK 1.8即可這個(gè)項(xiàng)目沒(méi)用到任何高版本特性。Tomcat建議用8.5或者9.0版本。MySQL這邊需要注意如果用8.x版本JDBC驅(qū)動(dòng)必須用mysql-connector-java8.0以上版本驅(qū)動(dòng)類名也發(fā)生了變化如果繼續(xù)用老版本的com.mysql.jdbc.Driver運(yùn)行時(shí)會(huì)直接報(bào)錯(cuò)。IDE方面Eclipse和IDEA都可以。我自己的習(xí)慣是用IDEA因?yàn)樗鼉?nèi)置了數(shù)據(jù)庫(kù)工具可以直接連MySQL執(zhí)行SQL腳本非常方便。如果你用的是IDEA打開項(xiàng)目時(shí)要注意設(shè)置項(xiàng)目的JDK版本和編譯級(jí)別避免出現(xiàn)發(fā)布版本不一致的問(wèn)題。5.2 配置JDBC連接參數(shù)的陷阱系統(tǒng)運(yùn)行前需要修改數(shù)據(jù)庫(kù)連接配置文件通常在src/jdbc.properties或者db.properties里。核心參數(shù)有四個(gè)URL、用戶名、密碼、驅(qū)動(dòng)類。數(shù)據(jù)庫(kù)URL的格式稍有講究jdbc:mysql://localhost:3306/hospital?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8。這里面的serverTimezone參數(shù)非常重要MySQL 8.x默認(rèn)時(shí)區(qū)是美國(guó)時(shí)區(qū)如果不指定數(shù)據(jù)庫(kù)連接時(shí)可能報(bào)錯(cuò)。另外characterEncodingutf8可以保證連接的編碼是UTF-8預(yù)防中文亂碼問(wèn)題。說(shuō)實(shí)話中文亂碼是這類Java Web項(xiàng)目里出現(xiàn)頻率最高的問(wèn)題至少有一半的同學(xué)跑不起來(lái)系統(tǒng)都是因?yàn)閬y碼。亂碼的類型大概有三層頁(yè)面顯示亂碼、請(qǐng)求參數(shù)亂碼、數(shù)據(jù)庫(kù)存儲(chǔ)亂碼。要徹底解決必須統(tǒng)一全鏈路編碼。JSP頁(yè)面設(shè)置% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%Servlet里設(shè)置request.setCharacterEncoding(UTF-8)和response.setContentType(text/html;charsetUTF-8)數(shù)據(jù)庫(kù)連接URL里加characterEncodingutf8建表時(shí)字符集用utf8mb4。只要這幾處都統(tǒng)一成UTF-8亂碼問(wèn)題基本就不會(huì)出現(xiàn)。5.3 部署時(shí)常見的運(yùn)行異常與排查思路我再整理幾個(gè)最初跑這個(gè)項(xiàng)目時(shí)可能遇到的典型報(bào)錯(cuò)并附上排查思路。報(bào)錯(cuò)java.lang.ClassNotFoundException: com.mysql.jdbc.Driver這個(gè)最常見的原因有兩個(gè)一是沒(méi)有把MySQL的JDBC驅(qū)動(dòng)JAR包放進(jìn)WEB-INF/lib目錄二是在MySQL 8.x環(huán)境下驅(qū)動(dòng)類名寫錯(cuò)了。生產(chǎn)驅(qū)動(dòng)JAR包的正確位置是WEB-INF/lib目錄不是Build Path里加一下就完事了部署到Tomcat后Tomcat只認(rèn)WEB-INF/lib下的JAR包。報(bào)錯(cuò)Access denied for user rootlocalhost這個(gè)一般是數(shù)據(jù)庫(kù)密碼設(shè)置不對(duì)或者當(dāng)前用戶沒(méi)有遠(yuǎn)程連接權(quán)限。開發(fā)時(shí)可以先在MySQL客戶端里用同樣的用戶名和密碼登錄試試如果客戶端能登錄大概率就是連接URL或者驅(qū)動(dòng)的問(wèn)題。報(bào)錯(cuò)HTTP Status 500 - java.sql.SQLException: Connection refused意思是無(wú)法連接數(shù)據(jù)庫(kù)服務(wù)。先檢查MySQL服務(wù)是否啟動(dòng)Windows下可以在服務(wù)管理器里看MySQL服務(wù)狀態(tài)或者用命令行執(zhí)行net start mysql。還要確認(rèn)MySQL的端口號(hào)是不是默認(rèn)的3306如果你改過(guò)端口連接URL里也要同步改。6. 項(xiàng)目復(fù)盤與可優(yōu)化的方向6.1 現(xiàn)狀復(fù)盤這套系統(tǒng)整體框架是完整的業(yè)務(wù)主線流程全部跑通代碼結(jié)構(gòu)上分了Entity、Dao、Service、Servlet、JSP幾個(gè)層次適合初學(xué)者作為學(xué)習(xí)參考。但從工程化角度看還有幾個(gè)明顯的短板。最突出的是安全防護(hù)基本為零密碼是明文存儲(chǔ)沒(méi)有做參數(shù)化校驗(yàn)也沒(méi)有應(yīng)對(duì)SQL注入的加固。雖然這是大多數(shù)教學(xué)項(xiàng)目的通病但如果打算把這份經(jīng)歷寫進(jìn)簡(jiǎn)歷或者拿去求職面試官大概率會(huì)追問(wèn)這些問(wèn)題建議提前想好應(yīng)對(duì)方案。并發(fā)控制也很薄弱。真實(shí)醫(yī)院門診在早高峰期間掛號(hào)請(qǐng)求并發(fā)量是非常高的如果不做防重復(fù)提交、不做庫(kù)存鎖控制容易出現(xiàn)超賣或藥品庫(kù)存負(fù)數(shù)的情況。教學(xué)項(xiàng)目里可以用簡(jiǎn)單的synchronized或者數(shù)據(jù)庫(kù)樂(lè)觀鎖來(lái)模擬解決但在文檔和面試時(shí)要說(shuō)清楚這個(gè)方案的局限性。6.2 從初級(jí)項(xiàng)目到生產(chǎn)級(jí)系統(tǒng)的升級(jí)路徑如果把這份代碼作為起點(diǎn)后續(xù)升級(jí)路徑其實(shí)很清晰。第一層升級(jí)是框架替換把Servlet JSP替換成Spring Boot MyBatis減少大量樣板代碼通過(guò)Transactional簡(jiǎn)化事務(wù)管理。第二層升級(jí)是視圖層升級(jí)JSP換成當(dāng)前更主流的Vue或React前后端通過(guò)JSON交互職責(zé)分離更清晰。第三層升級(jí)是加了登錄認(rèn)證框架比如Spring Security或Shiro把權(quán)限模型從簡(jiǎn)單的“角色-用戶”升級(jí)為“用戶-角色-菜單-按鈕”的多級(jí)權(quán)限。第四層升級(jí)是引入Redis用于緩存科室、醫(yī)生、藥品字典等熱點(diǎn)數(shù)據(jù)以及實(shí)現(xiàn)分布式Session。這些升級(jí)不一定要全部做但寫實(shí)驗(yàn)報(bào)告或做技術(shù)分享的時(shí)候把“未來(lái)展望”這部分寫出來(lái)會(huì)顯得你思考得比較深。6.3 面試中圍繞該項(xiàng)目的常見追問(wèn)我總結(jié)過(guò)面試官圍繞這類項(xiàng)目最常問(wèn)的十個(gè)問(wèn)題建議提前準(zhǔn)備答案。第一講講項(xiàng)目的業(yè)務(wù)流程和模塊劃分。第二數(shù)據(jù)庫(kù)有哪些表為什么這樣設(shè)計(jì)。第三掛號(hào)單狀態(tài)是怎么流轉(zhuǎn)的如何保證狀態(tài)正確。第四如果一個(gè)患者掛號(hào)后沒(méi)有就診系統(tǒng)如何處理。第五處方收費(fèi)的時(shí)候并發(fā)情況下如何避免重復(fù)扣費(fèi)和重復(fù)收費(fèi)。第六項(xiàng)目里遇到的比較棘手的問(wèn)題是什么怎么解決的。第七為什么選擇JDBC而不是MyBatis或者Hibernate。第八如果患者數(shù)量很大系統(tǒng)怎么擴(kuò)展數(shù)據(jù)庫(kù)怎么優(yōu)化。第九你是怎么測(cè)試這個(gè)系統(tǒng)的用了哪些測(cè)試方法。第十這個(gè)系統(tǒng)如果給你繼續(xù)做你最想優(yōu)化哪些點(diǎn)和為什么。這里我想重點(diǎn)說(shuō)一下第一題很多人在介紹項(xiàng)目時(shí)容易變成“報(bào)菜名”把我的模塊名稱一個(gè)個(gè)念出來(lái)這樣既浪費(fèi)機(jī)會(huì)又顯得沒(méi)有理解。我個(gè)人的建議是先一句話概括系統(tǒng)的定位和用戶群體再講核心業(yè)務(wù)鏈路最后才展開模塊細(xì)節(jié)。具體可以這樣說(shuō)“這是一個(gè)面向醫(yī)院門診場(chǎng)景的業(yè)務(wù)管理系統(tǒng)核心是解決患者從掛號(hào)到取藥的全流程辦理主要用戶分三塊大廳掛號(hào)收費(fèi)人員、門診醫(yī)生和藥房人員。系統(tǒng)核心流程為掛號(hào)、接診開方、繳費(fèi)、發(fā)藥四個(gè)節(jié)點(diǎn)通過(guò)業(yè)務(wù)單據(jù)的狀態(tài)流轉(zhuǎn)串聯(lián)起來(lái)?!边@樣短短幾句話業(yè)務(wù)邏輯就清楚了。7. 實(shí)用經(jīng)驗(yàn)與心得匯總通篇寫下來(lái)最后分享幾個(gè)我實(shí)際項(xiàng)目里體會(huì)最深的地方。第一點(diǎn)開發(fā)任何業(yè)務(wù)系統(tǒng)之前先畫業(yè)務(wù)流程圖。哪怕只用筆在紙上畫也要先把流程梳理清楚。業(yè)務(wù)流程不清晰的時(shí)候代碼寫得越快坑填得越多。第二點(diǎn)數(shù)據(jù)庫(kù)設(shè)計(jì)是這類型項(xiàng)目的核心。表結(jié)構(gòu)設(shè)計(jì)得好后續(xù)業(yè)務(wù)擴(kuò)展會(huì)非常輕松。比如掛號(hào)單表、處方主表和明細(xì)表這些基礎(chǔ)結(jié)構(gòu)只要設(shè)計(jì)合理即使后面換成Spring Boot、改成微服務(wù)架構(gòu)核心的字段也基本不需要大動(dòng)。第三點(diǎn)異常處理和日志輸出不能省。我最初開發(fā)時(shí)偷懶catch塊里就寫一個(gè)e.printStackTrace()結(jié)果出問(wèn)題時(shí)只知道報(bào)錯(cuò)完全看不清上下文。后來(lái)我要求自己在每一個(gè)catch里都輸出一個(gè)帶有定位信息的日志比如log.error(更新掛號(hào)單狀態(tài)失敗, registerId{}, registerId, e)。這看起來(lái)是小改動(dòng)但排查問(wèn)題效率完全兩樣。第四點(diǎn)資料整理的規(guī)范性會(huì)直接影響別人對(duì)你項(xiàng)目的評(píng)價(jià)。目錄結(jié)構(gòu)清晰、注釋規(guī)范、文檔完整這些看似不起眼的細(xì)節(jié)恰恰是你的工程素養(yǎng)的體現(xiàn)。我給的這套zip包里源碼和文檔是按目錄分開的如果你要二次開發(fā)也建議保持這種組織方式。這種好習(xí)慣對(duì)未來(lái)無(wú)論是求職還是進(jìn)入團(tuán)隊(duì)工作都能獲得正向反饋。本文還有配套的精品資源點(diǎn)擊獲取