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