提交與批改系統(tǒng)畢設(shè):SpringBoot+Vue全棧實現(xiàn)與數(shù)據(jù)庫腳本設(shè)計)
簡介這是一套基于JavaWeb的作業(yè)提交與批改系統(tǒng)項目源碼面向計算機相關(guān)專業(yè)正在做畢設(shè)的學(xué)生以及需要項目實戰(zhàn)練習的Java學(xué)習者可直接作為畢業(yè)設(shè)計使用。系統(tǒng)采用B/S結(jié)構(gòu)后臺基于JSP、Servlet與JDBC實現(xiàn)以MySQL作為數(shù)據(jù)庫開發(fā)環(huán)境為JDK、Eclipse與Tomcat并劃分管理員、教師、學(xué)生三種角色。功能上覆蓋班級、教師、學(xué)生、公告、留言、資料及系統(tǒng)管理七大模塊支持信息的錄入、查詢、刪除與打印等操作界面美觀、操作簡單具備較高的實際應(yīng)用價值。資源包共3個文件包含項目源碼壓縮包、數(shù)據(jù)庫腳本sql文件以及項目說明txt文檔整體約4.51MB源碼與腳本均已嚴格調(diào)試可正常運行。目前已有3583人學(xué)習下載適合希望快速搭建作業(yè)管理類系統(tǒng)、理解JavaWeb分層開發(fā)與數(shù)據(jù)庫設(shè)計的讀者參考使用。1. 從一份“能跑起來”的作業(yè)提交與批改系統(tǒng)說起很多同學(xué)做畢設(shè)時選題定成“基于JavaWeb的作業(yè)提交與批改系統(tǒng)”第一反應(yīng)是去搜“javaweb項目完整案例mysql”然后復(fù)制一份代碼改改界面就交差。但真正答辯時被問到“學(xué)生提交的作業(yè)文件怎么存”“教師批改狀態(tài)怎么流轉(zhuǎn)”“數(shù)據(jù)庫腳本為什么這樣設(shè)計”往往答不上來。這個標題背后其實是一套完整的業(yè)務(wù)閉環(huán)學(xué)生上傳作業(yè)、系統(tǒng)記錄提交狀態(tài)、教師下載批閱、回填分數(shù)與評語、學(xué)生查看結(jié)果。它適合計算機畢設(shè)選題中偏管理信息系統(tǒng)方向的同學(xué)也適合想用SpringBootVue畢設(shè)練手全棧開發(fā)的人。核心難點不在頁面多漂亮而在文件存儲策略、狀態(tài)機設(shè)計和數(shù)據(jù)庫表關(guān)系是否經(jīng)得起追問。下面按理論選型、庫表設(shè)計、后端實現(xiàn)、前端聯(lián)調(diào)、部署排錯的順序把一套可復(fù)現(xiàn)的方案講清楚。2. 作業(yè)提交與批改系統(tǒng)的技術(shù)選型與數(shù)據(jù)庫腳本設(shè)計2.1 為什么用 Spring Boot MyBatis-Plus 而不是純 Servlet標題寫的是 JavaWeb但 2024 年之后的畢設(shè)如果還用 JSP Servlet 裸寫代碼量會膨脹到難以維護。常見做法是保留 JavaWeb 的 MVC 分層思想用 Spring Boot 做容器和自動配置MyBatis-Plus 做數(shù)據(jù)訪問。這樣既符合“JavaWeb”的范疇又能讓答辯老師看到你對主流框架的掌握。選型理由有三點第一Spring Boot 內(nèi)嵌 Tomcatidea運行javaweb項目配置時不需要單獨裝 Tomcat第二MyBatis-Plus 的BaseMapper能省掉大量單表 CRUD第三前后端分離后 Vue 負責交互Java 端只返回 JSON職責清晰。依賴版本建議鎖定 Spring Boot 2.7.x 和 MyBatis-Plus 3.5.xJDK 用 8 或 11MySQL 用 5.7 或 8.0。不要盲目追 Spring Boot 3.x因為部分畢設(shè)環(huán)境里的 JDK 還是 8貿(mào)然升級會導(dǎo)致javax包名變成jakarta啟動直接報錯。2.2 數(shù)據(jù)庫腳本的核心表與字段說明數(shù)據(jù)庫腳本是畢設(shè)查重和答辯的重點。作業(yè)提交與批改系統(tǒng)至少需要五張表用戶表、課程表、作業(yè)表、提交記錄表、批改記錄表。下面給出建表腳本的關(guān)鍵部分可直接在 MySQL 中執(zhí)行。-- 用戶表區(qū)分學(xué)生、教師、管理員 CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登錄名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密, real_name VARCHAR(50) DEFAULT NULL, role TINYINT NOT NULL COMMENT 1學(xué)生 2教師 3管理員, class_id BIGINT DEFAULT NULL COMMENT 學(xué)生所屬班級, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 作業(yè)表教師發(fā)布的作業(yè)任務(wù) CREATE TABLE homework ( id BIGINT NOT NULL AUTO_INCREMENT, course_id BIGINT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT, deadline DATETIME NOT NULL COMMENT 截止時間, create_by BIGINT NOT NULL COMMENT 教師ID, status TINYINT DEFAULT 1 COMMENT 1進行中 0已截止, PRIMARY KEY (id), KEY idx_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 提交記錄表一個學(xué)生一份作業(yè)只有一條有效記錄 CREATE TABLE submission ( id BIGINT NOT NULL AUTO_INCREMENT, homework_id BIGINT NOT NULL, student_id BIGINT NOT NULL, file_path VARCHAR(255) DEFAULT NULL COMMENT 相對存儲路徑, file_name VARCHAR(255) DEFAULT NULL, submit_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0未批改 1已批改, score DECIMAL(5,1) DEFAULT NULL, comment VARCHAR(500) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_hw_stu (homework_id,student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;uk_hw_stu唯一索引是關(guān)鍵它保證同一個學(xué)生對同一份作業(yè)只能提交一次后續(xù)再交就是更新操作。file_path存相對路徑而不是絕對路徑方便遷移服務(wù)器。status字段驅(qū)動整個批改流程教師端查詢時只查status0的記錄。2.3 文件存儲路徑與上傳大小限制作業(yè)文件不能存進數(shù)據(jù)庫 BLOB否則備份和查詢都會變慢。常見做法是在application.yml里配置本地磁盤目錄并用日期分目錄避免單目錄文件過多。spring: servlet: multipart: max-file-size: 20MB max-request-size: 20MB file: upload-dir: /data/homework/upload/ allowed-ext: pdf,doc,docx,zip,rarmax-file-size限制單文件 20MB超過會拋MaxUploadSizeExceededException需要在全局異常處理器里捕獲并返回友好提示。allowed-ext做后綴白名單防止上傳可執(zhí)行文件。存儲時用homeworkId/studentId_時間戳.后綴命名既避免重名又便于追溯。3. 后端接口實現(xiàn)提交、批改與狀態(tài)流轉(zhuǎn)3.1 學(xué)生提交作業(yè)的接口與文件落盤邏輯學(xué)生提交作業(yè)的接口需要同時處理文件流和表單字段。用MultipartFile接收文件先校驗作業(yè)是否已截止再判斷是新增還是更新提交記錄。PostMapping(/submit) public Result submit(RequestParam Long homeworkId, RequestParam(file) MultipartFile file, HttpServletRequest request) { Long studentId getCurrentUserId(request); Homework hw homeworkMapper.selectById(homeworkId); // 截止時間校驗 if (hw.getDeadline().before(new Date())) { return Result.fail(已過截止時間無法提交); } // 后綴白名單 String ext FileUtil.getSuffix(file.getOriginalFilename()); if (!allowedExt.contains(ext)) { return Result.fail(不支持的文件類型); } // 按作業(yè)ID分目錄存儲 String dir uploadDir homeworkId /; String storeName studentId _ System.currentTimeMillis() . ext; File dest new File(dir storeName); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } try { file.transferTo(dest); } catch (IOException e) { return Result.fail(文件保存失敗); } // 存在則更新不存在則插入 Submission sub submissionMapper.selectOne( new LambdaQueryWrapperSubmission() .eq(Submission::getHomeworkId, homeworkId) .eq(Submission::getStudentId, studentId)); if (sub null) { sub new Submission(); sub.setHomeworkId(homeworkId); sub.setStudentId(studentId); sub.setFilePath(homeworkId / storeName); sub.setFileName(file.getOriginalFilename()); sub.setStatus(0); submissionMapper.insert(sub); } else { sub.setFilePath(homeworkId / storeName); sub.setFileName(file.getOriginalFilename()); sub.setSubmitTime(new Date()); sub.setStatus(0); // 重新提交后回到未批改 submissionMapper.updateById(sub); } return Result.ok(提交成功); }邏輯說明先查作業(yè)是否存在、是否截止再校驗后綴然后落盤。落盤目錄按homeworkId分文件夾文件名用studentId_時間戳保證唯一。數(shù)據(jù)庫層面用selectOne判斷是否已有記錄有則更新無則插入。注意重新提交后要把status重置為 0否則教師端看不到新版本。參數(shù)homeworkId和file是必傳的file為空時 Spring 會直接返回 400前端需要做非空校驗。3.2 教師批改接口與分數(shù)回填教師批改時傳入提交記錄 ID、分數(shù)和評語后端更新submission表并置status1。PostMapping(/grade) public Result grade(RequestBody GradeDTO dto) { Submission sub submissionMapper.selectById(dto.getSubmissionId()); if (sub null) { return Result.fail(提交記錄不存在); } if (dto.getScore() null || dto.getScore() 0 || dto.getScore() 100) { return Result.fail(分數(shù)需在0到100之間); } sub.setScore(dto.getScore()); sub.setComment(dto.getComment()); sub.setStatus(1); submissionMapper.updateById(sub); return Result.ok(批改完成); }GradeDTO包含submissionId、score、comment三個字段。分數(shù)用BigDecimal或Double接收數(shù)據(jù)庫字段是DECIMAL(5,1)支持一位小數(shù)。批改后學(xué)生端查詢時根據(jù)status顯示“已批改”并展示分數(shù)和評語。這里不要用物理刪除保留歷史記錄便于申訴。3.3 分頁查詢與多條件篩選教師端需要按課程、作業(yè)、狀態(tài)篩選提交記錄學(xué)生端需要查看自己的提交歷史。用 MyBatis-Plus 的分頁插件實現(xiàn)。GetMapping(/page) public Result page(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, Long homeworkId, Integer status) { PageSubmissionVO p new Page(page, size); LambdaQueryWrapperSubmission wrapper new LambdaQueryWrapper(); wrapper.eq(homeworkId ! null, Submission::getHomeworkId, homeworkId) .eq(status ! null, Submission::getStatus, status) .orderByDesc(Submission::getSubmitTime); return Result.ok(submissionMapper.selectPage(p, wrapper)); }page和size控制分頁homeworkId和status為可選篩選條件。eq的第一個參數(shù)是布爾值條件為真時才拼接 SQL這是 MyBatis-Plus 的條件構(gòu)造技巧。排序按提交時間倒序最新提交的排前面。返回的SubmissionVO需要關(guān)聯(lián)查詢學(xué)生姓名和作業(yè)標題可以用Select自定義 SQL 或 XML 映射。4. 前端聯(lián)調(diào)與部署排錯從 IDEA 到可訪問4.1 Vue 端上傳組件與請求封裝前端用 Vue Element Plus 的el-upload組件設(shè)置:http-request自定義上傳避免默認行為不受控。// 自定義上傳攜帶 token 和 homeworkId const handleUpload async (options) { const formData new FormData(); formData.append(file, options.file); formData.append(homeworkId, currentHomeworkId.value); try { const res await axios.post(/api/submission/submit, formData, { headers: { Content-Type: multipart/form-data } }); if (res.data.code 200) { ElMessage.success(提交成功); loadList(); } else { ElMessage.error(res.data.msg); } } catch (e) { ElMessage.error(網(wǎng)絡(luò)異常); } };FormData自動設(shè)置multipart/form-data邊界不要手動設(shè)置Content-Type否則邊界丟失會導(dǎo)致后端解析失敗。homeworkId從當前作業(yè)詳情頁傳入。上傳成功后刷新列表讓狀態(tài)實時更新。4.2 IDEA 運行 JavaWeb 項目的常見配置坑在 IDEA 中運行 Spring Boot 項目時常見問題有三個。第一端口被占用改server.port即可第二數(shù)據(jù)庫連接失敗檢查application.yml中的url是否加了useSSLfalseserverTimezoneAsia/Shanghai第三文件上傳目錄沒有寫權(quán)限Linux 下用chmod 755授權(quán)。如果啟動報Table xxx doesnt exist說明數(shù)據(jù)庫腳本沒執(zhí)行完整用idea導(dǎo)出數(shù)據(jù)庫腳本功能反向核對表結(jié)構(gòu)。提示MySQL 8.0 的驅(qū)動類名是com.mysql.cj.jdbc.Driver5.7 是com.mysql.jdbc.Driver寫錯會報ClassNotFoundException。4.3 批改狀態(tài)不一致的排查思路如果教師批改后學(xué)生端仍顯示“未批改”按以下順序排查先查數(shù)據(jù)庫submission表的status字段是否已更新為 1再查后端接口是否返回了最新數(shù)據(jù)最后查前端是否緩存了舊列表。常見原因是前端用了keep-alive緩存頁面切換路由時沒有重新請求。解決辦法是在activated鉤子里重新加載數(shù)據(jù)或者關(guān)閉該頁面的緩存?,F(xiàn)象可能原因處理方式上傳報 413文件超過 max-file-size調(diào)大配置或前端限制批改后狀態(tài)不變前端緩存或事務(wù)未提交檢查Transactional和刷新邏輯文件下載 404file_path 拼接錯誤核對存儲根路徑與相對路徑登錄后接口 401token 未攜帶檢查 axios 攔截器5. 進階技巧用狀態(tài)機與軟刪除讓系統(tǒng)更經(jīng)得起答辯5.1 把 status 字段升級為顯式狀態(tài)機很多畢設(shè)的status只有 0 和 1答辯時被問“如果學(xué)生遲交怎么辦”“如果教師打回重做怎么辦”就卡住了。進階做法是定義狀態(tài)枚舉0 未提交、1 已提交待批改、2 已批改、3 打回重做、4 遲交。狀態(tài)流轉(zhuǎn)用服務(wù)層方法控制禁止前端直接改status。public enum SubmitStatus { NOT_SUBMITTED(0), SUBMITTED(1), GRADED(2), RETURNED(3), LATE(4); private final int code; SubmitStatus(int code) { this.code code; } public int getCode() { return code; } }在提交接口里判斷當前時間是否超過deadline超過則置為LATE教師端可以單獨篩選遲交記錄。打回重做時把狀態(tài)改為 3學(xué)生端顯示“需重新提交”再次提交后回到 1。這樣狀態(tài)流轉(zhuǎn)清晰答辯時能畫出完整的狀態(tài)圖。5.2 軟刪除與數(shù)據(jù)恢復(fù)批改記錄和提交記錄不建議物理刪除。在表里加deleted字段MyBatis-Plus 用TableLogic注解自動處理。TableLogic private Integer deleted;配置后deleteById實際執(zhí)行UPDATE ... SET deleted1查詢時自動追加deleted0。這樣誤刪后還能從數(shù)據(jù)庫恢復(fù)也符合畢設(shè)對數(shù)據(jù)完整性的要求。注意唯一索引uk_hw_stu在軟刪除下會沖突需要把deleted加入唯一索引或者用時間戳標記刪除。5.3 用接口文檔工具提升聯(lián)調(diào)效率前后端分離后接口字段對不齊是最大時間黑洞。建議引入 Knife4j 或 Swagger在 Controller 上加注解自動生成文檔。ApiOperation(學(xué)生提交作業(yè)) PostMapping(/submit) public Result submit(ApiParam(作業(yè)ID) RequestParam Long homeworkId, ApiParam(作業(yè)文件) RequestParam MultipartFile file) { ... }啟動后訪問/doc.html即可看到所有接口和參數(shù)說明前端不用反復(fù)問后端字段名。導(dǎo)出接口文檔 PDF 附在畢設(shè)論文附錄里也是加分項。最后提醒一點數(shù)據(jù)庫腳本要隨源碼一起提交并在 README 里寫清楚執(zhí)行順序和初始賬號否則答辯現(xiàn)場跑不起來前面做得再好也會大打折扣。本文還有配套的精品資源點擊獲取