學(xué)習(xí)網(wǎng)站:從源碼到部署的完整實(shí)踐)
這個(gè)項(xiàng)目我在本地從頭跑到尾又把數(shù)據(jù)庫、接口、前端頁面全部捋了一遍還順手總結(jié)了答辯環(huán)節(jié)最容易被追問的技術(shù)點(diǎn)。今天這篇文章不聊虛的就直接把這個(gè) SpringBootVue 在線互動(dòng)學(xué)習(xí)網(wǎng)站的源碼結(jié)構(gòu)、SQL腳本怎么導(dǎo)、接口文檔怎么用、前后端怎么對(duì)接、線上怎么跑通一條一條給你講完。不管你是剛拿到這份完整項(xiàng)目源碼準(zhǔn)備做畢設(shè)還是想找一個(gè)現(xiàn)成的 Java Web 項(xiàng)目練手看完這篇基本就能自己復(fù)現(xiàn)出來。1. 項(xiàng)目定位與模塊全景一個(gè)在線互動(dòng)學(xué)習(xí)平臺(tái)要裝下哪些東西先把這個(gè)項(xiàng)目的業(yè)務(wù)邊界圈清楚。在線互動(dòng)學(xué)習(xí)平臺(tái)核心不是簡單地放幾個(gè)課程視頻而是要把“學(xué)、練、問、反饋”這一整個(gè)閉環(huán)做出來。我拆開看這個(gè)項(xiàng)目實(shí)際包含了三類角色、六大業(yè)務(wù)模塊互動(dòng)能力貫穿始終。1.1 三類角色的業(yè)務(wù)圈學(xué)生端是這個(gè)平臺(tái)的使用主體功能路徑圍繞“學(xué)習(xí)”展開注冊(cè)登錄后可以瀏覽課程列表、按關(guān)鍵詞搜索課程、進(jìn)入課程詳情頁查看章節(jié)大綱點(diǎn)進(jìn)具體課時(shí)開始學(xué)習(xí)學(xué)習(xí)過程中可以記錄進(jìn)度學(xué)完可以在評(píng)論區(qū)提問或回答別人的問題最后還能參加課程測驗(yàn)并查看得分。這些動(dòng)作在代碼里分別落到了course、lesson、study_record、comment、question、exam_record這些數(shù)據(jù)表上。教師端的價(jià)值在“內(nèi)容生產(chǎn)”教師可以創(chuàng)建課程、維護(hù)課程章節(jié)、上傳課時(shí)內(nèi)容還可以給自己的課程配置測驗(yàn)題目查看學(xué)生的測驗(yàn)成績和課程評(píng)論。這里有一個(gè)很容易被忽略的點(diǎn)——教師端的課程和章節(jié)操作幾乎都是對(duì)course表和lesson表的增刪改查但它考驗(yàn)的是“一對(duì)多”關(guān)系處理一個(gè)課程下面掛多個(gè)章節(jié)章節(jié)又要排序所以表設(shè)計(jì)里一定要有course_id外鍵和sort排序字段。管理員端負(fù)責(zé)“平臺(tái)治理”用戶管理禁用/啟用賬號(hào)、課程上下架審核、系統(tǒng)公告發(fā)布。這部分對(duì)應(yīng)的是后臺(tái)管理頁面通常做成獨(dú)立的Management視圖。管理員賬號(hào)在SQL腳本的初始數(shù)據(jù)里預(yù)置一般是admin開頭的賬號(hào)拿到源碼后第一件事就去初始化腳本里確認(rèn)這個(gè)默認(rèn)賬號(hào)的密碼后面所有調(diào)試都靠它。1.2 所謂的“互動(dòng)”到底落在哪些功能上這個(gè)項(xiàng)目叫“在線互動(dòng)學(xué)習(xí)”互動(dòng)不能只是一個(gè)概念得具體到功能上。我梳理下來互動(dòng)主要體現(xiàn)在四個(gè)地方學(xué)習(xí)記錄與進(jìn)度追蹤學(xué)生學(xué)完某一課時(shí)后前端會(huì)向后端上報(bào)一條study_record記錄下次再進(jìn)來能接著看。這屬于“人與內(nèi)容的互動(dòng)”也是在線學(xué)習(xí)平臺(tái)比普通視頻網(wǎng)站多出來的核心價(jià)值。課程評(píng)論與回復(fù)學(xué)生可以在課程詳情頁發(fā)表評(píng)論也能回復(fù)別人的評(píng)論。這條鏈路涉及comment表自關(guān)聯(lián)的parent_id字段是典型的“父子關(guān)系”設(shè)計(jì)答辯時(shí)很容易被問到。在線測驗(yàn)與即時(shí)判分學(xué)生答完題提交答案后端按標(biāo)準(zhǔn)答案計(jì)算得分并返回結(jié)果。這里涉及question表中answer字段和exam_record表中answers字段的JSON格式存儲(chǔ)。教師與學(xué)生的間接反饋教師能看到自己課程的評(píng)論和測驗(yàn)成績分布從而調(diào)整內(nèi)容。雖然技術(shù)上只是普通的查詢統(tǒng)計(jì)但業(yè)務(wù)上講這就是反饋閉環(huán)。對(duì)于一個(gè)畢設(shè)級(jí)別的 Java Web 項(xiàng)目來說這套模塊量剛剛好——既有常規(guī)的增刪改查又有業(yè)務(wù)關(guān)系上的關(guān)聯(lián)查詢還帶一點(diǎn)“在線”和“互動(dòng)”的特色不至于太復(fù)雜講不清楚也不會(huì)簡單得像課程作業(yè)。2. 選型與工程結(jié)構(gòu)SpringBootVue 這套組合是怎么分工的很多人拿到項(xiàng)目源碼后第一件事就是找啟動(dòng)類這其實(shí)不對(duì)。前后端分離項(xiàng)目第一步應(yīng)該是先看目錄結(jié)構(gòu)分清楚哪部分是后端、哪部分是前端、哪部分是數(shù)據(jù)庫腳本。搞清楚了這三個(gè)目錄的位置整個(gè)項(xiàng)目的主心骨就抓住了。2.1 為什么這個(gè)選題要用前后端分離而不是傳統(tǒng)JSP拿傳統(tǒng)單體JSP方案和SpringBootVue方案做對(duì)比選擇邏輯就很直觀對(duì)比維度傳統(tǒng)JSP單體SpringBootVue前后端分離頁面渲染服務(wù)端動(dòng)態(tài)渲染Java代碼和HTML混在一起前端獨(dú)立渲染后端只提供JSON數(shù)據(jù)接口職責(zé)邊界一個(gè)Tomcat里既要寫接口又要寫頁面后端跑接口前端跑SPA互不干擾調(diào)試方式改頁面要重啟整個(gè)Web服務(wù)前端熱更新后端只改接口時(shí)不用動(dòng)前端答辯話題點(diǎn)相對(duì)單薄技術(shù)亮點(diǎn)少可以深入講RESTful、跨域、鑒權(quán)、SPA路由部署復(fù)雜度一個(gè)WAR包后端JAR包 前端靜態(tài)文件各管各的SpringBoot負(fù)責(zé)出接口、處理業(yè)務(wù)邏輯、讀寫數(shù)據(jù)庫Vue負(fù)責(zé)頁面展示、交互狀態(tài)、用戶操作。兩者通過HTTP接口通信數(shù)據(jù)格式統(tǒng)一用JSON。這個(gè)分離方式讓項(xiàng)目結(jié)構(gòu)清爽也很貼近公司里真實(shí)項(xiàng)目的協(xié)作模式。對(duì)一個(gè)Java Web畢設(shè)來說選這套組合意味著你可以把“前后端分離架構(gòu)”作為答辯里的第一個(gè)技術(shù)亮點(diǎn)不用靠一堆花哨框架堆砌。2.2 拿到源碼后快速定位關(guān)鍵代碼的方法源碼解壓后通常會(huì)看到類似這樣的頂層目錄結(jié)構(gòu)online-study/ ├── backend/ # SpringBoot后端工程 │ ├── pom.xml # 后端Maven依賴與SpringBoot版本 │ ├── src/main/java/ │ ├── src/main/resources/ │ │ ├── application.yml │ │ └── mapper/ │ └── sql/ │ ├── study_platform.sql # 完整SQL腳本 │ └── README.txt ├── frontend/ # Vue前端工程 │ ├── package.json │ ├── vue.config.js # 開發(fā)代理配置 │ └── src/ │ ├── api/ # 所有后端接口封裝 │ ├── router/ # 前端路由定義 │ ├── views/ # 頁面組件 │ ├── components/ │ └── utils/ └── docs/ └── 接口文檔.md # 接口說明文檔后端工程的包結(jié)構(gòu)拿過來后重點(diǎn)看這幾個(gè)位置com.example.study ├── controller/ # 接口入口從這層開始讀代碼最容易理解業(yè)務(wù) ├── service/ # 業(yè)務(wù)邏輯層 ├── mapper/ # 數(shù)據(jù)訪問層MyBatis的Mapper接口 ├── entity/ # 數(shù)據(jù)庫實(shí)體類 ├── common/ # 統(tǒng)一返回體、全局異常、鑒權(quán)工具 └── config/ # 配置類跨域、攔截器、文件上傳等前端工程的src目錄依次看api、router、views、utils四層。先看api里發(fā)了哪些請(qǐng)求再看router里頁面怎么跳轉(zhuǎn)然后看views里每個(gè)頁面對(duì)應(yīng)哪個(gè)組件最后看utils里axios封裝了什么。3. 數(shù)據(jù)庫設(shè)計(jì)與SQL腳本落地課程、學(xué)習(xí)記錄、評(píng)論這些表是怎么串起來的這個(gè)項(xiàng)目的靈魂在數(shù)據(jù)庫。只要把表關(guān)系看明白后端寫的那些service邏輯基本上就是照著表關(guān)系做CRUD和條件查詢。我按業(yè)務(wù)模塊把核心表拆開來講。3.1 核心表結(jié)構(gòu)拆解我用一套典型的表設(shè)計(jì)來說明不同版本源碼里表名可能略有差異但核心字段不會(huì)跳出這個(gè)框架表名業(yè)務(wù)含義核心字段說明user用戶表id, username, password, role, real_name, avatar, statusrole區(qū)分管理員/教師/學(xué)生status控制賬號(hào)是否禁用course課程表id, title, cover, description, teacher_id, category, statusteacher_id關(guān)聯(lián)用戶表status控制上下架lesson課時(shí)表id, course_id, title, video_url, content, sort每門課程下掛多個(gè)課時(shí)sort控制先后順序study_record學(xué)習(xí)記錄表id, user_id, course_id, lesson_id, progress, create_time記錄“誰學(xué)了哪個(gè)課時(shí)”唯一索引防重復(fù)comment評(píng)論表id, user_id, course_id, content, parent_id, create_timeparent_id為空表示頂級(jí)評(píng)論不為空表示回復(fù)question試題表id, course_id, type, stem, options, answer, scoreoptions用JSON存answer存標(biāo)準(zhǔn)答案exam_record測驗(yàn)記錄表id, user_id, course_id, answers, score, create_timeanswers用JSON存避免額外建答題明細(xì)表幾個(gè)關(guān)鍵設(shè)計(jì)決策建議答辯前一定要搞清楚為什么用role字段區(qū)分角色而不是建三張用戶表因?yàn)橛脩舻墓矊傩杂脩裘?、密碼、頭像是一樣的拆成多張表既冗余又讓登錄邏輯變復(fù)雜。用一個(gè)role字段配合后端的角色校驗(yàn)是最常見也最清晰的做法。comment表的parent_id自關(guān)聯(lián)這是評(píng)論回復(fù)功能的經(jīng)典設(shè)計(jì)。根評(píng)論的parent_id為NULL回復(fù)某條評(píng)論時(shí)把parent_id指向那條評(píng)論的id。注意檢索時(shí)不能只查一層通常做法是查出頂級(jí)評(píng)論后再批量查所有回復(fù)用Java在內(nèi)存里組裝成樹形結(jié)構(gòu)。study_record表要建聯(lián)合唯一索引比如UNIQUE KEY(user_id, lesson_id)保證同一用戶對(duì)同一課時(shí)的學(xué)習(xí)記錄只有一條重復(fù)提交時(shí)用ON DUPLICATE KEY UPDATE更新學(xué)習(xí)時(shí)間。這個(gè)細(xì)節(jié)是防止用戶反復(fù)打開視頻產(chǎn)生垃圾數(shù)據(jù)的關(guān)鍵。3.2 初始化SQL腳本的正確打開方式拿到SQL腳本后不要“雙擊導(dǎo)入”就跑先花兩分鐘看三樣?xùn)|西建庫語句、默認(rèn)賬號(hào)、測試數(shù)據(jù)范圍。腳本開頭通常長這樣CREATE DATABASE IF NOT EXISTS study_platform DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE study_platform; -- 用戶表 CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(100) NOT NULL, role varchar(10) DEFAULT student, real_name varchar(50) DEFAULT NULL, avatar varchar(255) DEFAULT NULL, status tinyint DEFAULT 1, create_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4;導(dǎo)入的時(shí)候我習(xí)慣用命令行或SQL管理工具的“運(yùn)行SQL文件”功能。注意三個(gè)細(xì)節(jié)字符集必須是utf8mb4否則中文評(píng)論和username會(huì)亂碼導(dǎo)入前確認(rèn)MySQL版本兼容MySQL5.7和8.0大體通用但個(gè)別字段類型在8.0里行為略有差異導(dǎo)入完成后立刻執(zhí)行一條SELECT驗(yàn)證數(shù)據(jù)條數(shù)比如SELECT COUNT(*) FROM user;不要直接悶頭啟動(dòng)項(xiàng)目然后發(fā)現(xiàn)查不到賬號(hào)。默認(rèn)數(shù)據(jù)里通常會(huì)有一個(gè)管理員賬號(hào)、一個(gè)教師賬號(hào)、一個(gè)學(xué)生賬號(hào)、幾門測試課程、幾十道測試題。這些賬號(hào)都是拿來做聯(lián)調(diào)和演示的。建議用測試賬號(hào)跑通全流程后再考慮改密碼否則中途改掉了默認(rèn)密碼前端登錄頁一測全是密碼錯(cuò)誤你還以為代碼有bug。4. 后端接口與前端對(duì)接圍繞“一份接口文檔”把主流程走通這個(gè)項(xiàng)目配套的接口文檔說實(shí)在話信息量不小。它按模塊列出了每個(gè)接口的路徑、請(qǐng)求方式、入?yún)?、出參以及錯(cuò)誤碼含義。很多人拿到后不知道從哪里下手我的建議是不要按接口順序一行行讀而是先看“統(tǒng)一約定”再按一條業(yè)務(wù)主流程串起來讀。4.1 統(tǒng)一返回體、JWT鑒權(quán)、全局異常怎么設(shè)計(jì)接口文檔里幾乎每個(gè)接口的出參都長一個(gè)樣{ code: 200, message: success, data: { } }這個(gè)統(tǒng)一返回體是后端設(shè)計(jì)的核心約定Java里通常對(duì)應(yīng)一個(gè)泛型類Result 。code為200表示成功401表示未登錄或token失效500表示服務(wù)端異常。設(shè)計(jì)成統(tǒng)一結(jié)構(gòu)前端axios響應(yīng)攔截器就只需要判斷code不需要對(duì)每個(gè)接口單獨(dú)處理。再看鑒權(quán)設(shè)計(jì)。項(xiàng)目接口分為兩類公開接口注冊(cè)、登錄、課程列表、課程詳情和受保護(hù)接口提交學(xué)習(xí)記錄、發(fā)布評(píng)論、參加測驗(yàn)、后臺(tái)管理。受保護(hù)接口要求請(qǐng)求頭攜帶Authorization: Bearer 這個(gè)token是登錄成功后后端簽發(fā)的JWT字符串。后端的處理方式是寫一個(gè)攔截器HandlerInterceptor放行登錄、注冊(cè)等少數(shù)白名單路徑其余路徑統(tǒng)一解析token解析失敗直接返回401成功則把用戶信息放進(jìn)ThreadLocal或Request上下文供后續(xù)使用。密碼存儲(chǔ)方面正經(jīng)項(xiàng)目不會(huì)明文存密碼至少是MD5加鹽更規(guī)范的做法是用BCrypt加密。如果你是拿現(xiàn)成SQL腳本初始化的項(xiàng)目里面預(yù)置賬號(hào)的密碼一定是已經(jīng)加密過的字符串直接用就行。4.2 在線學(xué)習(xí)主流程的接口調(diào)用鏈我建議你按下面這條鏈路把接口文檔串一遍這也是答辯演示時(shí)最自然的業(yè)務(wù)故事線步驟接口說明1POST /api/auth/login學(xué)生用賬號(hào)密碼登錄返回token和用戶信息2GET /api/course/list?page1size8分頁查課程列表展示課程封面3GET /api/course/detail/{courseId}進(jìn)入課程詳情返回章節(jié)列表和教師信息4GET /api/lesson/detail/{lessonId}獲取課時(shí)詳情返回視頻地址和課時(shí)內(nèi)容5POST /api/study/record上報(bào)學(xué)習(xí)記錄傳courseId和lessonId6POST /api/comment/add發(fā)表評(píng)論傳courseId、content、parentId7GET /api/comment/list?courseId1加載某門課程下的全部評(píng)論8GET /api/exam/list?courseId1獲取課程測驗(yàn)題目9POST /api/exam/submit提交答案后端自動(dòng)判分并返回成績從第5步開始請(qǐng)求都要帶token。這里有個(gè)細(xì)節(jié)值得注意學(xué)習(xí)記錄的提交時(shí)機(jī)。**前端一般會(huì)在視頻播放到某個(gè)進(jìn)度條觸發(fā)“記進(jìn)度”事件或者在離開課時(shí)頁面時(shí)上報(bào)一次而不是播放一幀就上報(bào)一次。**接口文檔里肯定沒有寫這個(gè)前端策略但你答辯時(shí)如果能主動(dòng)講出來會(huì)給評(píng)委留下“真的考慮過用戶體驗(yàn)”的印象。4.3 前端Axios封裝和路由守衛(wèi)怎么配合前端和后端咬合最緊的就是axios封裝和Vue Router守衛(wèi)。最常見的axios配置長這樣// frontend/src/utils/request.js import axios from axios const request axios.create({ baseURL: /api, // 開發(fā)環(huán)境走vue.config.js代理 timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code 401) { localStorage.removeItem(token) window.location.href /login return Promise.reject(new Error(未登錄)) } return res }, error Promise.reject(error) ) export default request這段代碼的核心邏輯請(qǐng)求前自動(dòng)從localStorage取token塞進(jìn)請(qǐng)求頭響應(yīng)時(shí)統(tǒng)一判斷401并踢回登錄頁。有了這層封裝每個(gè)頁面里調(diào)接口就不需要關(guān)心鑒權(quán)細(xì)節(jié)了。路由守衛(wèi)負(fù)責(zé)的是“沒有token不能訪問個(gè)人中心/教師后臺(tái)”這類頁面級(jí)控制// frontend/src/router/index.js router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })頁面級(jí)路由守衛(wèi)和axios請(qǐng)求攔截是兩道防線頁面進(jìn)不去接口也調(diào)不動(dòng)。答辯時(shí)如果能順帶提一句“前端判斷是為了體驗(yàn)后端攔截才是安全底線”說明你是真的理解了權(quán)限控制。5. 從0到1本地跑通環(huán)境版本、配置修改、啟動(dòng)順序與典型報(bào)錯(cuò)接下來是實(shí)操環(huán)節(jié)。這個(gè)SpringBootVue項(xiàng)目想在本機(jī)跑起來最重要的是先對(duì)齊環(huán)境版本。我見過太多人因?yàn)镴DK版本或Node版本不對(duì)折騰半天還沒看到登錄頁。5.1 推薦的環(huán)境組合我實(shí)際測試下來下面這套組合最省心組件推薦版本說明JDK1.8 或 11SpringBoot 2.x系列都兼容優(yōu)先1.8Maven3.6優(yōu)先3.6.3新版3.8也okMySQL5.7 或 8.08.0注意時(shí)區(qū)和認(rèn)證插件問題Node.js14.xVue2 / 16.xVue3看package.json里的vue版本IDEIDEA / VSCode均可后端建議IDEA前端VSCode夠用檢查版本的命令敲一遍java -version、mvn -v、node -v、npm -v、mysql --version。任何一項(xiàng)對(duì)不上優(yōu)先調(diào)整而不是硬跑。5.2 改配置、導(dǎo)數(shù)據(jù)、兩頭啟動(dòng)的完整步驟我按照自己反復(fù)試驗(yàn)過的順序給你整理了一份可以直接照著操作的步驟清單打開后端工程的application.yml確認(rèn)數(shù)據(jù)庫連接配置。重點(diǎn)關(guān)注這幾行spring: datasource: url: jdbc:mysql://localhost:3306/study_platform?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你自己的數(shù)據(jù)庫密碼 driver-class-name: com.mysql.cj.jdbc.Driver如果你用的是MySQL 8.0url里的serverTimezoneAsia/Shanghai必須保留否則連接會(huì)報(bào)時(shí)區(qū)錯(cuò)誤。如果MySQL 5.7的驅(qū)動(dòng)版本較老driver-class-name可能是com.mysql.jdbc.Driver別和8.0搞混。導(dǎo)入SQL腳本。先手動(dòng)創(chuàng)建數(shù)據(jù)庫或直接執(zhí)行腳本里的CREATE DATABASE語句然后選擇study_platform庫運(yùn)行腳本。腳本執(zhí)行無報(bào)錯(cuò)后查詢幾個(gè)核心表的記錄數(shù)確認(rèn)有數(shù)據(jù)。啟動(dòng)后端。兩種方式任選在IDEA里直接Run主啟動(dòng)類或者命令行執(zhí)行mvn spring-boot:run。看到“Started Application”日志就說明啟動(dòng)成功默認(rèn)端口看配置文件里的server.port常見8080。安裝前端依賴。進(jìn)入frontend目錄執(zhí)行npm install。這里是最容易卡住的地方——如果裝的是Vue2項(xiàng)目依賴?yán)锟赡苡衝ode-sass這東西在Node高版本下經(jīng)常編譯失敗。遇到這種情況優(yōu)先用Node 14版本或者把依賴?yán)锏膎ode-sass替換為sass。如果npm install速度慢可以先配置國內(nèi)npm鏡像。啟動(dòng)前端。執(zhí)行npm run serve看到“Compiled successfully”后就說明前端起來了。前端默認(rèn)端口一般是8081或8080如果和后端端口沖突vue.config.js或package.json里會(huì)改端口建議前后端用不同端口避免混淆。瀏覽器訪問前端地址用SQL腳本里的測試賬號(hào)登錄開始聯(lián)調(diào)。這里建議先別全頁面點(diǎn)一遍就按第4節(jié)那條業(yè)務(wù)鏈路走接口通了再擴(kuò)展測試其他功能。5.3 三個(gè)必踩的坑我自己在這個(gè)項(xiàng)目上踩得最深的坑有三個(gè)寫出來給你提個(gè)醒第一個(gè)坑前端請(qǐng)求404或連不上后端。這多半是vue.config.js里的代理沒生效。開發(fā)代理的前端配置長這樣// frontend/vue.config.js module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }這個(gè)配置的含義是前端頁面里所有以/api開頭的請(qǐng)求都會(huì)被開發(fā)服務(wù)器轉(zhuǎn)發(fā)到后端8080端口。配置沒問題還404大概率是沒重啟npm run serve。代理配置改完必須重啟前端服務(wù)熱更新不會(huì)重載devServer配置。第二個(gè)坑登錄接口報(bào)401或密碼錯(cuò)誤。優(yōu)先看數(shù)據(jù)庫里有沒有導(dǎo)入初始用戶數(shù)據(jù)其次看密碼字段是否是加密串。有些項(xiàng)目初始化腳本會(huì)插入一條明文密碼的記錄但后端登錄邏輯用的是BCrypt校驗(yàn)明文密碼永遠(yuǎn)校驗(yàn)不過去。第三個(gè)坑課程視頻加載不出來。這是最容易讓演示翻車的問題。SQL腳本里的video_url字段可能指向一個(gè)已經(jīng)失效的示例地址或者本地沒有對(duì)應(yīng)視頻文件。解決方法是準(zhǔn)備一個(gè)本地測試視頻文件放到后端靜態(tài)資源目錄或替換video_url確保演示時(shí)能正常播放。別忘了這個(gè)細(xì)節(jié)否則答辯現(xiàn)場黑屏很尷尬。6. 答辯級(jí)的演示準(zhǔn)備怎么把這個(gè)項(xiàng)目講得像自己親手寫的項(xiàng)目跑通只是第一步畢設(shè)答辯的成敗在于你怎么講。我見過很多同學(xué)功能全做了但講的時(shí)候東點(diǎn)一下西點(diǎn)一下評(píng)委完全抓不住重點(diǎn)。下面是我整理的高效演示和常見追問思路。6.1 按“業(yè)務(wù)閉環(huán)”演示而不是按頁面菜單演示推薦的演示路線是三個(gè)人物故事線管理員線登錄管理后臺(tái)演示用戶管理禁用/啟用一個(gè)學(xué)生賬號(hào)、課程上下架、發(fā)布公告。目的是展示平臺(tái)的“可控性”。教師線登錄教師賬號(hào)進(jìn)入課程管理新建一門課程添加一個(gè)課時(shí)給課程配兩道測試題。目的是展示內(nèi)容生產(chǎn)鏈路。學(xué)生線登錄學(xué)生賬號(hào)瀏覽課程列表搜索剛才新創(chuàng)建的課程進(jìn)入課時(shí)學(xué)習(xí)上報(bào)學(xué)習(xí)記錄發(fā)表一條評(píng)論參加在線測驗(yàn)并查看得分。這是整場演示的高潮一定要把“互動(dòng)”和“反饋”兩個(gè)詞掛在嘴邊。這條線走完評(píng)委自然能看明白“誰在什么場景下用這個(gè)平臺(tái)解決什么問題”。不要按菜單順序從頭點(diǎn)到尾那是驗(yàn)收系統(tǒng)不是講解項(xiàng)目。6.2 高頻追問與應(yīng)對(duì)思路答辯時(shí)被問到技術(shù)細(xì)節(jié)不要慌圍繞項(xiàng)目實(shí)際設(shè)計(jì)回答就行為什么用JWT而不用Session因?yàn)槠脚_(tái)是前后端分離架構(gòu)后端接口可能被多個(gè)端瀏覽器管理端、學(xué)生端調(diào)用JWT無狀態(tài)、可攜帶用戶信息適合跨域場景。Session在分離架構(gòu)下要處理跨域Cookie和會(huì)話同步成本更高。Token過期了怎么辦前端axios響應(yīng)攔截器捕獲401清除本地token并跳轉(zhuǎn)登錄頁重新登錄。更完整的方案是加刷新token接口但這個(gè)項(xiàng)目里演示登錄跳轉(zhuǎn)就夠了。密碼是明文存的嗎不是。項(xiàng)目里用了BCrypt加密登錄時(shí)用BCrypt校驗(yàn)數(shù)據(jù)庫里存的是加密后的哈希串即使數(shù)據(jù)庫泄露也無法反推原始密碼。學(xué)習(xí)進(jìn)度是怎么記錄的study_record表以u(píng)serId和lessonId做聯(lián)合唯一索引用戶學(xué)習(xí)時(shí)提交記錄存在則更新學(xué)習(xí)時(shí)間不存在則插入。這樣既能實(shí)現(xiàn)“上次學(xué)到哪”的恢復(fù)又不會(huì)因?yàn)榉磸?fù)進(jìn)入課時(shí)產(chǎn)生大量重復(fù)數(shù)據(jù)。評(píng)論的回復(fù)是怎么組織的根評(píng)論parent_id為空回復(fù)時(shí)把parent_id設(shè)為目標(biāo)評(píng)論id。查詢時(shí)先查出根評(píng)論再批量查該課程下所有回復(fù)在Java層組裝成嵌套結(jié)構(gòu)。6.3 三條讓它“顯得更完整”的擴(kuò)展方向答辯收尾環(huán)節(jié)評(píng)委經(jīng)常會(huì)問“你覺得這個(gè)項(xiàng)目還能怎么改進(jìn)”。準(zhǔn)備兩三個(gè)具體的擴(kuò)展點(diǎn)比空泛說“繼續(xù)完善功能”有說服力得多引入Redis緩存熱門課程列表和課程詳情降低數(shù)據(jù)庫壓力。這個(gè)點(diǎn)很好講因?yàn)檎n程訪問是典型的讀多寫少場景。用WebSocket實(shí)現(xiàn)實(shí)時(shí)的在線討論或彈幕互動(dòng)讓“互動(dòng)”從異步的評(píng)論區(qū)升級(jí)到實(shí)時(shí)對(duì)話。視頻管理從本地靜態(tài)文件遷移到云端對(duì)象存儲(chǔ)避免服務(wù)器存儲(chǔ)空間和帶寬成為瓶頸。這個(gè)點(diǎn)能體現(xiàn)你對(duì)實(shí)際部署環(huán)境的思考。如果時(shí)間允許我甚至建議你提前把Redis方案在本地做一個(gè)最小實(shí)現(xiàn)——不用全套改造只需要把課程列表查詢加一層緩存答辯時(shí)告訴評(píng)委“我實(shí)際跑過緩存命中后接口響應(yīng)時(shí)間明顯下降”這個(gè)項(xiàng)目的高度立刻就不一樣了。