懲管理系統(tǒng)實(shí)戰(zhàn):權(quán)限設(shè)計(jì)與審批流全解)
這個(gè)項(xiàng)目是我在帶本科畢業(yè)設(shè)計(jì)時(shí)經(jīng)常被學(xué)生拿來(lái)問(wèn)的一款典型Java后端系統(tǒng)學(xué)生德育獎(jiǎng)懲管理系統(tǒng)同時(shí)也叫綜合素質(zhì)測(cè)評(píng)與獎(jiǎng)助管理系統(tǒng)。說(shuō)白了就是把過(guò)去輔導(dǎo)員手動(dòng)記錄德育分、紙質(zhì)審批獎(jiǎng)懲材料、人工排獎(jiǎng)助學(xué)金名單這些事搬到Web端去做成一個(gè)可以量化、可追溯、能導(dǎo)出報(bào)表的管理平臺(tái)。我?guī)н^(guò)不少用SpringBoot做畢設(shè)的學(xué)生這類(lèi)題目的生命力在于它不算難但業(yè)務(wù)鏈條長(zhǎng)涉及角色多非常貼近真實(shí)的高校管理場(chǎng)景。德育分的錄入與申訴、獎(jiǎng)懲事項(xiàng)的審批流、獎(jiǎng)助學(xué)金名額分配與發(fā)放記錄這些功能前后能串到一起做完以后無(wú)論是寫(xiě)論文還是答辯都有大量可展開(kāi)的素材。這篇文章我會(huì)按我實(shí)際帶項(xiàng)目的思路來(lái)拆這個(gè)系統(tǒng)先講需求邊界和角色權(quán)限怎么定再看SpringBoot如何撐起整個(gè)Web端然后是數(shù)據(jù)庫(kù)表設(shè)計(jì)和核心模塊的實(shí)打?qū)嵶龇ㄗ詈蟀褜W(xué)生最容易踩的坑拉出來(lái)說(shuō)一遍。如果你現(xiàn)在正在為畢設(shè)選題發(fā)愁或者已經(jīng)選了類(lèi)似題目但還不知道從哪兒下手這篇應(yīng)該能幫你省不少時(shí)間。1. 內(nèi)容整體設(shè)計(jì)與思路拆解1.1 從業(yè)務(wù)場(chǎng)景反推系統(tǒng)邊界很多人一上來(lái)就打開(kāi)IDE開(kāi)始建表這是最忌諱的。德育獎(jiǎng)懲管理系統(tǒng)聽(tīng)起來(lái)不大但如果你把“德育”和“獎(jiǎng)懲”都往大了做功能能膨脹到讓你寫(xiě)不完。我的建議是先畫(huà)一張業(yè)務(wù)流程圖把參與者、觸發(fā)條件和結(jié)果落點(diǎn)理清楚。以高校的典型場(chǎng)景為例輔導(dǎo)員每個(gè)學(xué)期要給學(xué)生打德育測(cè)評(píng)分基礎(chǔ)分由班級(jí)評(píng)議小組給有日常加分項(xiàng)和扣分項(xiàng)學(xué)生如果做了好人好事或者獲得表彰可以提交加分申請(qǐng)違反校紀(jì)校規(guī)則要由學(xué)工辦發(fā)起扣分或處分流程。獎(jiǎng)助學(xué)金模塊則是在德育總分的基礎(chǔ)上結(jié)合學(xué)業(yè)成績(jī)、家庭經(jīng)濟(jì)情況按名額和排序規(guī)則生成初選名單再由院系審核、公示、最終確定發(fā)放記錄。這些場(chǎng)景能拆出五類(lèi)角色學(xué)生、輔導(dǎo)員、院系管理員學(xué)工辦、校級(jí)管理員學(xué)生處、系統(tǒng)超級(jí)管理員。不要五類(lèi)角色都建獨(dú)立的表去存權(quán)限那樣后期維護(hù)會(huì)非常痛苦用Spring Security RBAC角色-權(quán)限-資源模型就夠了。1.2 為什么選SpringBoot而不是SSH或者Node選擇SpringBoot不是因?yàn)樗傲餍小倍沁@個(gè)項(xiàng)目需要的幾個(gè)能力它恰好最穩(wěn)。第一是權(quán)限控制。Web端要區(qū)分學(xué)生和輔導(dǎo)員的操作邊界Spring Security結(jié)合JWT令牌能實(shí)現(xiàn)無(wú)狀態(tài)認(rèn)證前端每次請(qǐng)求帶token后端通過(guò)注解就能控制接口權(quán)限不需要像Session方案那樣考慮集群共享的問(wèn)題。第二是業(yè)務(wù)復(fù)雜度的支撐。德育量化考核涉及加分項(xiàng)、減分項(xiàng)、指標(biāo)維度每個(gè)維度又有權(quán)重和閾值獎(jiǎng)助學(xué)金要處理名額、金額、公示狀態(tài)這些數(shù)據(jù)關(guān)系用Spring Data JPA或MyBatis-Plus來(lái)操作都相當(dāng)順手。如果你用Node.js寫(xiě)也不是不行但遇到事務(wù)嵌套、批量更新這類(lèi)操作調(diào)試成本會(huì)明顯高一些。第三是生態(tài)和參考資源。畢設(shè)階段你大概率會(huì)遇到BugSpringBoot在社區(qū)里的解決方案是最多的任何報(bào)錯(cuò)只要你復(fù)制到搜索引擎基本都能找到現(xiàn)成的答案。這一點(diǎn)對(duì)時(shí)間緊張的學(xué)生來(lái)說(shuō)價(jià)值比技術(shù)本身更大。1.3 前端選型的現(xiàn)實(shí)考量這個(gè)項(xiàng)目是Web端系統(tǒng)對(duì)于畢設(shè)而言我不建議去搞微服務(wù)或者高并發(fā)架構(gòu)老老實(shí)實(shí)做單體應(yīng)用即可。前端我一般推薦兩種方案一種是服務(wù)端模板引擎Thymeleaf適合不熟悉前端的同學(xué)好處是部署簡(jiǎn)單把項(xiàng)目打成jar包就能跑另一種是前后端分離模式Vue Element UI適合愿意多花些時(shí)間在界面上、想給答辯加分的同學(xué)。我個(gè)人的傾向是如果題目要求里有“前后端分離”或“Web端交互流暢”的字樣就用Vue如果沒(méi)有明確要求優(yōu)先用Thymeleaf減少Node環(huán)境的依賴(lài)也縮短聯(lián)調(diào)時(shí)間。因?yàn)檫@個(gè)項(xiàng)目的核心價(jià)值在管理流程的閉環(huán)而不是頁(yè)面炫酷程度。德育分被記錄在案、審批流能正常流轉(zhuǎn)、獎(jiǎng)助名單能導(dǎo)出Excel這些才是答辯評(píng)委關(guān)心的事。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 德育量化考核的指標(biāo)設(shè)計(jì)德育量化考核看起來(lái)簡(jiǎn)單其實(shí)最容易做含糊。很多學(xué)生把德育分設(shè)計(jì)成“管理員手動(dòng)加或減”這種方案答辯時(shí)會(huì)被問(wèn)住因?yàn)樵u(píng)委必然會(huì)追問(wèn)“加分的依據(jù)是什么權(quán)重怎么定的”正確的做法是把德育指標(biāo)拆成維度表。常見(jiàn)維度包括思想品德、遵紀(jì)守法、集體活動(dòng)、志愿服務(wù)、學(xué)術(shù)競(jìng)賽、宿舍文明等。每個(gè)維度下設(shè)具體條目比如志愿服務(wù)下面可以細(xì)分“校內(nèi)志愿活動(dòng)每小時(shí)0.5分上限5分”“無(wú)償獻(xiàn)血每次2分”等。每條加分/扣分記錄都要關(guān)聯(lián)一個(gè)指標(biāo)條目并且記錄操作人和證明附件。數(shù)據(jù)庫(kù)上我建議就五張核心表moral_score學(xué)生德育總分表保存當(dāng)前有效總分。moral_record加減分明細(xì)表每產(chǎn)生一條記錄就入表附帶指標(biāo)id、分?jǐn)?shù)變化量、狀態(tài)、審批人、證明材料地址。moral_indicator指標(biāo)條目表維護(hù)維度、條目名稱(chēng)、分值、類(lèi)型加分/減分、是否啟用。permission_info學(xué)生某個(gè)學(xué)期的匯總測(cè)評(píng)信息用于生成學(xué)期綜測(cè)報(bào)告。approval_flow審批流實(shí)例表這個(gè)不一定所有畢業(yè)設(shè)計(jì)都做但如果德育分變化需要兩級(jí)審核建議加這張表。2.2 獎(jiǎng)助學(xué)金模塊的狀態(tài)機(jī)設(shè)計(jì)獎(jiǎng)助學(xué)金發(fā)放平臺(tái)是這類(lèi)系統(tǒng)的另一個(gè)重頭。這里最好把“名額配置-申請(qǐng)?zhí)峤?初選評(píng)定-院系審核-學(xué)校審核-名單公示-資金發(fā)放-歸檔”做成一個(gè)明確的狀態(tài)機(jī)。狀態(tài)一般有未開(kāi)始、申請(qǐng)中、初步評(píng)定、院系審核通過(guò)/退回、校級(jí)審核通過(guò)/退回、公示中、異議處理中、發(fā)放完成、已歸檔。每個(gè)狀態(tài)對(duì)應(yīng)可操作的角色例如“院系審核通過(guò)”只能由輔導(dǎo)員執(zhí)行校級(jí)管理員不能越權(quán)直接通過(guò)“初步評(píng)定”。SpringBoot里做狀態(tài)機(jī)不需要引入額外框架你只要在設(shè)計(jì)實(shí)體時(shí)保留一個(gè)status字段然后在Service層寫(xiě)一個(gè)狀態(tài)流轉(zhuǎn)方法規(guī)定當(dāng)前狀態(tài)允許跳轉(zhuǎn)到的目標(biāo)狀態(tài)集合即可。這樣代碼直觀答辯時(shí)也容易講清楚。2.3 權(quán)限設(shè)計(jì)的幾個(gè)坑我在幫學(xué)生檢查代碼時(shí)發(fā)現(xiàn)最典型的問(wèn)題是只做了登錄攔截沒(méi)做接口級(jí)權(quán)限。比如學(xué)生知道/api/moral/update這個(gè)接口后直接繞過(guò)前端去請(qǐng)求就能給自己加分。這是非常嚴(yán)重的安全漏洞。解決方式很簡(jiǎn)單Spring Security的PreAuthorize(hasRole(ADMIN))注解就能實(shí)現(xiàn)。在Controller方法上標(biāo)注所需角色再開(kāi)啟方法級(jí)安全配置。同時(shí)操作記錄一定要寫(xiě)入日志表保留操作人IP方便追溯。另外德育分敏感操作建議加“二次確認(rèn)”機(jī)制。比如扣分超過(guò)5分時(shí)需要填寫(xiě)具體原因和佐證材料否則后端直接拒絕。這不算復(fù)雜邏輯但能讓系統(tǒng)看起來(lái)更規(guī)范也能減少誤操作。3. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 項(xiàng)目初始化與依賴(lài)配置我以一個(gè)真實(shí)的SpringBoot MyBatis-Plus Vue項(xiàng)目為例先把后端骨架搭出來(lái)。用IDEA新建Spring Initializr項(xiàng)目Java版本建議JDK 1.8或11不要追新版本SpringBoot選擇2.7.x即可。2.7版本太低的換成3.x后有些依賴(lài)兼容性會(huì)出問(wèn)題臨到答辯前再踩這個(gè)坑就很冤枉。核心依賴(lài)包括dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency注意SpringBoot 2.7.x對(duì)應(yīng)的MyBatis-Plus要用3.5.x版本不要再沿用3.4的老版本。數(shù)據(jù)庫(kù)連接池直接用HikariCPSpringBoot默認(rèn)已經(jīng)集成不用額外配置。3.2 數(shù)據(jù)庫(kù)表設(shè)計(jì)的落地腳本下面這張數(shù)據(jù)庫(kù)設(shè)計(jì)是我認(rèn)為比較能支撐整個(gè)系統(tǒng)運(yùn)轉(zhuǎn)的核心你可以直接參考再按自己學(xué)校的規(guī)則調(diào)整。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) NOT NULL, role VARCHAR(20) NOT NULL, -- STUDENT / COUNSELOR / COLLEGE_ADMIN / SCHOOL_ADMIN student_no VARCHAR(30), college_id BIGINT, status TINYINT DEFAULT 1 ); CREATE TABLE moral_indicator ( id BIGINT PRIMARY KEY AUTO_INCREMENT, dimension VARCHAR(30) NOT NULL, item_name VARCHAR(100) NOT NULL, score_value DECIMAL(5,2) NOT NULL, score_type TINYINT NOT NULL, -- 1加分 2減分 upper_limit DECIMAL(5,2) DEFAULT 0, enabled TINYINT DEFAULT 1 ); CREATE TABLE moral_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, indicator_id BIGINT NOT NULL, score_change DECIMAL(5,2) NOT NULL, reason VARCHAR(500), attachment_url VARCHAR(200), status TINYINT DEFAULT 0, -- 0待審核 1通過(guò) 2駁回 operator_id BIGINT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, audit_time DATETIME ); CREATE TABLE scholarship_apply ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, project_id BIGINT NOT NULL, moral_score DECIMAL(5,2), academic_score DECIMAL(5,2), apply_reason VARCHAR(2000), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE scholarship_project ( id BIGINT PRIMARY KEY AUTO_INCREMENT, project_name VARCHAR(100) NOT NULL, quota INT NOT NULL, amount DECIMAL(10,2) NOT NULL, apply_start DATETIME, apply_end DATETIME, status TINYINT DEFAULT 0 ); CREATE TABLE scholarship_disbursement ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, project_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, disburse_time DATETIME, bank_card_no VARCHAR(30), status TINYINT DEFAULT 0 -- 0未發(fā)放 1已發(fā)放 );這張?jiān)O(shè)計(jì)做下來(lái)整個(gè)系統(tǒng)的數(shù)據(jù)流動(dòng)已經(jīng)閉環(huán)了。這里有個(gè)關(guān)鍵點(diǎn)moral_score不要單獨(dú)設(shè)計(jì)表直接用SQL執(zhí)行SELECT student_id, SUM(score_change) FROM moral_record WHERE status 1 GROUP BY student_id來(lái)實(shí)時(shí)計(jì)算總成績(jī)放在單獨(dú)的匯總表里定時(shí)任務(wù)刷新即可。這樣能避免人工改總分導(dǎo)致明細(xì)對(duì)不上賬的情況。3.3 登錄認(rèn)證與JWT實(shí)現(xiàn)Spring Security JWT是項(xiàng)目里比較復(fù)雜的一塊我建議不要抄太復(fù)雜的方案。網(wǎng)上有很多擴(kuò)展幾十個(gè)類(lèi)的教程看著很厲害但對(duì)畢設(shè)來(lái)說(shuō)過(guò)于沉重。簡(jiǎn)單做法是登錄接口驗(yàn)證用戶(hù)名密碼成功后用Jwts.builder()生成token把用戶(hù)ID和角色塞進(jìn)claims里返回給前端。前端在后續(xù)請(qǐng)求的請(qǐng)求頭Authorization里帶上Bearer token后端寫(xiě)一個(gè)OncePerRequestFilter解析token把用戶(hù)信息放進(jìn)SecurityContextHolder。核心代碼大概是這個(gè)形態(tài)Component public class JwtAuthenticationFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String header request.getHeader(Authorization); if (header ! null header.startsWith(Bearer )) { String token header.substring(7); try { Claims claims Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); String username claims.get(username, String.class); Long userId claims.get(userId, Long.class); String role claims.get(role, String.class); // 構(gòu)建Authentication對(duì)象并放入上下文 } catch (Exception e) { // token校驗(yàn)失敗不設(shè)置上下文即可 } } chain.doFilter(request, response); } }這里要注意secretKey長(zhǎng)度必須大于等于256位否則JJWT 0.11.x版本會(huì)報(bào)錯(cuò)。我見(jiàn)過(guò)好幾個(gè)學(xué)生在這里卡了一個(gè)下午其實(shí)就是把key改長(zhǎng)一點(diǎn)就行了。3.4 德育加減分的審批流程德育加分不能讓輔導(dǎo)員一鍵直接通過(guò)要通過(guò)兩級(jí)審核否則評(píng)優(yōu)評(píng)先的時(shí)候?qū)W生就有意見(jiàn)。我的實(shí)現(xiàn)方式是學(xué)生提交加分申請(qǐng)→輔導(dǎo)員初審→院系管理員終審。如果終審駁回記錄會(huì)退回給輔導(dǎo)員同時(shí)學(xué)生端能看到駁回原因。在Service層每次狀態(tài)流轉(zhuǎn)都做一次合法性校驗(yàn)比如狀態(tài)是待輔導(dǎo)員審核時(shí)只有輔導(dǎo)員能調(diào)用審核接口狀態(tài)是待院系終審時(shí)院系管理員才能操作。這里不要只依賴(lài)前端隱藏按鈕后端一定要校驗(yàn)當(dāng)前操作者的角色和當(dāng)前記錄狀態(tài)是否匹配。if (!currentRole.equals(COUNSELOR)) { throw new BusinessException(當(dāng)前用戶(hù)無(wú)審核權(quán)限); } if (!record.getStatus().equals(RecordStatus.PENDING_COUNSELOR)) { throw new BusinessException(記錄當(dāng)前狀態(tài)不允許該操作); }3.5 獎(jiǎng)助學(xué)金名額排序算法獎(jiǎng)學(xué)金初選名單是按綜合成績(jī)排序來(lái)生成的。綜合成績(jī)公式通常為綜合成績(jī) 德育成績(jī) × 30% 學(xué)業(yè)成績(jī) × 70%。每個(gè)項(xiàng)目權(quán)重要做成可配置不要寫(xiě)死。有了成績(jī)后按項(xiàng)目?jī)?nèi)所有人從高到低排序取前N名進(jìn)入初選名單。這里要注意一個(gè)很現(xiàn)實(shí)的問(wèn)題如果第N名和第N1名成績(jī)相同怎么辦我的方案是增加排序條件學(xué)業(yè)成績(jī)高的優(yōu)先如果還相同就按照提交申請(qǐng)時(shí)間早的優(yōu)先。這樣程序不會(huì)出現(xiàn)并列分發(fā)名單時(shí)也沒(méi)有爭(zhēng)議。ListStudentScoreVO sortedList scoreList.stream() .sorted(Comparator.comparing(StudentScoreVO::getEligible) .thenComparing(StudentScoreVO::getAcademicScore).reversed() .thenComparing(StudentScoreVO::getApplyTime)) .collect(Collectors.toList()); ListStudentScoreVO selected sortedList.subList(0, Math.min(quota, sortedList.size()));當(dāng)?shù)掠煽?jī)和學(xué)業(yè)成績(jī)都是0時(shí)要排除掉未提交申請(qǐng)的學(xué)生這部分邏輯切記要提前過(guò)濾。3.6 公示與異議處理名單生成后需要進(jìn)入公示狀態(tài)。公示期內(nèi)允許學(xué)生提交異議不直接改名單而是生成一條異議記錄由院系管理員復(fù)核。復(fù)核通過(guò)后被駁回的申請(qǐng)狀態(tài)改回“待審核”并標(biāo)記異議原因。這個(gè)功能雖然不起眼但在答辯中很容易成為亮點(diǎn)因?yàn)樗w現(xiàn)了系統(tǒng)設(shè)計(jì)時(shí)對(duì)實(shí)際業(yè)務(wù)場(chǎng)景的考慮。我做的時(shí)候還加了個(gè)小功能公示期滿(mǎn)后系統(tǒng)自動(dòng)將名單狀態(tài)更新為已生效并給相關(guān)學(xué)生發(fā)站內(nèi)消息。4. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄4.1 SpringBoot啟動(dòng)時(shí)報(bào)端口被占用這類(lèi)問(wèn)題在Windows開(kāi)發(fā)機(jī)上經(jīng)常出現(xiàn)。明明上一個(gè)項(xiàng)目已經(jīng)停掉了但端口還是被占用。先執(zhí)行下面命令查看netstat -ano | findstr 8080后面顯示PID然后去任務(wù)管理器結(jié)束對(duì)應(yīng)進(jìn)程即可。如果不想每次手動(dòng)找就在application.yml里設(shè)置隨機(jī)端口server: port: ${PORT:8080}部署時(shí)指定-DPORT8081就能切換這個(gè)技巧對(duì)后續(xù)把項(xiàng)目放到服務(wù)器上演示非常有用。4.2 MyBatis-Plus分頁(yè)查詢(xún)失效很多學(xué)生寫(xiě)分頁(yè)時(shí)前面配置了PaginationInnerInterceptor但查詢(xún)一頁(yè)返回所有數(shù)據(jù)原因是這個(gè)攔截器要引入最新版本的mybatis-plus-boot-starter并且注入時(shí)指定了數(shù)據(jù)庫(kù)類(lèi)型。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }還有些同學(xué)用的是自定義SQL join查詢(xún)分頁(yè)對(duì)象必須作為第一個(gè)參數(shù)傳進(jìn)去否則不生效。這個(gè)點(diǎn)尤其容易忽視。4.3 前后端聯(lián)調(diào)時(shí)跨域問(wèn)題用Vue開(kāi)發(fā)時(shí)axios請(qǐng)求后端接口會(huì)報(bào)跨域。最簡(jiǎn)單的辦法是在SpringBoot里配置CORS。注意CrossOrigin注解寫(xiě)單個(gè)Controller是能解決但要處理所有Controller我建議寫(xiě)一個(gè)全局配置類(lèi)Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }開(kāi)發(fā)環(huán)境下直接允許所有來(lái)源就行正式部署時(shí)再收緊。4.4 導(dǎo)出Excel亂碼問(wèn)題獎(jiǎng)助名單必須支持導(dǎo)出Excel很多同學(xué)用POI導(dǎo)出后打開(kāi)出現(xiàn)亂碼這是因?yàn)轫憫?yīng)頭沒(méi)設(shè)置或者文件名里包含中文導(dǎo)致的。正確設(shè)置是這樣的response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setCharacterEncoding(UTF-8); String fileName URLEncoder.encode(獎(jiǎng)助名單.xlsx, UTF-8); response.setHeader(Content-Disposition, attachment; filename\ fileName \);文件名不要直接拼中文否則在下載時(shí)就會(huì)被截?cái)喑蓙y碼。4.5 頭像等文件上傳路徑問(wèn)題文件上傳功能很多學(xué)生用絕對(duì)路徑保存真正部署到服務(wù)器后就找不到文件。我建議配置一個(gè)統(tǒng)一的存儲(chǔ)根路徑并通過(guò)一個(gè)/files/**映射來(lái)訪問(wèn)app: file-root: D:/upload/然后寫(xiě)資源映射器Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceLocations(file: appFileRoot); }這樣上傳的證明材料和頭像都能通過(guò)相對(duì)路徑訪問(wèn)換環(huán)境時(shí)只需要修改配置項(xiàng)。5. 這個(gè)系統(tǒng)還可以怎么擴(kuò)展5.1 接入手寫(xiě)簽名或電子簽章德育獎(jiǎng)懲審批流如果希望更貼近真實(shí)辦公場(chǎng)景可以接入手寫(xiě)簽名板輔導(dǎo)員和院系管理員審核時(shí)直接在頁(yè)面上簽名簽名圖片存入審批流表。這樣整個(gè)審批鏈條不僅有時(shí)間記錄還有真實(shí)的簽批形態(tài)打眼一看就比普通CRUD系統(tǒng)高級(jí)一截。5.2 增加綜合測(cè)評(píng)報(bào)告自動(dòng)生成每個(gè)學(xué)期結(jié)束后系統(tǒng)可以按學(xué)生維度生成德育綜合素質(zhì)報(bào)告以PDF或Word格式導(dǎo)出。內(nèi)容包含學(xué)期得分、各項(xiàng)指標(biāo)明細(xì)、獎(jiǎng)懲記錄、排名情況作為評(píng)獎(jiǎng)評(píng)優(yōu)的歸檔材料。這個(gè)功能實(shí)現(xiàn)起來(lái)其實(shí)就是排版加數(shù)據(jù)聚合技術(shù)上不難但工作量能明顯撐起畢設(shè)論文的“系統(tǒng)實(shí)現(xiàn)”章節(jié)。5.3 消息通知與移動(dòng)端適配獎(jiǎng)助學(xué)金申請(qǐng)狀態(tài)變化、德育分審批結(jié)果這些都需要通知學(xué)生。我一般建議加一個(gè)簡(jiǎn)單的站內(nèi)信模塊不要輕易接入短信或微信模板消息因?yàn)槟切枰髽I(yè)資質(zhì)或額外費(fèi)用。做移動(dòng)端適配時(shí)前端使用Vue的話可以用響應(yīng)式布局讓W(xué)eb端在手機(jī)上也能看應(yīng)付日常瀏覽和審核足夠了。6. 寫(xiě)在最后的一些真實(shí)體會(huì)帶過(guò)這么多屆畢設(shè)我發(fā)現(xiàn)學(xué)生做這類(lèi)管理系統(tǒng)最終成績(jī)拉開(kāi)差距的地方往往不在代碼量而在業(yè)務(wù)邏輯的完整性和細(xì)節(jié)考慮。德育獎(jiǎng)懲管理系統(tǒng)的核心不只是一個(gè)增刪改查而是你能不能把“德育分變化可追溯、獎(jiǎng)懲審批有狀態(tài)、獎(jiǎng)助名單有依據(jù)”這三件事講清楚并且用代碼實(shí)現(xiàn)出來(lái)。我自己在指導(dǎo)過(guò)程中有個(gè)習(xí)慣做每個(gè)模塊前先問(wèn)學(xué)生三個(gè)問(wèn)題這個(gè)操作誰(shuí)來(lái)做什么時(shí)候能做做完以后會(huì)影響到哪些數(shù)據(jù)把這三個(gè)問(wèn)題想通了整個(gè)系統(tǒng)的骨架自然就出來(lái)了SpringBoot只是把它落地的工具而已。如果你現(xiàn)在正在寫(xiě)這個(gè)題目我建議你先把數(shù)據(jù)庫(kù)表建好把審批流邏輯跑通再去裝飾頁(yè)面。先把最難啃的骨頭啃掉后面的時(shí)間就比較從容了。如果這個(gè)過(guò)程中遇到具體的報(bào)錯(cuò)歡迎在評(píng)論區(qū)帶上日志和代碼片段我看到后會(huì)幫你分析。做這類(lèi)畢業(yè)設(shè)計(jì)一次成功的完整跑通比反復(fù)推翻重來(lái)要重要得多。