設(shè)計與實現(xiàn)全解析)
每年到畢設(shè)季找我要選題建議的同學(xué)里十有八九會問“有沒有那種功能完整、技術(shù)棧主流、還不太容易翻車的題目”而“師生健康信息管理系統(tǒng)”就是我從頭到尾都很推薦的一類。原因也很簡單這個題目管理的數(shù)據(jù)對象明確、角色分工清晰、業(yè)務(wù)邏輯不復(fù)雜但足夠完整正好能把 SpringBoot、Vue、MySQL 這條最主流的技術(shù)線串起來做出來既能寫進簡歷又能在答辯時講出東西。這篇就結(jié)合我實際帶項目的經(jīng)驗把從環(huán)境搭建、數(shù)據(jù)庫設(shè)計到前后端實現(xiàn)、部署上線的完整鏈路拆開講清楚特別是那些“文檔里不會寫、代碼跑不起來你才著急”的細節(jié)。這套系統(tǒng)能做的事情很直觀管理員維護全校師生賬號校醫(yī)錄入體檢數(shù)據(jù)和健康檔案班主任查看本班學(xué)生的健康狀態(tài)學(xué)生本人可以填報日常健康信息、瀏覽自己的隨訪記錄和健康建議。不同角色看到的內(nèi)容不同核心數(shù)據(jù)圍繞“師生基礎(chǔ)信息—體檢記錄—日常健康監(jiān)測—預(yù)警提醒”展開。適合拿來練手或做畢業(yè)設(shè)計的同學(xué)參考尤其是已經(jīng)學(xué)過 Java 或 Vue 基礎(chǔ)但沒完整做過一個前后端分離項目的這篇的內(nèi)容基本可以幫你把盲區(qū)填上。1. 項目整體設(shè)計與技術(shù)選型思路1.1 為什么是 SpringBoot Vue MySQL而不是其他組合畢設(shè)選題的第一原則我個人覺得不是“越新越好”而是“穩(wěn)”。SpringBoot 2.7.x 加 Vue 2 加 MySQL 8.0 這個組合在目前的資料豐富度、生態(tài)穩(wěn)定性和社區(qū)排障經(jīng)驗上是綜合最優(yōu)的。很多同學(xué)一開始會糾結(jié)“要不要上 SpringBoot 3、Vue 3”我的建議很直接除非你已經(jīng)有足夠經(jīng)驗否則別在畢設(shè)階段給自己加這種不必要的變量。SpringBoot 3 基于 Jakarta EE很多老教程里的依賴坐標(biāo)和寫法直接失效Vue 3 的生態(tài)雖然也成熟但配套的 Element UI 組件庫還停留在 Vue 2Ant Design Vue 的新版用法和網(wǎng)上大量舊教程對不上排查起來非常費勁。后端選 SpringBoot最大優(yōu)勢是“約定大于配置”。不需要像 SSM 時代那樣寫一堆 XML 配置一個啟動類加幾個注解就能把項目跑起來內(nèi)置 Tomcat 也省去了單獨部署 Web 服務(wù)器的麻煩。前端用 Vue核心優(yōu)勢是組件化和響應(yīng)式數(shù)據(jù)綁定頁面邏輯和 UI 分離得清楚像表格、表單、彈窗這些后臺管理系統(tǒng)的高頻場景用組件封裝之后寫起來效率能提升一大截。MySQL 這邊就不用多說了開源免費、安裝簡單、大學(xué)里基本都學(xué)過而且 Workbench 和 Navicat 這類圖形化工具已經(jīng)很成熟建表、導(dǎo)數(shù)據(jù)都方便。1.2 系統(tǒng)功能模塊與角色權(quán)限劃分健康信息管理系統(tǒng)屬于典型的“多角色、同業(yè)務(wù)、不同視角”系統(tǒng)這是它比一般 CRUD 項目高一個檔次的地方。我在設(shè)計功能時把它拆成了四個角色每個角色看到的菜單和操作權(quán)限都做了隔離管理員用戶管理、班級管理、數(shù)據(jù)統(tǒng)計、系統(tǒng)日志、賬號重置。校醫(yī)保健教師體檢記錄錄入、健康檔案查看、異常指標(biāo)預(yù)警、健康建議維護。班主任查看本班學(xué)生列表、日常健康填報審批、班級健康統(tǒng)計。學(xué)生個人信息維護、日常健康情況填報、查看本人體檢記錄和健康建議。1.3 前后端分離的項目結(jié)構(gòu)規(guī)劃前后端分離不是說把文件夾分開就算完事關(guān)鍵是要在開發(fā)階段就把“接口約定”定好。我的做法是后端統(tǒng)一返回 Result 對象格式是{ code: 200, message: 操作成功, data: {...} }前端所有請求都走封裝好的 Axios 實例攔截器統(tǒng)一處理 code 非 200 的情況。這樣前后端可以并行開發(fā)只要接口文檔約定清楚誰也不用等誰。后端按“Controller → Service → Mapper”三層劃分包結(jié)構(gòu)實體類放在 entity 包DTO 放在 dto 包VO 放在 vo 包避免把數(shù)據(jù)庫實體直接返回給前端防止把密碼等敏感字段暴露出去。前端按“頁面級組件 通用組件”組織src/api目錄下面按模塊建文件管理接口src/router里統(tǒng)一配置路由和權(quán)限守衛(wèi)這樣代碼量上來之后不會有“一個文件到處找”的混亂感。2. 數(shù)據(jù)庫設(shè)計與核心表結(jié)構(gòu)2.1 健康管理系統(tǒng)的數(shù)據(jù)模型拆解健康信息管理系統(tǒng)說到底管的是兩類東西一類是“人”另一類是“人的健康數(shù)據(jù)”。圍繞這個概念我把數(shù)據(jù)庫拆成了幾個核心表用戶表、學(xué)生信息表、體檢記錄表、日常健康填報表、健康預(yù)警表。另外還有班級表和健康建議字典表用來支撐業(yè)務(wù)邏輯。先說用戶表。設(shè)計時沒有把學(xué)生信息直接堆在用戶表里而是拆成sys_user賬號密碼、角色類型和student學(xué)號、姓名、性別、出生日期、班級ID、緊急聯(lián)系人兩張表通過 user_id 關(guān)聯(lián)。好處是賬號體系和業(yè)務(wù)身份解耦以后想擴展老師表、管理員表不需要動用戶表的結(jié)構(gòu)。很多同學(xué)圖省事把所有人塞一張表里最后做權(quán)限控制和數(shù)據(jù)擴展的時候會把自己繞暈。2.2 核心表字段設(shè)計細節(jié)學(xué)生表的核心字段大概是這樣的字段名類型說明idbigint主鍵自增user_idbigint關(guān)聯(lián)用戶表student_novarchar(20)學(xué)號唯一索引namevarchar(50)姓名gendertinyint性別 0男 1女birth_datedate出生日期class_idbigint班級IDphonevarchar(20)聯(lián)系電話emergency_contactvarchar(50)緊急聯(lián)系人emergency_phonevarchar(20)緊急聯(lián)系電話create_timedatetime創(chuàng)建時間體檢記錄表的坑主要在“動態(tài)指標(biāo)”上。一次體檢會有身高、體重、肺活量、左右眼視力、血壓、心率等一堆指標(biāo)如果每個指標(biāo)建一列表會越拉越寬而且以后想加指標(biāo)還得分表。但全部用 JSON 存又不利于統(tǒng)計。我采用的方式是“主表存體檢單公共信息 擴展字段存常用指標(biāo)”身高體重這種高頻統(tǒng)計字段單獨建列像營養(yǎng)狀況評價這種偶爾用的字段放一個result字段里存文本這樣既有統(tǒng)計能力又保留靈活性。2.3 建庫建表與初始化數(shù)據(jù)的建議數(shù)據(jù)庫設(shè)計好了之后務(wù)必要寫一份完整的init.sql腳本把建庫、建表、初始賬號、測試數(shù)據(jù)一次性寫好。這樣不僅自己開發(fā)的時候方便最后交付的時候把 SQL 腳本給老師他能在自己的機器上 5 分鐘把環(huán)境跑起來體驗會好很多。我一般會在腳本開頭加DROP DATABASE IF EXISTS保證重復(fù)執(zhí)行不會出問題插入數(shù)據(jù)統(tǒng)一用 INSERT盡量不要用圖形化工具導(dǎo)出的那種帶庫名的格式換環(huán)境時容易出錯。初始數(shù)據(jù)建議每個班級建 3 到 5 個學(xué)生體檢記錄至少建 3 個不同年級的樣例數(shù)據(jù)這樣前端頁面展示時分頁效果、統(tǒng)計圖表都能看出效果。有同學(xué)圖省事只插入一條數(shù)據(jù)結(jié)果一進列表頁就露怯——分頁器只有一頁統(tǒng)計圖表全是單點答辯時老師一看就知道是空殼系統(tǒng)。3. SpringBoot 后端核心實現(xiàn)3.1 項目初始化與依賴配置IDE 這塊社區(qū)版 IntelliJ IDEA 就夠了不需要破解旗艦版。新建項目時選Spring Initializr注意 Server URL 用默認的就行。但這里有個很多新手容易踩的坑JDK 版本和 SpringBoot 版本對應(yīng)不上。比如 IDEA 里只有 JDK 17卻選 SpringBoot 2.7.x啟動會直接報錯。我建議 Java 8 配 SpringBoot 2.3.x 或 2.7.xJava 17 就老老實實配 SpringBoot 3.x或者直接在 IDEA 里把 Project SDK 降到 8。網(wǎng)上說的“IDEA 不能創(chuàng)建 SpringBoot 項目也不能用 JDK 1.8”大多是這個原因不是軟件壞了是版本組合沒對上。pom.xml 里的依賴我按模塊整理一下dependencies !-- Web 啟動器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus 持久層框架 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驅(qū)動 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- Lombok簡化實體類代碼 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- JWT 鑒權(quán) -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency /dependencies3.2 實體類與數(shù)據(jù)庫字段映射實體類這里有個 MyBatis-Plus 的經(jīng)典問題數(shù)據(jù)庫字段是下劃線命名如student_noJava 屬性是駝峰命名如studentNo。默認情況下 MyBatis-Plus 會自動開啟駝峰映射但如果配置沒寫好查出來的數(shù)據(jù)會發(fā)現(xiàn)studentNo為 null而student_no能取到值。我建議在application.yml里顯式聲明mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto實體類上記得加TableName注解指定表名主鍵字段加TableId(type IdType.AUTO)這樣數(shù)據(jù)庫主鍵自增的時候插入數(shù)據(jù)后getId()就能拿到回填的主鍵后續(xù)操作用起來非常方便。3.3 JWT 登錄鑒權(quán)與攔截器實現(xiàn)健康管理系統(tǒng)涉及學(xué)生隱私數(shù)據(jù)不可能裸奔。我用 JWT 做無狀態(tài)鑒權(quán)流程是用戶提交用戶名密碼 → 后端校驗 → 校驗成功生成 token 返回前端 → 前端存到 localStorage → 后續(xù)每個請求在 header 里帶上 token → 后端寫好攔截器統(tǒng)一校驗。攔截器是這里面的核心寫的時候要注意兩個點。第一是放行路徑登錄接口/api/auth/login必須放行否則登錄都登不進來了。第二是 token 校驗失敗后的返回格式前端約定的是 Result 對象攔截器里不能用RestControllerAdvice全局異常直接處理因為請求還沒進入 controller異常拋到容器層就直接返回默認錯誤頁了。我一般在攔截器里直接往 response 寫 JSONresponse.setContentType(application/json;charsetUTF-8); response.setStatus(401); response.getWriter().write(JSONObject.toJSONString(Result.error(401, 登錄狀態(tài)已過期請重新登錄)));3.4 MyBatis-Plus 分頁查詢與條件篩選分頁是這個系統(tǒng)里列表頁的高頻需求。MyBatis-Plus 的分頁插件配置很簡單但網(wǎng)上搜“mybatis 分頁插件用法”一搜全是 PageHelper 的教程版本對不上很多人直接懵了。我這里只講 MyBatis-Plus 的用法Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }配置好之后Service 層這樣寫public Result getStudentList(Integer pageNum, Integer pageSize, String keyword) { LambdaQueryWrapperStudent wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), Student::getName, keyword) .orderByDesc(Student::getCreateTime); PageStudent page studentMapper.selectPage(new Page(pageNum, pageSize), wrapper); return Result.success(page); }這里我用 LambdaQueryWrapper 而不是 QueryWrapper好處是寫字段名時有類型檢查不會因為拼錯字符串在運行時才報錯。另外如果查詢條件里有班級ID、性別這種精確字段記得在 wrapper 里.eq()加上前端篩選功能就是這么一步步拼出來的。3.5 健康異常預(yù)警的業(yè)務(wù)規(guī)則與實現(xiàn)預(yù)警功能是這套系統(tǒng)最能講出東西的地方。規(guī)則其實很簡單但實現(xiàn)方式要稍微想想每次體檢記錄或日常填報插入后觸發(fā)一次檢查邏輯看有沒有指標(biāo)超標(biāo)。我建了warning_log表記錄預(yù)警信息字段包括學(xué)生ID、預(yù)警類型BMI異常、視力下降、血壓偏高、預(yù)警級別輕微/嚴(yán)重、預(yù)警描述、處理狀態(tài)。規(guī)則那邊建議做成常量配置放枚舉或配置類里不要硬寫在 service 里。比如public enum WarningRule { // BMI 超過 28 判定為肥胖 BMI_LIMIT(28.0), // 視力低于 4.8 判定為異常 VISION_LIMIT(4.8), // 收縮壓超過 140 判定為偏高 SYSTOLIC_PRESSURE_LIMIT(140); }為什么建議用枚舉而不是直接數(shù)字因為體檢標(biāo)準(zhǔn)可能會變今年用的是 28明年可能改 26集中管理一處修改全局生效比到處找魔法數(shù)字強太多了。我自己寫這種規(guī)則邏輯時習(xí)慣把判定方法獨立成一個 service 類比如HealthWarningService.checkRecord(record)誰來調(diào)用都清晰也方便寫單元測試。4. Vue 前端頁面搭建與交互4.1 Vue 項目初始化、路由與菜單權(quán)限前端環(huán)境這塊Node.js 版本建議用 16 或 18太新的版本比如 20 以上在某些老項目上會報 OpenSSL 錯誤。Vue CLI 創(chuàng)建項目后先裝依賴要安裝 Element UI、Axios、ECharts 這幾個核心庫。npm 安裝慢的問題我建議把 registry 切到國內(nèi)鏡像一次性解決npm config set registry https://registry.npmmirror.com npm install element-ui axios echarts vue-router3Vue 2 里路由用 vue-router 3.xVue 3 用 4.x這個坑也很大裝錯了版本啟動直接白屏。路由這塊我用的是靜態(tài)路由加動態(tài)菜單結(jié)合的方式基礎(chǔ)路由登錄頁、404靜態(tài)注冊業(yè)務(wù)路由學(xué)生管理、體檢記錄、健康填報根據(jù)用戶角色動態(tài)生成。動態(tài)菜單實現(xiàn)上有個實用小技巧路由的 meta 里加一個roles字段比如{ path: /student, name: Student, component: () import(/views/student/index.vue), meta: { title: 學(xué)生管理, roles: [admin, teacher] } }路由守衛(wèi)里判斷當(dāng)前用戶的角色是否在meta.roles里不在就跳 403 頁面。這樣做權(quán)限控制的邏輯非常直觀評審老師一問你就答得上來。4.2 Axios 請求封裝與攔截器前端和后端溝通全靠 Axios但一定不要每個頁面都寫一遍 axios.get太散而且不好處理統(tǒng)一錯誤。我在src/utils/request.js里封裝一個實例統(tǒng)一配置 baseURL、請求超時時間、請求攔截器和響應(yīng)攔截器import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) // 請求攔截器自動帶 token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) // 響應(yīng)攔截器統(tǒng)一處理返回碼和登錄過期 service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 請求失敗) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { Message.error(登錄已過期請重新登錄) localStorage.removeItem(token) router.push(/login) } else { Message.error(網(wǎng)絡(luò)異常請稍后重試) } return Promise.reject(error) } )4.3 表單校驗、父子組件通信與數(shù)據(jù)可視化健康數(shù)據(jù)填報頁面的表單校驗是前端的一個重點。Element UI 的el-form自帶的 rules 校驗很好用比如身高必須是 50 到 250 之間的數(shù)字、體重必須是 20 到 200 之間的數(shù)字日期不能晚于今天。自定義校驗函數(shù)里要注意校驗失敗時一定要調(diào)用 callback 返回錯誤否則表單會一直轉(zhuǎn)圈圈不提示任何東西。另外日期組件value-formatyyyy-MM-dd一定要加不加的話提交到后端是 Date 對象JSON 序列化容易出格式問題。體檢記錄查看頁面我用 ECharts 做了可視化圖表比如班級 BMI 分布柱狀圖、學(xué)生個人身高體重趨勢折線圖。這里有一個踩坑點ECharts 在表格彈窗里渲染時經(jīng)常出現(xiàn)寬度為 0 的情況圖表畫不出來原因是在彈窗還沒顯示完畢時就初始化。解決辦法很簡單圖表初始化放在nextTick里或者監(jiān)聽彈窗的 opened 事件后再初始化這個經(jīng)驗我至少幫三個同學(xué)排查過都是同一個問題。5. 部署、環(huán)境配置與高頻問題排查5.1 本地環(huán)境準(zhǔn)備JDK、Node、MySQL 安裝要點環(huán)境準(zhǔn)備這塊最容易卡住的是 MySQL。官網(wǎng)下載 zip 包后還要自己配置 my.ini、初始化 data 目錄、設(shè)置 root 密碼步驟多且繁瑣。新版本 MySQL 8.x 和 5.7 在密碼加密方式上還不一樣mysql_native_password和caching_sha2_password兩種方式對老客戶端兼容性有差異。我建議裝 MySQL 8.0 時用官方安裝包一把梭中途設(shè)置 root 密碼千萬別忘連接測試用 Workbench 或者命令行都行。還是連不上十有八九是服務(wù)沒啟動Windows 下到“服務(wù)”里看 MySQL 服務(wù)狀態(tài)或者運行net start mysql手動啟動。JDK 和 Maven 的坑相對少一些但要注意 JDK 8 和 JDK 17 在 Maven 編譯配置里 target 版本必須對應(yīng)不然報invalid target release。5.2 數(shù)據(jù)庫連接配置與前端 API 地址聯(lián)調(diào)后端配置這里我提供一個穩(wěn)定可用的application.yml模板server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/health_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: your_password jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai聯(lián)調(diào)時最頭疼的是跨域問題。前端在 8081 端口跑 dev server后端在 8080瀏覽器直接發(fā)請求肯定跨域。不過如果你按我前面說的在 Axios 里配了baseURL: /api然后在 vue.config.js 里配了 devServer 的 proxy那么本地開發(fā)是不需要后端配CrossOrigin的// vue.config.js module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }這樣請求/api/student/list時Vue 開發(fā)服務(wù)器會自動幫你把請求轉(zhuǎn)發(fā)到http://localhost:8080/api/student/list瀏覽器看到的只是同源請求自然沒有跨域問題。5.3 前后端打包與服務(wù)器部署本地開發(fā)全部干通了部署上線就是把前端打包成靜態(tài)文件后端打成 jar 包。前端在項目根目錄執(zhí)行npm run build產(chǎn)物在dist目錄。后端用 Maven 打包在 IDEA 右側(cè) Maven 面板執(zhí)行package產(chǎn)物是target目錄下的 jar 包。部署策略有兩種。一種是分開部署后端 jar 包直接java -jar跑在 8080 端口前端靜態(tài)文件交給 NginxNginx 里配一個/api路徑的反向代理轉(zhuǎn)發(fā)到后端。另一種適合畢設(shè)演示場景的簡單部署方式把前端構(gòu)建后的dist目錄直接復(fù)制到后端的src/main/resources/static下重新打包 jar這樣只用跑一個進程訪問 8080 端口就同時得到前后端。這種方式雖然不那么“生產(chǎn)化”但做畢設(shè)演示、給老師評審的時候真的省心兩臺電腦拷過去就能跑。部署完還有個小流程別忘了用全新的數(shù)據(jù)庫導(dǎo)入init.sql時注意 MySQL 8 的默認字符集是 utf8mb4建庫腳本里也建議顯式寫DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci不然存 emoji 表情或特殊字符時有可能報錯。5.4 高頻報錯與排查實錄報錯現(xiàn)象原因分析解決辦法Error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sockMySQL 服務(wù)未啟動或 socket 路徑不對檢查服務(wù)狀態(tài)Windows 用 net start mysqlLinux 用 systemctl status mysqldIDEA 創(chuàng)建 SpringBoot 項目用不了 JDK 1.8SpringBoot 版本和 JDK 版本不匹配統(tǒng)一版本JDK 8 配 2.7.xJDK 17 配 3.x前端白屏不報錯打開控制臺看 vue-router 報錯vue-router 版本裝錯Vue2 裝了 v4Vue2 用 vue-router3Vue3 用 vue-router4接口返回數(shù)據(jù)但字段全是 null駝峰映射未開啟或?qū)嶓w類屬性名與字段名不一致開啟 map-underscore-to-camel-case或檢查 TableField 注解登錄接口返回 401但賬號密碼肯定沒錯攔截器放行了錯誤路徑或驗證邏輯寫在登錄接口之前檢查攔截器 excludePathPatterns確認登錄路徑放行ECharts 圖表在彈窗中不顯示彈窗未顯示完就初始化圖表把初始化放在 nextTick 或彈窗 opened 事件里npm install 卡住不動網(wǎng)絡(luò)問題默認源訪問慢切換 npmmirror 鏡像源6. 從源碼到論文如何把項目變成一份像樣的畢設(shè)材料很多人以為源碼寫完就萬事大吉了其實畢業(yè)設(shè)計的得分大頭往往在論文和答辯。論文這塊我的建議是不要把論文寫成一本“用戶手冊”也不要寫成“操作教程”而是當(dāng)成一個技術(shù)方案文檔來寫——每個模塊要說出“為什么這么設(shè)計”、解決了什么問題、對比過什么方案。比如數(shù)據(jù)庫設(shè)計章節(jié)可以寫為什么用戶表和學(xué)生表要分開分開有什么好處如果合并在什么場景下會出問題。再比如 JWT 和 Session 的對比為什么選 JWT 做無狀態(tài)鑒權(quán)它在前后端分離架構(gòu)里比 Session 好在哪。這些內(nèi)容寫出來論文的深度一下就上去了。別小看這些“為什么”盲審老師和答辯評委看的就是你有沒有真正理解自己做的東西。系統(tǒng)部署文檔也值得認真寫。我見過很多同學(xué)源碼寫得挺完整但部署文檔就一句話“把前后端跑起來”。真要換一臺電腦讓評審老師自己配 JDK、MySQL、Node各種環(huán)境問題能把老師逼瘋。我的習(xí)慣是寫一份面向新手的部署文檔步驟寫到先裝什么、再裝什么、每個安裝界面注意勾選什么、MySQL 密碼設(shè)什么、SQL 腳本在哪執(zhí)行、前端依賴怎么裝、后端配置怎么改配圖加文字標(biāo)得清清楚楚。這份文檔自己在干凈的電腦上照著走一遍能順利跑起來才算合格。答辯的時候最容易翻車的不是講不出功能而是老師問“為什么這么設(shè)計”時答不上來。比如為什么用 MyBatis-Plus 而不用 MyBatis你要能說出“項目里大部分是單表 CRUDPlus 提供的 BaseMapper 和 LambdaQueryWrapper 能少寫大量重復(fù) XML只有多表關(guān)聯(lián)時才手寫 SQL”這種有思考的答案。再比如問“如果并發(fā)量大怎么優(yōu)化”你可以答“系統(tǒng)里主要是查詢操作可以在列表接口上做 Redis 緩存把頻繁讀的班級字典、健康指標(biāo)字典緩存起來”。問題答到點子上評委對你的印象會完全不一樣。按這個完整鏈路走下來從數(shù)據(jù)庫腳本到后端接口從前端頁面到部署文檔整個項目就是一套完整體面的作品。這套東西做完基本沒有明顯短板的系統(tǒng)拿去交差、寫簡歷、后面工作面試聊項目都能掏出實在內(nèi)容。要是你在復(fù)現(xiàn)過程中卡在某個環(huán)境問題或報錯上把錯誤信息拿出來逐條對上表排查80% 的情況都是版本不匹配、服務(wù)沒啟動、配置沒生效這幾類老問題。我最后再分享一個小技巧開發(fā)過程中遇到?jīng)]見過的報錯先把報錯信息原文復(fù)制到搜索引擎里搜英文資料別只搜中文很多冷門問題的答案其實只在官方 issue 或 Stack Overflow 上。