平臺實戰(zhàn)項目(含RBAC權(quán)限與部署避坑指南))
簡介本資源是一套完整的大學生就業(yè)服務平臺微信小程序畢業(yè)設(shè)計項目面向計算機專業(yè)本科生及Java全棧初學者解決校園招聘場景下學生求職、企業(yè)招聘與管理員協(xié)同管理的全流程需求。壓縮包共1270個文件涵蓋231張界面圖標png/svg、175個小程序邏輯腳本js、136個Vue組件vue、127個后端業(yè)務類java、96個配置與數(shù)據(jù)接口定義json、86個樣式文件wxss/wxml及2個建表SQL腳本完整包含前后端源碼、MySQL數(shù)據(jù)庫文件與部署說明總大小20.02MB。已有90人學習下載適合課程設(shè)計參考、畢設(shè)快速啟動與小程序SpringBootMySQL技術(shù)棧綜合實踐。讀者可直接導入Eclipse/IDEA與微信開發(fā)者工具運行含bat一鍵安裝/啟動腳本、多端登錄態(tài)管理、簡歷投遞與面試邀請閉環(huán)、企業(yè)類型分類管理等典型功能模塊目錄結(jié)構(gòu)規(guī)范注釋較完整便于理解分層架構(gòu)與跨端交互邏輯。1. 這不是又一個“畢設(shè)模板”而是一套能真跑起來的就業(yè)平臺閉環(huán)Java后端 微信小程序 MySQL全??刹渴鹪创a你是不是也見過太多標著“微信小程序畢業(yè)設(shè)計”的壓縮包點開一看全是空文件夾、半截 Vue 組件、連數(shù)據(jù)庫表都建不全的 SQL 腳本這次不一樣——這個.zip包里塞進的是某高校計算機專業(yè)真實交付過、學生答辯用過的完整就業(yè)平臺系統(tǒng)。它不是 Demo不是截圖生成器而是從1-install.bat到微信開發(fā)者工具掃碼預覽全鏈路可走通的工程學生端能投簡歷、查面試邀請企業(yè)端能發(fā)崗位、收簡歷、發(fā)邀約管理員后臺能增刪改查所有角色數(shù)據(jù)連留言板和用戶反饋都帶審核流。技術(shù)棧干凈利落JDK1.8 Spring Boot 2.1.x非 Spring Cloud 黑匣子、MySQL 5.7非高版本兼容陷阱、Vue 2.6非 Vue3 語法玄學小程序端用的是原生 WXML JS沒上 UniApp 抽象層調(diào)試時變量在哪、請求在哪、報錯在哪一眼可見。適合兩類人一是大四學生想拿個穩(wěn)過、能講清每行邏輯的畢設(shè)項目二是剛轉(zhuǎn)前端/全棧的新手想拆一個“有業(yè)務閉環(huán)、有真實權(quán)限分層、有前后端交互細節(jié)”的輕量級生產(chǎn)級參考。2. 搭建前必須確認的三件事環(huán)境邊界、文件結(jié)構(gòu)真相與啟動腳本邏輯提示別急著雙擊1-install.bat先看清它到底干了什么否則后續(xù)2-run.bat報錯時你會在ClassNotFoundException和Access denied for user之間反復橫跳。2.1 環(huán)境不是“支持”而是硬性依賴為什么 JDK1.8 和 MySQL 5.7 不可替換這個項目用的是 Spring Boot 2.1.18.RELEASE從pom.xml中spring-boot.version可確認它底層依賴 Spring Framework 5.1.x而 Spring Framework 5.1.x 明確要求 JDK8不支持 JDK9 的模塊化變更。如果你強行用 JDK11 啟動會在org.springframework.context.support.AbstractApplicationContext.refresh()階段拋出java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException—— 因為 JAXB 在 JDK11 中被徹底移除而本項目未添加jakarta.xml.bind:jakarta.xml.bind-api依賴補丁。MySQL 方面項目 SQL 文件如db.sql中大量使用datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP語法。MySQL 5.6 對ON UPDATE CURRENT_TIMESTAMP支持不完整僅允許一個TIMESTAMP字段而本項目student表、enterprise表均含多個帶該修飾的字段MySQL 5.7 才真正支持多字段CURRENT_TIMESTAMP自動更新。若你用 MySQL 8.0雖語法兼容但默認開啟的caching_sha2_password認證插件會導致 Java 連接報Unknown initial character set index 255錯誤——這不是代碼問題是驅(qū)動版本與服務端認證協(xié)議不匹配。所以結(jié)論很明確必須用 JDK1.8u202或以上 8u 版本 MySQL 5.7.32推薦。別信“我改個配置就能跑”這是血淚經(jīng)驗換來的邊界。2.2 解壓后看到的.bak文件不是垃圾而是關(guān)鍵調(diào)試線索解壓目錄里一堆*.vue.bak、*.css.bak比如update-password.vue.bak、main.css.bak。新手常直接刪掉結(jié)果運行時發(fā)現(xiàn)密碼修改頁樣式錯亂、按鈕點擊無響應。其實這些.bak是開發(fā)過程中保留的舊版組件備份而當前實際加載的是update-password.vue無.bak后綴——但注意update-password.vue文件本身可能為空或只有一行!-- TODO: 實現(xiàn)密碼修改邏輯 --。此時.bak文件就是唯一可用的完整實現(xiàn)。我們來驗證# 進入小程序源碼目錄通常是 /miniprogram/pages/update-password/ ls -la # 輸出類似 # update-password.js # update-password.json # update-password.wxml # update-password.wxss # update-password.vue.bak ← 這才是真·業(yè)務邏輯所在打開update-password.vue.bak你會發(fā)現(xiàn)它包含完整的 Vue 實例定義、wx.request調(diào)用/api/student/updatePassword接口、表單校驗規(guī)則含兩次輸入比對、成功后跳轉(zhuǎn)邏輯。而update-password.vue無 bak可能是空文件或占位符。這不是疏忽是典型的學生項目迭代痕跡用.bak存活代碼主文件留作重構(gòu)入口。2.31-install.bat不是“一鍵安裝”而是三步原子操作解壓、建庫、導入雙擊1-install.bat前請用記事本打開它看本質(zhì)別用 IDE 直接執(zhí)行echo off echo 正在解壓依賴jar包... 7z x lib\dependencies.7z -olib\temp nul echo 正在創(chuàng)建數(shù)據(jù)庫... mysql -uroot -proot -e CREATE DATABASE IF NOT EXISTS job_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; echo 正在導入SQL... mysql -uroot -proot job_platform db\db.sql echo 安裝完成請檢查控制臺是否出現(xiàn) ERROR。 pause關(guān)鍵點有三它用7z解壓lib\dependencies.7z到lib\temp說明項目未走 Maven 倉庫拉取而是把spring-boot-starter-web、mybatis-spring-boot-starter等 jar 包全部打進了壓縮包。如果你沒裝 7-Zip 或環(huán)境變量未配這一步會靜默失敗后續(xù)2-run.bat啟動時直接報NoClassDefFoundError。數(shù)據(jù)庫名硬編碼為job_platform字符集強制utf8mb4支持 emoji但賬號密碼寫死為-uroot -proot。如果你的 MySQL root 密碼不是root或用了其他賬號必須手動改 bat 文件里的-proot部分。db.sql是完整建庫腳本但注意它不包含初始化數(shù)據(jù)如管理員賬號admin/123456。初始化數(shù)據(jù)在另一個文件db\init-data.sql中而1-install.bat并未執(zhí)行它——這是故意留的“安全閥”避免部署時自動創(chuàng)建高權(quán)限賬號。所以正確流程是① 修改1-install.bat中 MySQL 密碼為你的真實密碼② 確保已安裝 7-Zip 并加入 PATH③ 手動執(zhí)行mysql -uroot -p你的密碼 job_platform db\init-data.sql補全基礎(chǔ)賬號。3. 后端啟動失敗的五大高頻現(xiàn)象從端口沖突到 MyBatis 映射斷裂3.1 現(xiàn)象2-run.bat執(zhí)行后控制臺閃退日志末尾只有INFO o.s.b.w.e.t.TomcatServletWebServerFactory : Tomcat initialized with port(s): 8080 (http)無Started Application in X seconds原因Tomcat 7 默認端口 8080 被占用常見于 Skype、另一套 Java 項目、甚至某些國產(chǎn)軟件。Spring Boot 啟動時無法綁定端口但未顯式報錯而是靜默退出。解決① 查端口占用netstat -ano | findstr :8080記下 PID② 查進程名tasklist | findstr PID③ 殺進程taskkill /f /pid PID④ 或改端口在src/main/resources/application.yml中加server: port: 8081注意改端口后小程序端utils/request.js中的baseUrl也要同步改為http://localhost:8081否則所有請求 404。3.2 現(xiàn)象控制臺報Caused by: java.sql.SQLException: Access denied for user rootlocalhost原因application.yml中數(shù)據(jù)庫配置寫死為spring: datasource: url: jdbc:mysql://localhost:3306/job_platform?useUnicodetruecharacterEncodingutf8serverTimezoneGMT%2B8 username: root password: root但你的 MySQL 服務未啟用rootlocalhost賬號或密碼不對。解決① 登錄 MySQLmysql -u root -p② 創(chuàng)建專用賬號更安全CREATE USER job_userlocalhost IDENTIFIED BY Job2024; GRANT ALL PRIVILEGES ON job_platform.* TO job_userlocalhost; FLUSH PRIVILEGES;③ 修改application.ymlusername: job_user password: Job20243.3 現(xiàn)象啟動成功但訪問http://localhost:8080/login返回 404或http://localhost:8080/api/student/login返回{code:500,msg:系統(tǒng)異常}原因MyBatis 的 XML 映射文件路徑或命名不匹配。本項目采用Mapper XML方式非注解StudentMapper.java接口需對應StudentMapper.xml且 XML 中namespace必須與接口全限定名一致。檢查src/main/resources/mapper/StudentMapper.xmlmapper namespacecom.job.mapper.StudentMapper select idlogin resultTypecom.job.entity.Student SELECT * FROM student WHERE phone #{phone} AND password #{password} /select /mapper若StudentMapper.java在com.job.dao.StudentMapper包下則namespace必須改為com.job.dao.StudentMapper否則 Spring Boot 啟動時不會注冊該 Mapper調(diào)用時拋Invalid bound statement (not found)。驗證方法啟動后查看日志中是否有Mapped Statements collection:后列出com.job.mapper.StudentMapper.login。沒有即為映射失敗。3.4 現(xiàn)象小程序登錄時提示 “request:fail timeout” 或 “request:fail net::ERR_CONNECTION_REFUSED”原因微信開發(fā)者工具默認不允許http請求iOS/Android 小程序強制https但開發(fā)者工具可本地調(diào)試http。但本項目utils/request.js中baseUrl寫的是http://localhost:8080而開發(fā)者工具運行時localhost指向的是開發(fā)者工具所在電腦的 localhost不是你后端服務所在的電腦。解決① 確保后端服務與開發(fā)者工具在同一臺電腦② 在開發(fā)者工具頂部菜單欄 → 詳情 → 本地設(shè)置 → 勾選“不校驗合法域名、web-view業(yè)務域名、TLS 版本以及 HTTPS 證書”③ 若后端在另一臺機器如公司服務器baseUrl改為服務器 IPhttp://192.168.1.100:8080并確保服務器防火墻放行 8080 端口。3.5 現(xiàn)象管理員登錄后臺http://localhost:8080/admin后左側(cè)菜單空白F12 查看 Network/api/admin/menu接口返回{code:401,msg:未登錄}原因Spring Security 配置中/api/admin/**路徑被攔截但登錄接口/api/admin/login未放行導致登錄請求本身就被 401 攔截。檢查com.job.config.SecurityConfig.javaOverride protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/api/admin/login).permitAll() // 必須有 .antMatchers(/api/admin/**).authenticated() .and() .formLogin().disable(); }若.antMatchers(/api/admin/login).permitAll()這一行缺失登錄請求就會被 Security 攔截永遠無法建立 session。修復補上該行并重啟后端。4. 小程序端核心功能落地從首頁輪播圖到簡歷投遞的四步鏈路4.1 首頁輪播圖不是靜態(tài)圖而是動態(tài)加載的 Banner 管理系統(tǒng)首頁index/index.wxml中輪播組件swiper indicator-dots autoplay interval3000 duration500 swiper-item wx:for{{banners}} wx:keyid image src{{item.imgUrl}} modeaspectFill bindtapgoToBannerDetail>onLoad() { this.getBanners(); }, getBanners() { wx.request({ url: getApp().globalData.baseUrl /api/banner/list, method: GET, success: res { if (res.data.code 200) { this.setData({ banners: res.data.data }); } } }); }后端接口/api/banner/list對應BannerController.javaGetMapping(/list) public Result list() { ListBanner banners bannerService.list(); // 查詢 banner 表 return Result.success(banners); }關(guān)鍵點banner表在db.sql中已建好但初始無數(shù)據(jù)。你需要手動插入INSERT INTO banner (id, title, imgUrl, linkUrl, sort, status) VALUES (1, 2024春招啟動, https://example.com/banner1.jpg, /pages/recruit/recruit, 1, 1);linkUrl字段存的是小程序頁面路徑非 URL/pages/recruit/recruit即跳轉(zhuǎn)到招聘列表頁。status1表示啟用否則不顯示。4.2 招聘信息列表分頁 關(guān)鍵詞搜索 企業(yè)篩選的完整實現(xiàn)招聘信息頁recruit/recruit.wxml使用scroll-view實現(xiàn)上拉加載scroll-view scroll-y bindscrolltolowerloadMore lower-threshold50 view wx:for{{jobs}} wx:keyid classjob-item bindtapgoToDetail>loadMore() { if (this.data.loading || this.data.hasMore false) return; this.setData({ loading: true }); const params { page: this.data.page 1, limit: 10, keyword: this.data.keyword, enterpriseId: this.data.enterpriseId }; wx.request({ url: getApp().globalData.baseUrl /api/job/page, data: params, method: GET, success: res { const jobs res.data.data.records; this.setData({ jobs: this.data.jobs.concat(jobs), page: params.page, hasMore: jobs.length 10 }); } }); }后端/api/job/page接口使用 MyBatis-Plus 的Page分頁GetMapping(/page) public Result page(RequestParam Integer page, RequestParam Integer limit, RequestParam(required false) String keyword, RequestParam(required false) Long enterpriseId) { PageJob jobPage new Page(page, limit); QueryWrapperJob wrapper new QueryWrapper(); if (StrUtil.isNotBlank(keyword)) { wrapper.like(title, keyword).or().like(requirement, keyword); } if (enterpriseId ! null) { wrapper.eq(enterprise_id, enterpriseId); } wrapper.orderByDesc(create_time); IPageJob result jobService.page(jobPage, wrapper); return Result.success(result); }注意keyword搜索同時匹配title職位名和requirement任職要求這是學生項目里少有的合理搜索設(shè)計。4.3 簡歷投遞一次請求完成「查簡歷狀態(tài) 發(fā)送投遞記錄 更新企業(yè)通知」三件事投遞按鈕在job-detail/job-detail.wxmlbutton bindtapapplyJob classbtn-apply disabled{{applied}}立即投遞/buttonapplyJob方法applyJob() { const jobId this.data.job.id; wx.request({ url: getApp().globalData.baseUrl /api/apply/submit, method: POST, data: { jobId: jobId }, header: { Authorization: wx.getStorageSync(token) }, // 攜帶學生 token success: res { if (res.data.code 200) { wx.showToast({ title: 投遞成功, icon: success }); this.setData({ applied: true }); } } }); }后端ApplyController.submit()是事務性操作Transactional PostMapping(/submit) public Result submit(RequestBody ApplyDTO dto) { // 1. 檢查學生是否已投遞該職位 QueryWrapperApply applyWrapper new QueryWrapper(); applyWrapper.eq(student_id, getCurrentStudentId()) .eq(job_id, dto.getJobId()); if (applyService.count(applyWrapper) 0) { return Result.error(您已投遞過該職位); } // 2. 創(chuàng)建投遞記錄 Apply apply new Apply(); apply.setStudentId(getCurrentStudentId()); apply.setJobId(dto.getJobId()); apply.setStatus(1); // 1-待處理 applyService.save(apply); // 3. 更新企業(yè)未讀通知數(shù) Job job jobService.getById(dto.getJobId()); Enterprise enterprise enterpriseService.getById(job.getEnterpriseId()); enterprise.setNoticeCount(enterprise.getNoticeCount() 1); enterpriseService.updateById(enterprise); return Result.success(); }亮點用Transactional保證三步原子性NoticeCount字段讓企業(yè)端首頁右上角小紅點實時更新這是真實業(yè)務感的體現(xiàn)。4.4 我的收藏跨表關(guān)聯(lián)查詢的輕量級實現(xiàn)“我的收藏”頁my-collection/my-collection.wxml展示收藏的職位view wx:for{{collections}} wx:keyid classcollection-item view classjob-title{{item.job.title}}/view view classjob-meta{{item.job.enterpriseName}} · {{item.job.salary}}/view button bindtapcancelCollect>getCollections() { wx.request({ url: getApp().globalData.baseUrl /api/collection/list, header: { Authorization: wx.getStorageSync(token) }, success: res { if (res.data.code 200) { // 后端已連表查出 job 信息前端無需二次請求 this.setData({ collections: res.data.data }); } } }); }后端CollectionController.list()使用 MyBatis 的resultMap連表!-- CollectionMapper.xml -- resultMap idCollectionWithJob typecom.job.entity.Collection id propertyid columnc.id/ result propertycreateTime columnc.create_time/ association propertyjob javaTypecom.job.entity.Job id propertyid columnj.id/ result propertytitle columnj.title/ result propertyenterpriseName columne.name/ result propertysalary columnj.salary/ /association /resultMap select idlistWithJob resultMapCollectionWithJob SELECT c.*, j.title, j.salary, e.name as enterpriseName FROM collection c LEFT JOIN job j ON c.job_id j.id LEFT JOIN enterprise e ON j.enterprise_id e.id WHERE c.student_id #{studentId} ORDER BY c.create_time DESC /select優(yōu)勢一次 SQL 查出所有需要字段避免 N1 查詢對學生項目而言是性能意識的體現(xiàn)。5. 管理員后臺權(quán)限體系解析RBAC 模型的極簡落地與三個繞不開的坑5.1 權(quán)限控制不是靠 Session ID而是基于 JWT Token 的無狀態(tài)鑒權(quán)管理員登錄/api/admin/login成功后后端返回{ code: 200, data: { token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTcxNzYwMzIwMH0.abc123..., admin: { id: 1, username: admin, realName: 系統(tǒng)管理員 } } }此token由JwtUtil.generateToken(admin.getUsername())生成算法為 HS256密鑰硬編碼在JwtUtil.javaprivate static final String SECRET job_platform_admin_secret_key_2024;后續(xù)所有/api/admin/**請求必須在 Header 中攜帶Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...JwtFilter.java攔截請求解析 token 獲取用戶名再查admin表確認權(quán)限。為什么不用 Session因為管理員后臺是純前端HTMLJS無服務端渲染Session 依賴 Cookie而跨域請求前端域名 vs 后端域名下 Cookie 默認不發(fā)送。JWT 通過 Header 傳遞天然規(guī)避跨域限制。5.2 菜單權(quán)限不是寫死而是數(shù)據(jù)庫驅(qū)動的動態(tài)路由管理員登錄后前端通過/api/admin/menu獲取菜單GetMapping(/menu) public Result menu() { Long adminId getCurrentAdminId(); ListMenu menus menuService.getMenusByAdminId(adminId); return Result.success(menus); }menuService.getMenusByAdminId()執(zhí)行 SQLSELECT DISTINCT m.* FROM menu m INNER JOIN role_menu rm ON m.id rm.menu_id INNER JOIN admin_role ar ON rm.role_id ar.role_id WHERE ar.admin_id ? ORDER BY m.sort ASCmenu表結(jié)構(gòu)idnamepathcomponenticonsortparentId1個人中心/admin/profileProfile.vueuser102學生管理/admin/studentStudentList.vueusers20component字段值如StudentList.vue被前端router/index.js動態(tài) importconst routes [ { path: /admin, component: () import(/views/Layout.vue), children: [] } ]; // 動態(tài)添加子路由 function addRoutes(menus) { menus.forEach(menu { routes[0].children.push({ path: menu.path, name: menu.name, component: () import(/views/${menu.component}) }); }); }這意味著你新增一個菜單項只需往menu表插一條記錄無需改任何前端代碼。5.3 企業(yè)類型管理下拉選擇背后的三級聯(lián)動陷阱企業(yè)發(fā)布職位時需選擇“企業(yè)類型”該下拉框數(shù)據(jù)來自/api/enterprise/type/list。EnterpriseTypeController.list()返回[ { id: 1, name: 互聯(lián)網(wǎng), parentId: 0 }, { id: 2, name: 金融, parentId: 0 }, { id: 3, name: 人工智能, parentId: 1 }, { id: 4, name: 大數(shù)據(jù), parentId: 1 } ]這是一個典型的樹形結(jié)構(gòu)parentId0為一級類目。但前端enterprise-add/enterprise-add.wxml的下拉框是扁平化的picker bindchangebindTypeChange value{{typeIndex}} range{{typeList}} range-keyname view classpicker{{typeList[typeIndex] ? typeList[typeIndex].name : 請選擇企業(yè)類型}}/view /picker坑就在這里range-keyname只顯示名稱但提交時需要id。bindTypeChange事件中bindTypeChange(e) { const typeId this.data.typeList[e.detail.value].id; // ? 正確獲取 id this.setData({ typeId: typeId, typeIndex: e.detail.value }); }如果誤寫成this.data.typeList[e.detail.value]沒取.id提交的typeId就是整個對象后端RequestBody綁定失敗報HttpMessageNotReadableException。5.4 避坑管理員后臺三大血淚教訓現(xiàn)象 1登錄后點擊“學生管理”頁面空白Console 報Cannot find module /views/StudentList.vue原因menu表中component字段值為StudentList.vue但實際文件在src/views/student/StudentList.vue路徑不匹配。解決統(tǒng)一menu.component字段格式為student/StudentList.vue或修改router/index.js的import路徑為/views/${menu.component}.vue加.vue后綴?,F(xiàn)象 2修改學生信息后列表頁數(shù)據(jù)未刷新仍顯示舊值原因前端StudentList.vue的editStudent方法調(diào)用update接口成功后未觸發(fā)this.getList()重新拉取數(shù)據(jù)而是僅this.dialogVisible false關(guān)閉彈窗。解決在update的success回調(diào)中加this.$message.success(更新成功); this.dialogVisible false; this.getList(); // ? 強制刷新列表現(xiàn)象 3刪除企業(yè)時關(guān)聯(lián)的職位、簡歷未級聯(lián)刪除導致數(shù)據(jù)不一致原因enterprise表與job表是物理外鍵job.enterprise_id外鍵指向enterprise.id但 MySQL 5.7 默認存儲引擎為 MyISAM不支持外鍵。項目db.sql中建表語句未指定ENGINEInnoDB。解決手動執(zhí)行ALTER TABLE job ENGINEInnoDB; ALTER TABLE resume ENGINEInnoDB; -- 然后加外鍵約束謹慎先備份 ALTER TABLE job ADD CONSTRAINT fk_job_enterprise FOREIGN KEY (enterprise_id) REFERENCES enterprise(id) ON DELETE CASCADE;注意ON DELETE CASCADE是雙刃劍確保業(yè)務允許級聯(lián)刪除。6. 從“能跑”到“能講”答辯前必做的三件事與一個防翻車 checklist6.1 重構(gòu)application.yml把所有硬編碼參數(shù)抽離為可配置項答辯時老師最愛問“如果數(shù)據(jù)庫密碼變了你要改幾個地方” 如果你答“改application.yml一個地方”老師會點頭如果答“還要改1-install.bat”你就露餡了。所以必須做配置解耦① 在application.yml中定義 profilespring: profiles: active: dev --- spring: config: activate: on-profile: dev datasource: url: jdbc:mysql://localhost:3306/job_platform?... username: ${DB_USER:root} password: ${DB_PASS:root} --- spring: config: activate: on-profile: prod datasource: url: ${DB_URL:jdbc:mysql://prod-db:3306/job_platform...} username: ${DB_USER:job_user} password: ${DB_PASS:Job2024}② 創(chuàng)建application-dev.yml和application-prod.yml把敏感信息放進去③ 啟動時加參數(shù)java -jar job-platform.jar --spring.profiles.activedev。這樣答辯演示時你可以自信地說“所有配置已外部化切換環(huán)境只需改一個參數(shù)?!?.2 為關(guān)鍵接口補全 Swagger 文檔讓老師一眼看懂你的設(shè)計項目自帶Swagger2配置SwaggerConfig.java但 Controller 方法缺少ApiOperation注解。補上后訪問http://localhost:8080/swagger-ui.html就能看到專業(yè)文檔。例如在StudentController.login()上加ApiOperation(學生登錄) ApiResponses({ ApiResponse(code 200, message 登錄成功返回 token), ApiResponse(code 401, message 賬號或密碼錯誤), ApiResponse(code 400, message 參數(shù)缺失) }) PostMapping(/login) public Result login(RequestBody StudentLoginDTO dto) { // ... }效果老師點開/api/student/login立刻看到請求體示例、返回格式、錯誤碼含義——這比你口頭解釋“我這里做了校驗”有力十倍。6.3 準備一份《答辯自測 checklist》覆蓋老師 90% 的提問點序號檢查項操作是否完成備注1后端能否獨立啟動訪問http://localhost:8080/actuator/health返回{status:UP}2-run.bat→ 瀏覽器訪問?必須成功2小程序能否掃碼預覽首頁輪播圖、招聘列表是否正常加載微信開發(fā)者工具 → 預覽?確保baseUrl正確3學生注冊→登錄→投遞簡歷→查看投遞記錄全流程走通手動操作一遍?錄屏備用4管理員登錄→學生管理→禁用某學生賬號→學生端再次登錄是否提示“賬號已禁用”模擬禁用流程?權(quán)限控制驗證5企業(yè)發(fā)布職位→學生投遞→企業(yè)后臺“面試邀請”頁是否收到新記錄兩端聯(lián)動測試?業(yè)務閉環(huán)證明6修改application.yml中server.port為8081重啟后端小程序baseUrl同步修改是否仍能通信端口變更測試?配置能力證明提示這份 checklist 不是給老師看的是給你自己用的。答辯前一晚按表逐項操作卡在哪一項就重點準備那一項的原理和代碼位置。我?guī)н^的 A同學就因漏測第 4 項在答辯時被問“怎么防止惡意注冊”當場懵住——其實答案就在StudentController.register()里的一行if (studentService.count(wrapper) 0) throw new RuntimeException(手機號已存在);但他沒定位到。從那以后我每次幫學生過畢設(shè)都強制他們把 checklist 打印出來用紅筆一項項劃掉直到所有 ? 變成 ?。不是為了應付老師而是讓你在答辯室坐下那一刻心里有底——你知道每一行代碼長什么樣知道每個 bug 曾在哪里翻過車知道這個系統(tǒng)不是拼湊的玩具而是你親手搭起來、調(diào)通、并真正理解的工程。希望幫到你。本文還有配套的精品資源點擊獲取