設(shè)計(jì):學(xué)生疫情信息管理系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn))
關(guān)于畢業(yè)設(shè)計(jì)很多同學(xué)一上來(lái)就問(wèn)選什么題目但真正的問(wèn)題其實(shí)是選一個(gè)什么技術(shù)棧的題目。JSP學(xué)生疫情信息管理系統(tǒng)這算是我見(jiàn)過(guò)最典型的畢設(shè)題目之一了。它有明確的管理系統(tǒng)特征有用戶(hù)角色劃分有數(shù)據(jù)流轉(zhuǎn)能展示增刪改查基本功又能掛上統(tǒng)計(jì)圖表這類(lèi)加分項(xiàng)。更重要的是它完全可以用JSPServletMySQL這套老三樣實(shí)現(xiàn)不需要你臨時(shí)去啃Spring Boot和Vue門(mén)檻適中工作量可控非常適合用來(lái)完成本科畢設(shè)。這篇文章不打算給你推倒重來(lái)的代碼也不會(huì)列一長(zhǎng)串第一步干嘛、第二步干嘛那種假大空的清單。我想以一個(gè)帶過(guò)多年畢設(shè)、也親自手寫(xiě)過(guò)這類(lèi)系統(tǒng)的視角把這個(gè)題目從選題邏輯、數(shù)據(jù)庫(kù)設(shè)計(jì)、代碼分層到答辯準(zhǔn)備的全過(guò)程拆開(kāi)講一遍。你會(huì)知道哪些地方值得認(rèn)真做哪些地方是大多數(shù)人的通病以及那些只有在實(shí)際動(dòng)手時(shí)才會(huì)踩到的坑。1. 為什么JSP學(xué)生疫情信息管理這個(gè)題目經(jīng)久不衰選題邏輯與技術(shù)棧分析1.1 核心需求拆解這個(gè)系統(tǒng)到底要管什么先別急著想我要不要用別的框架。畢業(yè)設(shè)計(jì)的第一件事是把題目的業(yè)務(wù)需求拆清楚。學(xué)生疫情信息管理系統(tǒng)看起來(lái)是個(gè)管理系統(tǒng)但它的數(shù)據(jù)模型和普通的學(xué)生管理系統(tǒng)、圖書(shū)管理系統(tǒng)有明顯區(qū)別。普通的CRUD系統(tǒng)重點(diǎn)在增刪改查而疫情信息管理系統(tǒng)的核心其實(shí)是兩個(gè)詞收集和核查。學(xué)生的健康信息是每天或每周產(chǎn)生的它不是一次性的靜態(tài)數(shù)據(jù)而是一條持續(xù)更新的時(shí)間序列。比如體溫記錄、所在地、行程軌跡、健康碼狀態(tài)這些信息在不同時(shí)間點(diǎn)會(huì)產(chǎn)生不同值。所以設(shè)計(jì)數(shù)據(jù)庫(kù)時(shí)如果沿用用戶(hù)表健康表這種一錘子模式后面做統(tǒng)計(jì)和按日期查詢(xún)時(shí)會(huì)極其痛苦。我一般建議把該系統(tǒng)拆成四個(gè)業(yè)務(wù)域基礎(chǔ)信息域?qū)W生檔案、院系班級(jí)、輔導(dǎo)員信息。這是管理系統(tǒng)的地基。健康上報(bào)域?qū)W生的健康打卡、體溫記錄、位置信息、健康狀態(tài)。這對(duì)應(yīng)一張高頻寫(xiě)入的表必須以日期和學(xué)號(hào)做聯(lián)合約束。行程與外出登記域離校申請(qǐng)、返校申請(qǐng)、行程軌跡登記對(duì)應(yīng)審批流程。統(tǒng)計(jì)與預(yù)警域按班級(jí)、院系維度的未上報(bào)統(tǒng)計(jì)、異常體溫篩選、全校上報(bào)趨勢(shì)圖。很多同學(xué)做畢設(shè)時(shí)容易把能登錄、能增刪改查當(dāng)成全部結(jié)果系統(tǒng)做出來(lái)功能點(diǎn)是不少但數(shù)據(jù)之間是孤立的沒(méi)有一條主線(xiàn)把信息串起來(lái)。如果你在開(kāi)題階段就把上面這四個(gè)域理清楚后面無(wú)論是寫(xiě)代碼還是畫(huà)ER圖思路都會(huì)清晰很多。1.2 JSP技術(shù)棧的過(guò)時(shí)與不過(guò)時(shí)每次帶畢設(shè)我都會(huì)被問(wèn)同一個(gè)問(wèn)題老師JSP是不是太老了我是不是該用Spring Boot我的回答通常分兩方面。如果你有半年以上的Java Web基礎(chǔ)用Spring Boot做畢設(shè)當(dāng)然更時(shí)髦簡(jiǎn)歷上也更好看。但問(wèn)題是很多同學(xué)的實(shí)際情況是Java基礎(chǔ)剛過(guò)考試線(xiàn)Maven都還沒(méi)玩熟Spring Boot的自動(dòng)配置和依賴(lài)管理反而成為攔路虎。JSPServletJavaBean這套老組合雖然不夠現(xiàn)代但它把Web開(kāi)發(fā)的底層邏輯暴露得清清楚楚——HTTP請(qǐng)求怎么進(jìn)來(lái)、Servlet如何匹配、請(qǐng)求如何轉(zhuǎn)發(fā)到JSP頁(yè)面、JDBC如何操作數(shù)據(jù)庫(kù)。把這一套走通你對(duì)Java Web的理解反而是扎實(shí)的。再?gòu)漠呍O(shè)驗(yàn)收的角度說(shuō)學(xué)生疫情信息管理系統(tǒng)的核心價(jià)值在業(yè)務(wù)邏輯和數(shù)據(jù)組織不在技術(shù)架構(gòu)。JSP頁(yè)面可以直接內(nèi)嵌Java代碼雖然不推薦寫(xiě)起來(lái)直觀(guān)改起來(lái)也快特別適合需要快速出活的畢業(yè)設(shè)計(jì)節(jié)奏。而且JSP天然支持JavaBean用EL表達(dá)式和JSTL標(biāo)簽就能把頁(yè)面展示做得干凈整潔和Servlet配合非常順手。所以我的結(jié)論很直接如果你的目標(biāo)是順利完成畢設(shè)并真正理解一個(gè)Web系統(tǒng)的運(yùn)轉(zhuǎn)過(guò)程JSPServletMySQL完全夠用而且是一條穩(wěn)妥的路線(xiàn)。2. 從數(shù)據(jù)表設(shè)計(jì)到用戶(hù)權(quán)限先把系統(tǒng)骨架搭扎實(shí)2.1 分表邏輯推演哪些表必須拆哪些字段必須冗余很多同學(xué)建表時(shí)的習(xí)慣是想到哪兒建到哪兒這是大忌。對(duì)于學(xué)生疫情信息管理系統(tǒng)我建議一開(kāi)始就規(guī)劃好6張核心表sys_user用戶(hù)表存放用戶(hù)ID、賬號(hào)、密碼MD5加密存儲(chǔ)、姓名、角色類(lèi)型學(xué)生/輔導(dǎo)員/管理員、關(guān)聯(lián)學(xué)號(hào)或工號(hào)。t_student學(xué)生信息表學(xué)號(hào)、姓名、性別、院系ID、班級(jí)ID、聯(lián)系電話(huà)、家庭所在地。用戶(hù)表和學(xué)生表分開(kāi)是為了以后擴(kuò)展其他角色時(shí)不必改動(dòng)學(xué)生結(jié)構(gòu)。t_department、t_class院系表和班級(jí)表保持兩級(jí)組織結(jié)構(gòu)方便做按院系和班級(jí)的下鉆統(tǒng)計(jì)。t_health_report健康上報(bào)表主鍵ID、學(xué)號(hào)、上報(bào)日期、體溫、當(dāng)前所在地、健康碼狀態(tài)、行程碼截圖路徑、上報(bào)時(shí)間。這里一定要加一個(gè)聯(lián)合唯一索引(student_id, report_date)保證一個(gè)學(xué)生一天只能上報(bào)一條。t_travel_record行程登記表主鍵、學(xué)號(hào)、出發(fā)地、目的地、出發(fā)日期、返回日期、交通方式、審批狀態(tài)、審批意見(jiàn)。有一個(gè)容易忽略的點(diǎn)學(xué)生姓名和班級(jí)名稱(chēng)要不要冗余到健康上報(bào)表里我推薦冗余。雖然有學(xué)號(hào)可以關(guān)聯(lián)查詢(xún)但在高頻查詢(xún)比如按班級(jí)篩選未上報(bào)名單時(shí)多表JOIN的代價(jià)遠(yuǎn)大于冗余字段占用的空間。畢設(shè)系統(tǒng)數(shù)據(jù)量不大性能問(wèn)題不明顯但冗余字段能極大簡(jiǎn)化JSP頁(yè)面的查詢(xún)邏輯屬于以空間換時(shí)間的典型做法。另外一張重要的表是t_health_report里的健康碼狀態(tài)字段。這項(xiàng)數(shù)據(jù)在實(shí)際業(yè)務(wù)里可能有紅、黃、綠三種狀態(tài)設(shè)計(jì)時(shí)不要用VARCHAR隨便存字符串可以用TINYINT存0/1/2然后在Java代碼里做枚舉映射。這樣不僅存儲(chǔ)更緊湊也方便后續(xù)寫(xiě)統(tǒng)計(jì)SQL。2.2 三種角色三條權(quán)限線(xiàn)登錄后各看各的學(xué)生疫情信息管理系統(tǒng)最怕做成所有登錄用戶(hù)看到同一個(gè)菜單。如果輔導(dǎo)員登錄后也能看到管理員的系統(tǒng)設(shè)置頁(yè)面那這個(gè)系統(tǒng)在答辯時(shí)基本是送人頭。所以權(quán)限控制要在一開(kāi)始就規(guī)劃好。建議用最經(jīng)典的攔截器思路自定義一個(gè)LoginFilter在doFilter里檢查session中是否存在登錄用戶(hù)再根據(jù)roleType判斷是否放行。比如/student/*路徑只允許角色為2學(xué)生的賬號(hào)訪(fǎng)問(wèn)/teacher/*路徑只允許角色為1輔導(dǎo)員的賬號(hào)訪(fǎng)問(wèn)/admin/*路徑只允許角色為0管理員的賬號(hào)訪(fǎng)問(wèn)。三種角色的核心操作分別是學(xué)生端每日健康上報(bào)、查看歷史上報(bào)記錄、提交行程登記申請(qǐng)、查看審批結(jié)果。這個(gè)界面要盡量簡(jiǎn)潔一個(gè)表單搞定不要堆砌無(wú)關(guān)信息。輔導(dǎo)員端查看本班學(xué)生上報(bào)情況、查看未上報(bào)名單、審批學(xué)生的行程申請(qǐng)、按日期導(dǎo)出本班健康信息Excel。這里最核心的需求是查漏所以要重點(diǎn)設(shè)計(jì)按日期篩選未上報(bào)列表。管理員端維護(hù)院系班級(jí)信息、管理用戶(hù)賬號(hào)、查看全校上報(bào)統(tǒng)計(jì)圖表、管理所有學(xué)生的上報(bào)記錄。統(tǒng)計(jì)圖表是整個(gè)系統(tǒng)的亮點(diǎn)建議用ECharts展示趨勢(shì)折線(xiàn)和柱狀圖。2.3 健康上報(bào)的狀態(tài)流轉(zhuǎn)這條鏈子怎么串起來(lái)健康上報(bào)本身是簡(jiǎn)單的提交動(dòng)作但如果加上輔導(dǎo)員審核環(huán)節(jié)就需要一套流轉(zhuǎn)邏輯。我建議在t_health_report表中加一個(gè)status字段0表示待審核、1表示正常通過(guò)、2表示異常待復(fù)核。學(xué)生提交后默認(rèn)狀態(tài)為0輔導(dǎo)員在列表中看到待審核記錄判斷體溫是否正常、健康碼狀態(tài)如何然后選擇通過(guò)或標(biāo)記異常。這個(gè)狀態(tài)流轉(zhuǎn)看起來(lái)簡(jiǎn)單但有個(gè)小細(xì)節(jié)很重要學(xué)生端修改當(dāng)天上報(bào)記錄的權(quán)限應(yīng)當(dāng)被限制。比如學(xué)生提交后如果狀態(tài)已經(jīng)是正常通過(guò)那就不能再修改當(dāng)天記錄只能通過(guò)補(bǔ)充說(shuō)明功能留言。如果狀態(tài)還是待審核或異常待復(fù)核則允許在規(guī)定時(shí)間內(nèi)編輯一次。這個(gè)邏輯用代碼實(shí)現(xiàn)也就是一兩個(gè)if判斷但體現(xiàn)了業(yè)務(wù)流程的完整性答辯時(shí)是可以拿出來(lái)講的產(chǎn)品細(xì)節(jié)。3. Servlet / DAO / JSP三層實(shí)戰(zhàn)核心功能與踩坑記錄3.1 從JDBC到DBUtil不要再在Servlet里寫(xiě)SQL了代碼結(jié)構(gòu)問(wèn)題我見(jiàn)過(guò)太多反面教材了。有的同學(xué)把數(shù)據(jù)庫(kù)連接、SQL查詢(xún)、前端跳轉(zhuǎn)全部寫(xiě)在Servlet的doPost里一個(gè)方法幾百行看著很唬人但一旦需求改動(dòng)改一個(gè)地方崩三處。JSP項(xiàng)目的標(biāo)準(zhǔn)做法是三層架構(gòu)Servlet控制層→ Service業(yè)務(wù)邏輯層→ DAO數(shù)據(jù)訪(fǎng)問(wèn)層。JSP只負(fù)責(zé)渲染數(shù)據(jù)。先寫(xiě)一個(gè)標(biāo)準(zhǔn)的數(shù)據(jù)庫(kù)連接工具類(lèi)。這里強(qiáng)烈推薦用c3p0連接池而不是傳統(tǒng)的DriverManager每次新建連接。連接池的好處是復(fù)用數(shù)據(jù)庫(kù)連接資源在高頻提交健康上報(bào)時(shí)不會(huì)因?yàn)轭l繁創(chuàng)建連接導(dǎo)致Tomcat卡頓。配置起來(lái)也不復(fù)雜import com.mchange.v2.c3p0.ComboPooledDataSource; public class DBUtil { private static ComboPooledDataSource dataSource new ComboPooledDataSource(); static { try { dataSource.setDriverClass(com.mysql.jdbc.Driver); dataSource.setJdbcUrl(jdbc:mysql://localhost:3306/epidemic_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai); dataSource.setUser(root); dataSource.setPassword(your_password); dataSource.setInitialPoolSize(5); dataSource.setMaxPoolSize(20); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs ! null) rs.close(); if (ps ! null) ps.close(); if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }這里有個(gè)非常常見(jiàn)的坑MySQL 8.0之后的驅(qū)動(dòng)類(lèi)是com.mysql.cj.jdbc.Driver不是com.mysql.jdbc.Driver。如果你用的是舊驅(qū)動(dòng)類(lèi)啟動(dòng)時(shí)不會(huì)立刻報(bào)錯(cuò)但第一次查詢(xún)數(shù)據(jù)庫(kù)時(shí)會(huì)拋出ClassNotFoundException。另外連接URL一定要配置serverTimezoneAsia/Shanghai否則MySQL 8.0的時(shí)間類(lèi)型處理會(huì)報(bào)錯(cuò)。這兩個(gè)問(wèn)題我在指導(dǎo)畢設(shè)時(shí)幾乎每年都會(huì)遇到。3.2 登錄模塊與密碼加密如何優(yōu)雅地處理Session登錄模塊是每個(gè)管理系統(tǒng)的門(mén)面也是最容易被問(wèn)你這里安全嗎的地方。明文存儲(chǔ)密碼在畢設(shè)里很常見(jiàn)但實(shí)在不該出現(xiàn)在一個(gè)2020年代的系統(tǒng)里。建議使用MD5加鹽的方式處理。public static String md5Encrypt(String input, String salt) { String value input salt; try { MessageDigest md MessageDigest.getInstance(MD5); byte[] bytes md.digest(value.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : bytes) { String hex Integer.toHexString(b 0xff); if (hex.length() 1) sb.append(0); sb.append(hex); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }登錄成功后把用戶(hù)對(duì)象放進(jìn)session然后重定向到對(duì)應(yīng)角色的首頁(yè)。這里有一個(gè)用戶(hù)體驗(yàn)的細(xì)節(jié)登錄頁(yè)面用驗(yàn)證碼。雖然增加了一點(diǎn)實(shí)現(xiàn)成本但能體現(xiàn)完整性也是答辯時(shí)的一個(gè)加分項(xiàng)。驗(yàn)證碼可以用Java的BufferedImage動(dòng)態(tài)生成一個(gè)包含4位數(shù)字的圖片把驗(yàn)證碼字符串存到session中登錄時(shí)比對(duì)即可。Session的管理也有講究。很多同學(xué)在退出登錄時(shí)只寫(xiě)一句session.removeAttribute(user)這是不夠的。正確的做法是session.invalidate()讓整個(gè)會(huì)話(huà)失效同時(shí)FrontController里處理Session超時(shí)的跳轉(zhuǎn)邏輯避免用戶(hù)會(huì)話(huà)過(guò)期后點(diǎn)擊提交報(bào)出空指針異常。3.3 健康上報(bào)流程實(shí)現(xiàn)事務(wù)與并發(fā)約束是核心考驗(yàn)健康上報(bào)是整個(gè)系統(tǒng)里最重要的功能也是容易出問(wèn)題的地方。在t_health_report表中我們?cè)O(shè)置了(student_id, report_date)的聯(lián)合唯一索引。如果學(xué)生瘋狂點(diǎn)擊提交按鈕第二個(gè)請(qǐng)求會(huì)被數(shù)據(jù)庫(kù)拒絕并拋出DuplicateKeyException。代碼層面要在Service層做攔截public int submitReport(HealthReport report) throws Exception { Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); String checkSql SELECT COUNT(*) FROM t_health_report WHERE student_id? AND report_date?; ps conn.prepareStatement(checkSql); ps.setInt(1, report.getStudentId()); ps.setDate(2, report.getReportDate()); rs ps.executeQuery(); if (rs.next() rs.getInt(1) 0) { throw new BusinessException(今日已上報(bào)無(wú)需重復(fù)提交); } rs.close(); ps.close(); String insertSql INSERT INTO t_health_report(student_id, report_date, temperature, location, health_code, travel_code_img, status, create_time) VALUES(?,?,?,?,?,?,?,NOW()); ps conn.prepareStatement(insertSql); ps.setInt(1, report.getStudentId()); ps.setDate(2, report.getReportDate()); ps.setBigDecimal(3, report.getTemperature()); ps.setString(4, report.getLocation()); ps.setInt(5, report.getHealthCode()); ps.setString(6, report.getTravelCodeImg()); ps.setInt(7, 0); ps.executeUpdate(); conn.commit(); return 1; } catch (Exception e) { if (conn ! null) conn.rollback(); throw e; } finally { DBUtil.close(conn, ps, rs); } }注意這個(gè)事務(wù)控制。上報(bào)操作涉及先查后插兩步之間如果不在同一事務(wù)里就可能出現(xiàn)并發(fā)下的重復(fù)插入。雖然聯(lián)合唯一索引兜底了但加上事務(wù)后流程才嚴(yán)謹(jǐn)這也是答辯時(shí)導(dǎo)師會(huì)關(guān)注的點(diǎn)。3.4 文件上傳與ECharts圖表讓頁(yè)面豐富起來(lái)行程碼截圖上傳是很多同學(xué)到后期才補(bǔ)的功能做得比較隨意。JSP里實(shí)現(xiàn)文件上傳最穩(wěn)妥的方式是用Apache的Commons FileUpload組件。需要引入commons-fileupload-1.4.jar和commons-io-2.11.0.jar兩個(gè)依賴(lài)然后在Servlet中解析MultipartRequest。文件存儲(chǔ)路徑有個(gè)細(xì)節(jié)不要把文件存在Web應(yīng)用根目錄下的一個(gè)固定文件夾里否則項(xiàng)目重新部署時(shí)文件會(huì)丟。建議存放在Tomcat的外部路徑比如D:/upload/數(shù)據(jù)庫(kù)里保存的是相對(duì)路徑或者文件名頁(yè)面展示時(shí)通過(guò)一個(gè)專(zhuān)門(mén)的Servlet流式讀取返回。這樣Demo期間即使清了Tomcat緩存圖片也還在。統(tǒng)計(jì)圖表的實(shí)現(xiàn)強(qiáng)烈推薦用ECharts。它不用你寫(xiě)任何后端圖表代碼只要在JSP頁(yè)面里引入ECharts的JS庫(kù)然后通過(guò)Ajax請(qǐng)求后端接口返回JSON數(shù)據(jù)再渲染到div容器中。后端返回JSON可以用Gson庫(kù)非常方便。script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script div idtrendChart stylewidth: 100%;height:400px;/div script var chart echarts.init(document.getElementById(trendChart)); $.getJSON(admin/reportTrend.do, function(data) { chart.setOption({ title: { text: 近14天全校上報(bào)人數(shù)趨勢(shì) }, xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [{ type: line, data: data.counts, smooth: true }] }); }); /script這塊的難度在于寫(xiě)統(tǒng)計(jì)SQL。比如近14天每天上報(bào)人數(shù)對(duì)應(yīng)的SQL是SELECT report_date, COUNT(*) FROM t_health_report WHERE report_date DATE_SUB(CURDATE(), INTERVAL 13 DAY) GROUP BY report_date;這里要注意沒(méi)有上報(bào)的那幾天這條SQL查出來(lái)是沒(méi)有結(jié)果行的需要后端的Java代碼補(bǔ)0否則ECharts的坐標(biāo)軸會(huì)缺數(shù)據(jù)。這個(gè)補(bǔ)零的細(xì)節(jié)很典型平時(shí)不寫(xiě)統(tǒng)計(jì)功能的人第一次處理時(shí)一定會(huì)漏。4. 組裝JSP頁(yè)面從前端布局到后臺(tái)路徑管理4.1 用include把頁(yè)面骨架統(tǒng)一起來(lái)很多同學(xué)的JSP頁(yè)面是CtrlC、CtrlV出來(lái)的一個(gè)項(xiàng)目里幾十個(gè)頁(yè)面每個(gè)頁(yè)面都有重復(fù)的導(dǎo)航欄和側(cè)邊欄。不僅代碼冗余后期想改菜單標(biāo)題得改幾十個(gè)文件。這里推薦用jsp:include或者JSTL的c:import做公共布局。以輔導(dǎo)員端為例可以把頁(yè)面拆成三個(gè)部分header.jsp頂部導(dǎo)航、menu.jsp側(cè)邊欄菜單、footer.jsp頁(yè)腳每個(gè)業(yè)務(wù)頁(yè)面只需要寫(xiě)主體內(nèi)容。% page contentTypetext/html;charsetUTF-8 languagejava % html head title班級(jí)上報(bào)情況/title jsp:include pagecommon/header.jsp/ /head body jsp:include pagecommon/menu.jsp/ div classmain-content h2本班今日上報(bào)情況/h2 table classtable table-bordered thead trth學(xué)號(hào)/thth姓名/thth體溫/thth所在地/thth健康狀態(tài)/thth上報(bào)時(shí)間/th/tr /thead tbody c:forEach items${reportList} varitem tr td${item.studentNo}/td td${item.studentName}/td td${item.temperature}/td td${item.location}/td td${item.healthCodeName}/td td${item.createTime}/td /tr /c:forEach /tbody /table /div jsp:include pagecommon/footer.jsp/ /body /html這里要多說(shuō)一句JSP頁(yè)面本身的編碼問(wèn)題。項(xiàng)目里所有JSP文件都要在頭部聲明pageEncodingUTF-8同時(shí)Tomcat的server.xml中Connector需要設(shè)置URIEncodingUTF-8。否則你會(huì)遇到中文亂碼的問(wèn)題而且是最難排查的那種——數(shù)據(jù)庫(kù)存的是正常中文頁(yè)面上顯示卻是問(wèn)號(hào)。排查過(guò)程通常要花掉半天時(shí)間其實(shí)源頭就是POST請(qǐng)求的編碼沒(méi)設(shè)置。4.2 Servlet路徑設(shè)計(jì)的取舍約定優(yōu)于配置Servlet的路徑映射看起來(lái)只是個(gè)細(xì)節(jié)但做好了能省很多事。建議采用REST風(fēng)格比如LoginServlet映射到/login.doLogoutServlet映射到/logout.doStudentReportServlet映射到/student/report.doTeacherAuditServlet映射到/teacher/audit.doAdminStatisticServlet映射到/admin/reportTrend.do這么做的好處是路徑本身就是功能模塊的語(yǔ)義。前端JSP里寫(xiě)表單的action時(shí)一眼就能看出這個(gè)請(qǐng)求屬于哪個(gè)角色、干什么用的。用了REST風(fēng)格后還可以給每個(gè)角色一個(gè)獨(dú)立的前綴目錄再和權(quán)限攔截器的路徑判斷配合起來(lái)非常絲滑。我還建議在項(xiàng)目中使用一個(gè)BaseServlet作為基類(lèi)。它繼承HttpServlet重寫(xiě)doGet和doPost統(tǒng)一解決請(qǐng)求編碼、登錄狀態(tài)校驗(yàn)和異常處理三個(gè)問(wèn)題。業(yè)務(wù)Servlet再繼承這個(gè)BaseServlet每個(gè)業(yè)務(wù)方法只需要關(guān)注自己的邏輯。這是非常典型的經(jīng)驗(yàn)?zāi)J揭彩谴疝q時(shí)展示工程能力的加分項(xiàng)。4.3 前端控件的細(xì)節(jié)優(yōu)化讓Demo顯得更完整疫情影響系統(tǒng)的前端不需要炫技但要有意識(shí)地做幾件小事第一個(gè)是日期控件默認(rèn)值。健康上報(bào)表單里的日期默認(rèn)應(yīng)該是今天不需要用戶(hù)手動(dòng)選擇。用input typedate value${today}即可后端在Model中傳入今天的日期字符串。第二個(gè)是表格的分頁(yè)。雖然畢設(shè)系統(tǒng)數(shù)據(jù)量小但上報(bào)歷史記錄這種表會(huì)隨著時(shí)間累積變長(zhǎng)不做分頁(yè)就顯得不專(zhuān)業(yè)??梢杂肞ageBean類(lèi)封裝頁(yè)碼、總記錄數(shù)、每頁(yè)大小和數(shù)據(jù)列表DAO層用LIMIT ?, ?配合預(yù)編譯實(shí)現(xiàn)JSP頁(yè)面用c:forEach渲染頁(yè)碼按鈕。第三個(gè)是表單校驗(yàn)。不要只依賴(lài)JavaScript彈窗提示后端Servlet里也要做非空和格式校驗(yàn)。體溫如果超過(guò)42度就拒絕寫(xiě)入這也是一個(gè)簡(jiǎn)單的業(yè)務(wù)規(guī)則幾個(gè)if就能搞定。5. 答辯前一晚功能自查清單與演示腳本5.1 完整走一遍核心流程別在臺(tái)上翻車(chē)一個(gè)常見(jiàn)的答辯翻車(chē)場(chǎng)景是學(xué)生現(xiàn)場(chǎng)演示上報(bào)流程結(jié)果發(fā)現(xiàn)報(bào)了十幾條數(shù)據(jù)后輔導(dǎo)員端列表里一條都沒(méi)顯示。原因多半是學(xué)號(hào)關(guān)聯(lián)查詢(xún)寫(xiě)錯(cuò)了或者新增的學(xué)生沒(méi)有被分配班級(jí)。所以我一直強(qiáng)調(diào)答辯前至少完整走三遍核心流程。建議你的演示腳本按下面這個(gè)順序走用一個(gè)學(xué)生賬號(hào)登錄提交一條健康上報(bào)體溫36.5所在地填一個(gè)正常城市健康碼狀態(tài)選綠碼。切到輔導(dǎo)員賬號(hào)登錄查看今日上報(bào)列表確認(rèn)剛才那條記錄出現(xiàn)。輔導(dǎo)員對(duì)這條記錄執(zhí)行審核通過(guò)。切回學(xué)生賬號(hào)查看歷史記錄確認(rèn)狀態(tài)變?yōu)檎!T谳o導(dǎo)員端點(diǎn)按班級(jí)統(tǒng)計(jì)確認(rèn)上報(bào)率圖表更新。切成管理員賬號(hào)查看全校近14天趨勢(shì)圖切到院系列表。這套腳本覆蓋了所有角色和核心數(shù)據(jù)流。只要這六個(gè)環(huán)節(jié)都能走通你的系統(tǒng)在功能層面已經(jīng)能打過(guò)大部分畢業(yè)設(shè)計(jì)了。5.2 準(zhǔn)備幾個(gè)主動(dòng)回答的技術(shù)亮點(diǎn)答辯時(shí)不是等老師問(wèn)你要主動(dòng)把亮點(diǎn)講出來(lái)。我建議你在最后準(zhǔn)備好三個(gè)可以主動(dòng)展示的點(diǎn)亮點(diǎn)一事務(wù)控制與防重復(fù)提交。說(shuō)明你在上報(bào)模塊用到了事務(wù)和聯(lián)合唯一索引的雙重保護(hù)在處理重復(fù)點(diǎn)擊時(shí)不會(huì)產(chǎn)生臟數(shù)據(jù)。亮點(diǎn)二分層設(shè)計(jì)與可維護(hù)性。直接展示你的項(xiàng)目目錄結(jié)構(gòu)說(shuō)明基于三層架構(gòu)業(yè)務(wù)代碼和頁(yè)面展示分離后期擴(kuò)展角色和功能不需要大規(guī)模重構(gòu)。亮點(diǎn)三密碼加密與權(quán)限控制。告訴評(píng)委登錄密碼使用了加鹽MD5并且通過(guò)Filter實(shí)現(xiàn)了基于角色的URL訪(fǎng)問(wèn)控制未授權(quán)用戶(hù)直接繞過(guò)路徑會(huì)被攔截。5.3 老師們愛(ài)問(wèn)的問(wèn)題提前想好答案我在答辯現(xiàn)場(chǎng)見(jiàn)過(guò)的老師問(wèn)題高度集中在下面幾個(gè)方向。建議每道題都準(zhǔn)備一口頭答案你的系統(tǒng)如何保證一個(gè)學(xué)生不會(huì)重復(fù)上報(bào)答聯(lián)合唯一索引加業(yè)務(wù)判斷在事務(wù)里先查詢(xún)當(dāng)日記錄再插入體溫異常的數(shù)據(jù)你是怎么處理的答體溫超過(guò)閾值時(shí)狀態(tài)自動(dòng)標(biāo)為異常待復(fù)核并推送給輔導(dǎo)員審核如果你變成千級(jí)并發(fā)用戶(hù)JSP這套方案會(huì)遇到什么瓶頸答數(shù)據(jù)庫(kù)連接池限制、Session在分布式下不共享、Servlet容器單機(jī)吞吐量有限可以橫向擴(kuò)展或改造SSM/Spring Boot你數(shù)據(jù)庫(kù)里用什么字段存日期讀取時(shí)候時(shí)區(qū)問(wèn)題怎么處理答用DATETIMEURL配置serverTimezone統(tǒng)一使用Asia/Shanghai時(shí)區(qū)最后這個(gè)問(wèn)題很容易被問(wèn)到因?yàn)楹芏嗤瑢W(xué)在這個(gè)細(xì)節(jié)上栽過(guò)跟頭。能主動(dòng)說(shuō)出時(shí)區(qū)配置的處理方式比你背十個(gè)名詞解釋都管用。收尾一些副產(chǎn)物資料每次做完這類(lèi)項(xiàng)目我都會(huì)順手把下面這幾樣?xùn)|西整理成獨(dú)立文檔答辯后直接裝進(jìn)畢設(shè)歸檔文件夾里SQL腳本.sql包含建庫(kù)、建表、插入初始賬號(hào)的完整SQL語(yǔ)句。方便評(píng)委機(jī)器上直接建環(huán)境。README.md寫(xiě)清楚環(huán)境要求JDK版本、Tomcat版本、MySQL版本、部署步驟、初始賬號(hào)密碼。這個(gè)文檔的價(jià)值在答辯當(dāng)天體現(xiàn)得淋漓盡致——評(píng)委換成自己的電腦時(shí)一份好的README能省大量解釋時(shí)間。操作說(shuō)明.doc配好安裝截圖的用戶(hù)手冊(cè)給不使用軟件系統(tǒng)的人看足以證明工程能力有條理。這些副產(chǎn)物不需要花太多時(shí)間但它們能讓你的畢設(shè)從能跑的程序變成完整的項(xiàng)目交付物。評(píng)委看的不只是功能還有一位畢業(yè)生是否具備了基本的軟件工程交付意識(shí)——說(shuō)實(shí)在話(huà)很多職場(chǎng)新人連這點(diǎn)都做不好。如果時(shí)間允許把這三樣?xùn)|西準(zhǔn)備好你的JSP學(xué)生疫情信息管理系統(tǒng)就不僅僅是一份合格畢業(yè)設(shè)計(jì)還是一件能拿出去給面試官看的真實(shí)作品了。