管理系統(tǒng)畢設(shè):從選課并發(fā)到權(quán)限設(shè)計(jì)全解析)
又到一年畢業(yè)設(shè)計(jì)季Java方向里“基于Spring Boot的教務(wù)管理系統(tǒng)”這類題目幾乎是每年都會(huì)出現(xiàn)的熱門選題。作為去年完整帶完一個(gè)類似課題的過來人我可以負(fù)責(zé)任地告訴你這題選得穩(wěn)但能不能順利答辯過關(guān)取決于你做系統(tǒng)時(shí)有沒有把權(quán)限、事務(wù)、并發(fā)這幾個(gè)關(guān)鍵問題想清楚。這篇文章不打算貼一整段代碼讓你抄那樣沒意義。我要拆的是這一類項(xiàng)目從0到1的完整思考鏈路為什么選這個(gè)課題、技術(shù)棧怎么定、數(shù)據(jù)庫(kù)怎么建模、核心業(yè)務(wù)邏輯怎么落、前后端聯(lián)調(diào)時(shí)踩過哪些坑。你照著這個(gè)思路自己重寫一遍不僅能應(yīng)付畢業(yè)設(shè)計(jì)還能在論文的“技術(shù)難點(diǎn)”章節(jié)寫出真正有深度的內(nèi)容而不是湊字?jǐn)?shù)的廢話。1. 課題定位與技術(shù)選型為什么教務(wù)管理系統(tǒng)是Java畢設(shè)的“安全牌”1.1 教務(wù)場(chǎng)景里到底藏著哪些真實(shí)痛點(diǎn)先說課題本身的業(yè)務(wù)價(jià)值。如果你去問任何一所高校的教務(wù)老師他們?nèi)粘9ぷ鞯恼鎸?shí)狀態(tài)一定包含這些場(chǎng)景課程安排靠Excel表格流轉(zhuǎn)學(xué)生選課在某個(gè)舊系統(tǒng)里卡到崩潰成績(jī)登記完了又因?yàn)楦袷讲唤y(tǒng)一要反復(fù)返工。教務(wù)管理系統(tǒng)要解決的就是把這些分散的業(yè)務(wù)集中到一套可操作的Web系統(tǒng)里教務(wù)管理員維護(hù)課程數(shù)據(jù)、發(fā)布開課計(jì)劃學(xué)生在線選課退課教師錄入成績(jī)學(xué)生再查詢成績(jī)。這個(gè)業(yè)務(wù)模型太適合做成畢業(yè)設(shè)計(jì)了它不是一個(gè)“玩具項(xiàng)目”而是有真實(shí)業(yè)務(wù)約束的完整系統(tǒng)。你要處理的核心難點(diǎn)在選課和成績(jī)兩個(gè)環(huán)節(jié)選課要控制課程容量避免超選成績(jī)要對(duì)不同角色做權(quán)限隔離教師不能改別人的課程成績(jī)。這些約束天然地引導(dǎo)你去思考事務(wù)、并發(fā)、權(quán)限校驗(yàn)而這些正是答辯時(shí)最容易問倒人的地方。1.2 技術(shù)棧怎么選才能既省事又不掉價(jià)Spring Boot作為主框架在Java畢業(yè)設(shè)計(jì)里是絕對(duì)的主流選擇原因很直白它把傳統(tǒng)SSHSpring MVC Spring Hibernate那一大堆XML配置全部簡(jiǎn)化成了自動(dòng)配置和注解你從零搭一個(gè)Web項(xiàng)目到跑起來可能只需要幾分鐘內(nèi)嵌的Tomcat也讓你不用額外去裝服務(wù)器環(huán)境。我比較推薦的技術(shù)棧組合是Spring Boot MyBatis Plus MySQL前端可以選Thymeleaf服務(wù)端渲染也可以做成前后端分離的Vue Element UI。這里有個(gè)權(quán)衡Thymeleaf適合想要快速跑通全流程、不想處理跨域和前端工程化的同學(xué)所有頁(yè)面由后端渲染部署時(shí)打成Jar包就行。Vue 前后端分離適合想在論文里多寫一章“前后端分離架構(gòu)設(shè)計(jì)”的同學(xué)但相應(yīng)地要處理跨域、Token鑒權(quán)、前端打包這些問題。數(shù)據(jù)庫(kù)用MySQL就夠了別為了顯得高級(jí)去碰PostgreSQL或MongoDB除非你的選題已經(jīng)明確要求。MyBatis Plus在日常CRUD上能省大量SQL編寫時(shí)間學(xué)習(xí)成本也低。1.3 功能范圍怎么定才能做到“麻雀雖小五臟俱全”我見過不少同學(xué)設(shè)計(jì)的教務(wù)管理系統(tǒng)功能表比企業(yè)ERP還復(fù)雜排課、調(diào)課、教室申請(qǐng)、畢業(yè)審核全塞進(jìn)去結(jié)果做了三個(gè)月連選課功能都還沒跑通。畢業(yè)設(shè)計(jì)不是商業(yè)軟件功能范圍必須收斂。合理的范圍建議聚焦三大角色、四條核心鏈路管理員用戶管理、課程管理、開課管理、選課截止時(shí)間設(shè)置教師查看授課列表、錄入成績(jī)、查看所授課程選課名單學(xué)生選課退課、查看已選課程、查詢成績(jī)、查看公告這四條鏈路是管理員維護(hù)課程并發(fā)布開課、學(xué)生選課退課、教師錄入成績(jī)、學(xué)生查詢成績(jī)。你看每條鏈路都完整覆蓋了一個(gè)業(yè)務(wù)閉環(huán)系統(tǒng)看起來不臃腫論文里能展示的截圖和用例又足夠多。2. 數(shù)據(jù)庫(kù)建模與權(quán)限設(shè)計(jì)項(xiàng)目上限由這一部分決定2.1 用戶角色表直接寫死字段還是引入RBAC用戶與角色的關(guān)系處理是教務(wù)管理系統(tǒng)里第一個(gè)暴露設(shè)計(jì)水平的地方。最基礎(chǔ)的做法是用戶表里加一個(gè)role字段值就是“admin”“teacher”“student”這種字符串用枚舉去判斷當(dāng)前用戶的角色權(quán)限。這個(gè)方案在功能實(shí)現(xiàn)上沒有任何問題代碼寫起來也快。但如果你想讓論文更有技術(shù)含量建議改成簡(jiǎn)單的RBAC模型用戶表(user)、角色表(role)、用戶角色關(guān)聯(lián)表(user_role)。雖然管理員的角色基本固定但RBAC的好處在于以后擴(kuò)展班主任、輔導(dǎo)員、教務(wù)秘書這些角色時(shí)不需要改表結(jié)構(gòu)只需往角色表里加記錄。更重要的是這個(gè)設(shè)計(jì)能在論文的“系統(tǒng)設(shè)計(jì)”章節(jié)畫出三張標(biāo)準(zhǔn)的表結(jié)構(gòu)圖顯得你的系統(tǒng)有擴(kuò)展性而不是一錘子買賣。學(xué)生和教師的信息建議不要堆在用戶表里。用戶表只存用戶名、密碼、昵稱、角色這些登錄相關(guān)的字段教師信息表(teacher)和學(xué)籍信息表(student)通過user_id和用戶表關(guān)聯(lián)。這樣做的好處是表結(jié)構(gòu)語(yǔ)義清晰教師有職稱、所屬教研室學(xué)生有學(xué)號(hào)、入學(xué)年份、專業(yè)如果全塞進(jìn)用戶表后期查詢和維護(hù)都會(huì)很別扭。2.2 核心業(yè)務(wù)表結(jié)構(gòu)拆解與關(guān)聯(lián)關(guān)系教務(wù)系統(tǒng)的核心業(yè)務(wù)表我建議至少要設(shè)計(jì)出以下這五張第一張是課程表(course)字段包括課程編碼、課程名稱、學(xué)分、學(xué)時(shí)、課程簡(jiǎn)介。課程是“基礎(chǔ)資料”類似于商品庫(kù)里的商品主數(shù)據(jù)。第二張是開課表(course_open)這是很容易被忽略但是最關(guān)鍵的一張表。它記錄的是“本學(xué)期某老師在某時(shí)間開設(shè)了某門課”字段包含開課學(xué)期、授課教師ID、上課時(shí)間地點(diǎn)、選課容量、已選人數(shù)、狀態(tài)。為什么需要一個(gè)開課表因?yàn)橥婚T課程在不同學(xué)期可能由不同老師開設(shè)、容量也不一樣如果把授課教師和上課時(shí)間直接放課程表就沒法表達(dá)這種動(dòng)態(tài)關(guān)系。第三張是選課表(course_selection)字段包括選課ID、學(xué)生ID、開課ID、選課時(shí)間。我特別提醒這張表一定要加聯(lián)合唯一約束索引建立在(student_id, course_open_id)上否則并發(fā)環(huán)境下一定會(huì)出現(xiàn)重復(fù)選課的數(shù)據(jù)臟記錄。這是我在實(shí)際項(xiàng)目中踩過的坑后面單獨(dú)講。第四張是成績(jī)表(score)字段包括成績(jī)ID、學(xué)生ID、開課ID、成績(jī)值、錄入時(shí)間、備注。成績(jī)表跟選課表是什么關(guān)系一般建議成績(jī)表單獨(dú)存在而不是直接在選課表上加一個(gè)成績(jī)字段。雖然有些系統(tǒng)會(huì)直接復(fù)用選課記錄但獨(dú)立的成績(jī)表更利于教師錄入、學(xué)生查詢、管理員統(tǒng)計(jì)成績(jī)分布這些操作。第五張是公告表(notice)用于管理員發(fā)布通知。2.3 選課表聯(lián)合唯一約束一次并發(fā)問題的前車之鑒這里說一個(gè)真實(shí)項(xiàng)目里的教訓(xùn)。某同學(xué)做選課功能時(shí)選課表的定義只把主鍵ID設(shè)成自增沒有加任何唯一約束然后寫了一個(gè)“先判斷是否已選過再插入新記錄”的邏輯。單用戶測(cè)試完全正常一旦幾十個(gè)學(xué)生同時(shí)點(diǎn)擊選課數(shù)據(jù)庫(kù)中就會(huì)出現(xiàn)同一個(gè)人選了同一門課兩次的數(shù)據(jù)。原因并不復(fù)雜兩個(gè)請(qǐng)求同時(shí)通過了“是否已選過”的判斷然后都執(zhí)行了插入因?yàn)閿?shù)據(jù)庫(kù)層面沒有任何約束阻止重復(fù)數(shù)據(jù)寫入。解決辦法分兩層表結(jié)構(gòu)層加上(student_id, course_open_id)的聯(lián)合唯一約束這是最后一道防線代碼邏輯層選課前再加一次查詢校驗(yàn)兩道防線同時(shí)存在才能叫可靠。我們?cè)谧瞿M項(xiàng)目X時(shí)把這個(gè)坑寫進(jìn)了缺陷報(bào)告后來每次設(shè)計(jì)涉及用戶與業(yè)務(wù)數(shù)據(jù)關(guān)聯(lián)的表我都會(huì)第一時(shí)間問自己這個(gè)關(guān)聯(lián)關(guān)系在數(shù)據(jù)庫(kù)層面用什么約束來保護(hù)3. 后端核心實(shí)現(xiàn)把關(guān)鍵業(yè)務(wù)邏輯寫扎實(shí)3.1 項(xiàng)目分層與代碼結(jié)構(gòu)Spring Boot項(xiàng)目的包結(jié)構(gòu)建議按照功能模塊而不是技術(shù)類型來劃分。很多教材喜歡用comon、mapper、service、controller這種方式按層分包功能簡(jiǎn)單時(shí)沒問題但一旦功能多了代碼會(huì)堆得很亂。我習(xí)慣的方式是主包下面先按業(yè)務(wù)模塊分比如system模塊用戶、角色、course模塊課程、開課、selection模塊選課、score模塊成績(jī)每個(gè)模塊內(nèi)部再放controller、service、mapper、entity、dto這些子包。這樣的結(jié)構(gòu)在做演示或后期擴(kuò)展時(shí)能快速定位“選課相關(guān)的所有代碼都在selection包里”不用在幾十個(gè)controller文件里翻來翻去。還有一個(gè)在畢設(shè)論文里加分的小點(diǎn)寫一個(gè)統(tǒng)一的Result返回類和全局異常處理器。統(tǒng)一返回類保證后端返回結(jié)構(gòu)一致類似{ code: 200, message: 操作成功, data: ... }全局異常處理器用RestControllerAdvice加ExceptionHandler處理業(yè)務(wù)異常、參數(shù)校驗(yàn)異常、運(yùn)行時(shí)異常這樣業(yè)務(wù)代碼里只需要throw一個(gè)自定義的BusinessException不用到處try-catch。答辯時(shí)如果老師問“你的系統(tǒng)怎么處理異?!蹦憧梢灾苯诱f出這套機(jī)制的設(shè)計(jì)理由。3.2 登錄鑒權(quán)與權(quán)限控制登錄鑒權(quán)這個(gè)問題答辯老師幾乎必問。常用方案有兩種Session方案和JWT方案。如果前端是Thymeleaf用Session方案最省事登錄成功后把用戶信息放進(jìn)Session攔截器里判斷當(dāng)前請(qǐng)求路徑是否需要登錄、當(dāng)前用戶角色是否能訪問。這個(gè)方案不需要引入額外依賴?yán)斫獬杀镜瓦m合畢設(shè)。如果前端是Vue這類分離項(xiàng)目建議用JWT登錄成功后后端簽發(fā)一個(gè)Token前端存在瀏覽器本地存儲(chǔ)每次請(qǐng)求在Header里加上Token后端通過攔截器解析Token確定用戶身份。攔截器里需要處理的邏輯是放行策略。登錄接口、注冊(cè)接口、靜態(tài)資源要放行其他接口統(tǒng)一走鑒權(quán)攔截器。角色權(quán)限的判斷可以在攔截器里根據(jù)請(qǐng)求路徑前綴攔截比如/api/admin/**只有管理員角色能訪問。這個(gè)規(guī)則比較死板但足夠用。如果想要更好看的實(shí)現(xiàn)可以引入自定義注解加AOP做權(quán)限控制論文里寫出來是個(gè)亮點(diǎn)但實(shí)現(xiàn)前要考慮自己是否真的掌握AOP原理否則答辯被追問會(huì)露餡。3.3 選課接口事務(wù)與并發(fā)控制的必修課選課接口是整個(gè)教務(wù)管理系統(tǒng)里最值得花時(shí)間設(shè)計(jì)的業(yè)務(wù)邏輯。它的完整流程至少包含四步校驗(yàn)學(xué)生身份確認(rèn)當(dāng)前處于選課時(shí)間范圍內(nèi)。查詢?cè)撻_課記錄判斷當(dāng)前已選人數(shù)是否小于容量。檢查該學(xué)生是否已經(jīng)選過這門課避免重復(fù)選課。插入選課記錄并將開課表的已選人數(shù)加1。仔細(xì)想想這四個(gè)步驟每一步之間都存在并發(fā)隱患。最常見的場(chǎng)景是課程只剩最后一個(gè)名額兩個(gè)學(xué)生同時(shí)操作兩個(gè)請(qǐng)求都讀到“已選人數(shù)容量-1”都認(rèn)為還有名額于是都插入選課記錄最后實(shí)際選課人數(shù)超出容量非常典型的超賣問題。解決這種問題必須在數(shù)據(jù)庫(kù)層面加約束。我的做法是不用先查再更新而是直接執(zhí)行一條帶條件的更新語(yǔ)句UPDATE course_open SET selected_count selected_count 1 WHERE id #{openId} AND selected_count capacity執(zhí)行這條語(yǔ)句后通過受影響行數(shù)判斷是否還有剩余名額如果結(jié)果是0說明課程已經(jīng)滿了直接拋出“課程已選滿”的異常如果結(jié)果是1才繼續(xù)插入選課記錄。為什么這樣有效因?yàn)閁PDATE語(yǔ)句在數(shù)據(jù)庫(kù)行上會(huì)加鎖兩個(gè)并發(fā)請(qǐng)求同時(shí)更新同一行時(shí)只可能有一個(gè)先獲得行鎖后一個(gè)請(qǐng)求看到的是更新后的數(shù)據(jù)從而重新判斷容量條件。這就是數(shù)據(jù)庫(kù)層面的樂觀鎖思想用條件更新代替“查詢?cè)俑隆?。再配上事?wù)整個(gè)選課過程要么全部提交要么全部回滾。在Service方法上加上Transactional注解注意這個(gè)注解不能自己類里調(diào)用自己類的方法否則事務(wù)不會(huì)生效這個(gè)問題后面在常見問題章節(jié)單獨(dú)展開。3.4 教師錄入成績(jī)與權(quán)限校驗(yàn)細(xì)節(jié)成績(jī)錄入的實(shí)現(xiàn)相對(duì)簡(jiǎn)單但權(quán)限校驗(yàn)的細(xì)節(jié)很容易被忽略。錄入成績(jī)接口接收的參數(shù)一般有開課ID、學(xué)生ID、成績(jī)值。問題是接口的調(diào)用者是否真的是這門課的授課教師如果不做校驗(yàn)任何登錄用戶只要猜到開課ID和學(xué)生ID就能往接口里傳參數(shù)改成績(jī)這是嚴(yán)重的安全漏洞。正確的做法是在Service層先根據(jù)當(dāng)前登錄用戶的教師ID查詢授課列表判斷目標(biāo)開課ID是否在列表里不在就直接拋出“無權(quán)操作該課程成績(jī)”的異常。這一點(diǎn)務(wù)必要寫進(jìn)論文的“系統(tǒng)安全設(shè)計(jì)”章節(jié)哪怕只是兩段話也比簡(jiǎn)單說“系統(tǒng)采用Spring Boot框架開發(fā)”能撐內(nèi)容。還有成績(jī)的范圍校驗(yàn)0到100之間的整數(shù)或一位小數(shù)用參數(shù)校驗(yàn)注解或者手動(dòng)判斷都行。這里我用一個(gè)從實(shí)際開發(fā)中總結(jié)的經(jīng)驗(yàn)提醒你修改成績(jī)比錄入成績(jī)更需要謹(jǐn)慎一定要記錄操作日志。成績(jī)表里加上updated_time字段擴(kuò)展一個(gè)score_log表記錄修改前后的值、操作人、操作時(shí)間。雖然畢業(yè)設(shè)計(jì)不需要做得像真實(shí)系統(tǒng)那么重但有個(gè)操作日志功能演示時(shí)可以給答辯老師展示“系統(tǒng)具備數(shù)據(jù)可追溯性”加分效果明顯。4. 前端頁(yè)面與接口聯(lián)調(diào)從接口到可演示頁(yè)面的最后一公里4.1 頁(yè)面規(guī)劃教務(wù)系統(tǒng)要哪些頁(yè)面才夠用前端頁(yè)面規(guī)劃不需要花哨但至少要覆蓋核心業(yè)務(wù)鏈路。按照三個(gè)角色劃分管理員端需要登錄頁(yè)、工作臺(tái)首頁(yè)、用戶管理頁(yè)增刪改查、課程管理頁(yè)、開課管理頁(yè)、公告發(fā)布頁(yè)。教師端需要授課列表頁(yè)、選課學(xué)生名單頁(yè)、成績(jī)錄入頁(yè)按學(xué)生批量錄入或逐條錄入。學(xué)生端需要待選課程列表頁(yè)、我的課表/已選課程頁(yè)、成績(jī)查詢頁(yè)、公告頁(yè)。頁(yè)面交互以表格和表單為主這是后臺(tái)管理系統(tǒng)的典型形態(tài)UI不用追求炫酷干凈整齊即可。如果是前后端分離項(xiàng)目用Element UI的表格、彈窗、表單組件會(huì)非常快在校生對(duì)Element UI也比較熟悉遇到問題社區(qū)資料一搜就有。4.2 接口聯(lián)調(diào)時(shí)的三個(gè)經(jīng)典問題前后端聯(lián)調(diào)的時(shí)候有幾個(gè)問題幾乎每次都會(huì)遇到提前避坑能省出好幾天時(shí)間。第一個(gè)是跨域問題。前端項(xiàng)目跑在8080端口后端跑在8081端口瀏覽器直接請(qǐng)求后端接口會(huì)被攔截。解決辦法是后端寫一個(gè)配置類實(shí)現(xiàn)WebMvcConfigurer接口重寫addCorsMappings方法允許指定前端地址跨域訪問。注意如果用了JWT鑒權(quán)跨域配置里要允許自定義請(qǐng)求頭否則前端沒辦法把Token帶過去。第二個(gè)是時(shí)間格式問題。后端用LocalDateTime作為時(shí)間字段類型時(shí)通過默認(rèn)的JSON序列化前端拿到的是一長(zhǎng)串?dāng)?shù)字?jǐn)?shù)組非常不直觀。解決辦法是在application.yml里統(tǒng)一配置時(shí)間格式化或者給實(shí)體類的時(shí)間字段加JsonFormat注解統(tǒng)一輸出為“yyyy-MM-dd HH:mm:ss”的字符串格式。第三個(gè)是null值問題。后端返回的數(shù)據(jù)里有些字段是null前端表格直接顯示“undefined”這個(gè)問題雖然不至于報(bào)錯(cuò)但看起來很業(yè)余。建議統(tǒng)一Result封裝中集合類型返回空集合而不是null字符串類型返回空字符串或者由前端做兜底展示。4.3 驗(yàn)收演示時(shí)的數(shù)據(jù)準(zhǔn)備技巧很多同學(xué)做畢業(yè)設(shè)計(jì)系統(tǒng)里的數(shù)據(jù)隨意亂填演示時(shí)打開頁(yè)面一片亂碼或內(nèi)容空洞給答辯老師的印象分直接掉一檔。數(shù)據(jù)準(zhǔn)備其實(shí)很簡(jiǎn)單就是要“看起來像一個(gè)真實(shí)系統(tǒng)”。預(yù)置數(shù)據(jù)要做到課程名稱用真實(shí)的課程名高等數(shù)學(xué)、大學(xué)英語(yǔ)、數(shù)據(jù)結(jié)構(gòu)用戶姓名用自然的中文姓名組合成績(jī)分布盡量接近正態(tài)分布不要全是100分也不要全是60分。選課數(shù)據(jù)要體現(xiàn)“選滿”和“有空余”兩種狀態(tài)這樣演示選課功能時(shí)既能展示成功選課也方便觸發(fā)“課程已滿”的提示效果。這個(gè)小技巧在很多實(shí)際項(xiàng)目中驗(yàn)證過花費(fèi)半小時(shí)準(zhǔn)備數(shù)據(jù)答辯演示效果遠(yuǎn)勝于打開全是“test”字符串的系統(tǒng)。5. 常見問題與排查技巧實(shí)測(cè)中的避坑實(shí)錄5.1 列表查詢的N1問題成績(jī)列表頁(yè)是一個(gè)典型場(chǎng)景頁(yè)面要展示成績(jī)記錄每條記錄除了成績(jī)本身還要顯示學(xué)生姓名和課程名稱。如果初學(xué)階段圖省事在Service里循環(huán)查詢成績(jī)列表每遍歷一條記錄就查一次學(xué)生表、課程表這就會(huì)觸發(fā)N1查詢問題。假設(shè)成績(jī)表里有100條記錄至少要額外執(zhí)行200次查詢數(shù)據(jù)庫(kù)壓力大接口響應(yīng)慢頁(yè)面打開卡頓。解決思路很簡(jiǎn)單不要循環(huán)查單表用一次關(guān)聯(lián)查詢把所有數(shù)據(jù)查出來。比如在Mapper里寫一個(gè)自定義SQL把成績(jī)表和學(xué)生表、開課表、課程表關(guān)聯(lián)起來返回一個(gè)帶學(xué)生姓名和課程名稱的視圖對(duì)象。MyBatis Plus提供的結(jié)果映射能支持這種自定義查詢但需要你手寫ResultMap這部分邏輯建議自己動(dòng)手寫徹底理解關(guān)聯(lián)查詢的執(zhí)行過程。5.2 Transactional事務(wù)不生效的隱形坑事務(wù)不生效的幾個(gè)典型場(chǎng)景我在實(shí)際項(xiàng)目中都遇到過。第一種是同類內(nèi)部調(diào)用。比如在CourseService里有一個(gè)public方法選課方法內(nèi)調(diào)用了同一個(gè)類里另一個(gè)Transactional方法后者的事務(wù)不會(huì)生效。原因很簡(jiǎn)單Spring的事務(wù)是通過代理對(duì)象實(shí)現(xiàn)的同類內(nèi)部調(diào)用走的是this對(duì)象而不是代理對(duì)象事務(wù)切面沒法攔截。解決辦法是避免同類調(diào)用把需要事務(wù)的子方法放到另一個(gè)Service類里或者直接在入口方法上加上事務(wù)注解。第二種是異常被catch了。事務(wù)方法執(zhí)行過程中內(nèi)部業(yè)務(wù)代碼如果自己try-catch吞掉了異常事務(wù)就不會(huì)回滾因?yàn)镾pring根本感知不到異常發(fā)生。正確的做法是不要在需要回滾的事務(wù)方法內(nèi)吞掉異?;蛘卟东@后重新拋出RuntimeException。第三種是方法非public。Spring默認(rèn)的基于CGLIB或JDK代理的事務(wù)代理對(duì)非public方法是不會(huì)攔截的所以事務(wù)注解只能加在public方法上。5.3 MyBatis Plus邏輯刪除與自動(dòng)填充的配置細(xì)節(jié)如果用了MyBatis Plus的邏輯刪除功能也就是配置了TableLogic注解需要注意兩點(diǎn)。第一實(shí)體類有了邏輯刪除字段后寫自定義SQL時(shí)如果不加is_deleted條件查出來的數(shù)據(jù)可能包含已經(jīng)標(biāo)記刪除的記錄。雖然MyBatis Plus自帶的queryWrapper會(huì)自動(dòng)附加邏輯刪除條件但手寫SQL時(shí)需要自己記得帶上這個(gè)條件。第二邏輯刪除字段會(huì)影響分頁(yè)查詢的count語(yǔ)句這個(gè)一般MyBatis Plus已經(jīng)處理好了不用太擔(dān)心。自動(dòng)填充功能也很實(shí)用在實(shí)體類創(chuàng)建時(shí)間和更新時(shí)間字段上加TableField(fill FieldFill.INSERT)或FieldFill.INSERT_UPDATE注解然后定義一個(gè)MetaObjectHandler實(shí)現(xiàn)類插入和更新記錄時(shí)自動(dòng)填入當(dāng)前時(shí)間。這樣所有表的時(shí)間字段都不用手動(dòng)維護(hù)代碼整潔度提升明顯。5.4 調(diào)試接口的實(shí)用工具組合隊(duì)伍里做后端調(diào)試一個(gè)趁手的接口測(cè)試工具效率遠(yuǎn)高于在瀏覽器里敲URL。我習(xí)慣用的組合是瀏覽器開發(fā)者工具查看接口請(qǐng)求響應(yīng)另一個(gè)桌面客戶端用來構(gòu)造各類測(cè)試請(qǐng)求。聯(lián)調(diào)階段構(gòu)造POST請(qǐng)求測(cè)試登錄接口、選課接口、成績(jī)錄入接口這些場(chǎng)景用桌面客戶端會(huì)比Postman更輕量操作也更順手。建議有空閑時(shí)把項(xiàng)目里所有接口按模塊整理一遍標(biāo)注清楚請(qǐng)求參數(shù)和返回示例這類接口文檔寫進(jìn)論文前面作為“系統(tǒng)接口設(shè)計(jì)”內(nèi)容非常扎實(shí)。6. 從功能完成到論文答辯最后一步怎么走答辯前建議把系統(tǒng)硬編碼的關(guān)鍵配置項(xiàng)比如選課時(shí)間的開啟和關(guān)閉狀態(tài)、課程容量的默認(rèn)值都做成可以在管理員頁(yè)面修改的配置項(xiàng)。這樣現(xiàn)場(chǎng)演示時(shí)可以直接操作開關(guān)不用臨時(shí)改代碼重新部署演示體驗(yàn)會(huì)流暢很多。系統(tǒng)的用戶密碼存儲(chǔ)要使用加密算法不要在數(shù)據(jù)庫(kù)里存明文密碼這是答辯老師非常喜歡問的一個(gè)安全問題。使用Bcrypt或MD5加鹽都行推薦BCryptSpring Security自帶的支持比較完善。數(shù)據(jù)庫(kù)初始化腳本和預(yù)置數(shù)據(jù)腳本要一起放進(jìn)項(xiàng)目里答辯現(xiàn)場(chǎng)評(píng)委用自己的電腦跑項(xiàng)目時(shí)導(dǎo)入SQL后系統(tǒng)就應(yīng)該能直接運(yùn)行。我在實(shí)際帶項(xiàng)目時(shí)還總結(jié)過一個(gè)規(guī)律答辯時(shí)與其緊張地演示所有功能不如圍繞一條核心鏈路講透從管理員創(chuàng)建開課到學(xué)生選課到教師錄成績(jī)?cè)俚綄W(xué)生查成績(jī)前后端數(shù)據(jù)和狀態(tài)的變化要能對(duì)上。能把這個(gè)閉環(huán)清晰展示出來的同學(xué)答辯分?jǐn)?shù)都不會(huì)低。這條鏈路同時(shí)也是論文里系統(tǒng)測(cè)試章節(jié)的測(cè)試用例設(shè)計(jì)基礎(chǔ)功能測(cè)試、權(quán)限測(cè)試、并發(fā)測(cè)試都能從這里面衍生出來。最后分享一個(gè)個(gè)人體會(huì)做教務(wù)管理系統(tǒng)這類項(xiàng)目技術(shù)上沒有任何一個(gè)點(diǎn)屬于“天頂星難度”難點(diǎn)全在于把大量簡(jiǎn)單的功能流程做得嚴(yán)謹(jǐn)可靠。你認(rèn)真處理了選課并發(fā)、權(quán)限校驗(yàn)、事務(wù)邊界這些細(xì)節(jié)你會(huì)發(fā)現(xiàn)寫論文時(shí)的技術(shù)難點(diǎn)部分根本不用編因?yàn)槊恳粋€(gè)坑都是真實(shí)踩出來的。這些踩坑經(jīng)驗(yàn)才是畢業(yè)設(shè)計(jì)能帶給你的最大收獲。