基于Spring Boot与Vue的课程作业管理系统:从零到一实战开发指南

发布时间:2026/7/21 8:00:45
基于Spring Boot与Vue的课程作业管理系统:从零到一实战开发指南 在实际 Java Web 项目开发中尤其是毕业设计阶段一个功能完整、结构清晰、技术栈主流的课程作业管理系统是检验学习成果的绝佳实践。它要求开发者不仅要串联起 Java 后端、Vue 前端、Spring Boot 框架和 MySQL 数据库更要理解前后端分离架构下的数据流转、业务逻辑分层以及项目从零到一的构建过程。本文将以一个可运行的课程作业管理系统为例带你从零开始完成环境搭建、技术选型、核心模块开发、前后端联调直至最终部署。整个过程将严格遵循工程实践不仅提供可复现的代码和配置更会深入解释每一步的设计考量与常见陷阱确保你不仅能“做出来”更能“弄明白”。1. 理解系统需求与技术选型在动手编码之前明确系统边界和采用的技术栈是项目成功的基石。一个典型的课程作业管理系统其核心是围绕“教师发布作业、学生提交作业、教师批改作业”这一主线展开的。1.1 核心业务功能拆解系统主要涉及三类角色管理员、教师和学生。其核心功能模块如下用户管理模块实现不同角色的注册、登录、信息维护及权限控制如RBAC。这是系统安全与业务隔离的基础。课程管理模块管理员或教师可以创建课程并将学生加入到特定课程中建立师生关联关系。作业管理模块这是系统的核心。教师可以在课程下发布作业含标题、内容、截止日期、附件等学生查看并提交作业支持文件上传教师随后进行批改并给出分数和评语。通知与交互模块系统应能提供作业发布提醒、提交截止提醒、批改完成通知等基础消息功能。1.2 技术栈决策与组件说明根据项目标题和主流实践我们选择以下技术栈并解释其选型理由后端 (Java Spring Boot):Spring Boot 2.7.x: 作为项目基石它提供了自动配置、内嵌服务器和“约定大于配置”的理念能极大简化 Spring 应用的初始搭建和开发过程。选择 2.7.x 这个长期支持版本兼顾了稳定性和社区支持。Spring MVC: 处理 HTTP 请求实现 RESTful API是前后端分离架构中后端的标准形态。Spring Data JPA / MyBatis-Plus: 用于数据库操作。JPA 更面向对象开发速度快MyBatis-Plus 在 MyBatis 基础上增强了功能SQL 更灵活。本文将基于 MyBatis-Plus 进行演示因其在复杂查询和 SQL 优化上对开发者更友好。Spring Security: 处理认证登录和授权权限检查是保障系统安全的核心组件。前端 (Vue.js):Vue 3 Composition API: 作为当前主流版本提供了更好的 TypeScript 支持和更灵活的逻辑复用能力。Vue Router: 管理前端路由实现单页面应用SPA的无刷新跳转。Pinia / Vuex: 进行状态管理用于存储用户登录态、全局配置等跨组件数据。Element Plus / Ant Design Vue: UI 组件库能快速构建出美观且交互一致的管理界面。数据库 (MySQL 8.0): 成熟的关系型数据库事务支持完善社区活跃。使用 8.0 及以上版本可以获得更好的性能和新特性支持如窗口函数。构建与依赖管理:Maven: 管理 Java 项目依赖、构建生命周期。Node.js npm/pnpm/yarn: 管理前端项目依赖和构建。这个技术栈组合在就业市场具有很高的认可度完成该项目能有效巩固这些核心技能。2. 项目初始化与基础环境搭建一个清晰的项目结构是团队协作和后期维护的保障。我们将采用前后端分离的目录结构后端和前端作为两个独立的工程。2.1 后端 Spring Boot 项目初始化使用 Spring Initializr (start.spring.io) 或 IDE (如 IntelliJ IDEA) 创建项目。项目信息:Project: MavenLanguage: JavaSpring Boot: 2.7.18 (LTS版本)Group:com.exampleArtifact:coursework-backendPackaging: JarJava: 11 或 17依赖选择:Spring WebMyBatis FrameworkMySQL DriverLombok (减少样板代码)Spring Security (可选初期可简化)生成项目后其核心目录结构应如下coursework-backend/ ├── src/ │ ├── main/ │ │ ├── java/com/example/coursework/ │ │ │ ├── CourseworkApplication.java # 启动类 │ │ │ ├── config/ # 配置类如MyBatis-Plus, Security, Cors │ │ │ ├── controller/ # 控制器层接收请求 │ │ │ ├── service/ # 业务逻辑层 │ │ │ ├── mapper/ # MyBatis Mapper接口层 │ │ │ ├── entity/ # 实体类对应数据库表 │ │ │ └── dto/ # 数据传输对象 │ │ └── resources/ │ │ ├── application.yml # 主配置文件 │ │ └── mapper/ # MyBatis XML映射文件如果用XML方式 │ └── test/ # 测试代码 └── pom.xml # Maven依赖管理关键配置application.yml:server: port: 8080 servlet: context-path: /api spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/coursework_db?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发时开启SQL日志 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名 logic-delete-value: 1 logic-not-delete-value: 0 mapper-locations: classpath*:/mapper/**/*.xml注意serverTimezoneAsia/Shanghai对于避免时区导致的日期时间问题至关重要。生产环境务必移除log-impl配置或改为文件日志。2.2 前端 Vue 项目初始化使用 Vue 官方脚手架创建项目。# 使用 npm npm create vuelatest coursework-frontend # 或使用更快的 pnpm pnpm create vuelatest coursework-frontend在创建向导中选择需要的功能TypeScript, Vue Router, Pinia, ESLint。 创建完成后安装 UI 库和请求库cd coursework-frontend npm install element-plus axios # 或 pnpm add element-plus axios调整src/main.ts引入 Element Plus:import { createApp } from vue import App from ./App.vue import router from ./router import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css const app createApp(App) app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(#app)配置axios实例 (src/utils/request.ts)统一处理请求基路径、超时、拦截器用于自动添加Token和响应处理。3. 数据库设计与核心实体建模数据库设计是业务的基石。我们需要设计出规范、高效且易于扩展的表结构。3.1 核心表结构设计以下是简化版的核心表设计包含必要的字段和注释。-- 用户表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名登录账号, password varchar(255) NOT NULL COMMENT 加密后的密码, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, role tinyint NOT NULL COMMENT 角色0-管理员1-教师2-学生, email varchar(100) DEFAULT NULL COMMENT 邮箱, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, status tinyint DEFAULT 1 COMMENT 状态0-禁用1-正常, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, deleted tinyint DEFAULT 0 COMMENT 逻辑删除标记, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 课程表 CREATE TABLE course ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, course_code varchar(20) NOT NULL COMMENT 课程代码, course_name varchar(100) NOT NULL COMMENT 课程名称, description text COMMENT 课程描述, teacher_id bigint NOT NULL COMMENT 授课教师ID, academic_year varchar(20) DEFAULT NULL COMMENT 学年学期如2023-2024-1, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted tinyint DEFAULT 0, PRIMARY KEY (id), KEY idx_teacher_id (teacher_id), CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES sys_user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; -- 学生选课关联表 CREATE TABLE course_student ( id bigint NOT NULL AUTO_INCREMENT, course_id bigint NOT NULL COMMENT 课程ID, student_id bigint NOT NULL COMMENT 学生ID, join_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 加入时间, PRIMARY KEY (id), UNIQUE KEY uk_course_student (course_id,student_id), KEY idx_student_id (student_id), CONSTRAINT fk_cs_course FOREIGN KEY (course_id) REFERENCES course (id), CONSTRAINT fk_cs_student FOREIGN KEY (student_id) REFERENCES sys_user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生选课表; -- 作业表 CREATE TABLE assignment ( id bigint NOT NULL AUTO_INCREMENT, course_id bigint NOT NULL COMMENT 所属课程ID, title varchar(200) NOT NULL COMMENT 作业标题, content text COMMENT 作业要求富文本, attachment_url varchar(500) DEFAULT NULL COMMENT 附件存储路径, deadline datetime NOT NULL COMMENT 截止时间, total_score decimal(5,2) DEFAULT NULL COMMENT 总分, publish_status tinyint DEFAULT 0 COMMENT 发布状态0-草稿1-已发布, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted tinyint DEFAULT 0, PRIMARY KEY (id), KEY idx_course_id (course_id), CONSTRAINT fk_assignment_course FOREIGN KEY (course_id) REFERENCES course (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT作业表; -- 作业提交表 CREATE TABLE submission ( id bigint NOT NULL AUTO_INCREMENT, assignment_id bigint NOT NULL COMMENT 作业ID, student_id bigint NOT NULL COMMENT 提交学生ID, content text COMMENT 提交内容文本, attachment_url varchar(500) DEFAULT NULL COMMENT 提交附件路径, submit_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 提交时间, grade decimal(5,2) DEFAULT NULL COMMENT 得分, feedback text COMMENT 教师评语, grade_time datetime DEFAULT NULL COMMENT 批改时间, status tinyint DEFAULT 0 COMMENT 状态0-已提交待批改1-已批改, PRIMARY KEY (id), UNIQUE KEY uk_assignment_student (assignment_id,student_id), -- 防止重复提交 KEY idx_student_id (student_id), CONSTRAINT fk_submission_assignment FOREIGN KEY (assignment_id) REFERENCES assignment (id), CONSTRAINT fk_submission_student FOREIGN KEY (student_id) REFERENCES sys_user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT作业提交表;3.2 实体类与 MyBatis-Plus 映射在后端项目中创建与表对应的实体类。使用 Lombok 简化代码并使用 MyBatis-Plus 注解进行映射。// src/main/java/com/example/coursework/entity/User.java package com.example.coursework.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data TableName(sys_user) // 指定表名 public class User { TableId(type IdType.AUTO) // 主键自增 private Long id; private String username; private String password; private String realName; private Integer role; // 0-admin, 1-teacher, 2-student private String email; private String avatar; private Integer status; TableField(fill FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; TableLogic // 逻辑删除注解 private Integer deleted; }需要配置一个元对象处理器 (MetaObjectHandler) 来自动填充createTime和updateTime字段。4. 核心业务模块实现与前后端联调我们将以实现“作业发布与提交”这个核心流程为例串联前后端。4.1 后端 API 开发 (Spring Boot MyBatis-Plus)1. 创建 Mapper 接口和 Service// AssignmentMapper.java package com.example.coursework.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.coursework.entity.Assignment; import org.apache.ibatis.annotations.Mapper; Mapper public interface AssignmentMapper extends BaseMapperAssignment { // 可以在此定义复杂的自定义SQL方法 }// AssignmentService.java package com.example.coursework.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.coursework.entity.Assignment; public interface AssignmentService extends IServiceAssignment { // 定义业务方法如发布作业 boolean publishAssignment(Long assignmentId); }// AssignmentServiceImpl.java package com.example.coursework.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.coursework.entity.Assignment; import com.example.coursework.mapper.AssignmentMapper; import com.example.coursework.service.AssignmentService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; Service public class AssignmentServiceImpl extends ServiceImplAssignmentMapper, Assignment implements AssignmentService { Override Transactional(rollbackFor Exception.class) public boolean publishAssignment(Long assignmentId) { Assignment assignment this.getById(assignmentId); if (assignment null || assignment.getPublishStatus() 1) { return false; } assignment.setPublishStatus(1); return this.updateById(assignment); } }2. 创建 Controller 和 DTO使用 DTO (Data Transfer Object) 来接收请求和返回数据避免直接暴露实体类。// AssignmentDTO.java (用于创建/更新作业) package com.example.coursework.dto; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; Data public class AssignmentDTO { NotNull(message 课程ID不能为空) private Long courseId; NotBlank(message 作业标题不能为空) private String title; private String content; private String attachmentUrl; NotNull(message 截止时间不能为空) private LocalDateTime deadline; private Double totalScore; }// AssignmentController.java package com.example.coursework.controller; import com.example.coursework.dto.AssignmentDTO; import com.example.coursework.entity.Assignment; import com.example.coursework.service.AssignmentService; import com.example.coursework.vo.Result; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/assignment) public class AssignmentController { Autowired private AssignmentService assignmentService; // 创建作业草稿 PostMapping public ResultLong createAssignment(Validated RequestBody AssignmentDTO dto) { Assignment assignment new Assignment(); BeanUtils.copyProperties(dto, assignment); assignment.setPublishStatus(0); // 默认为草稿 assignmentService.save(assignment); return Result.success(assignment.getId()); } // 发布作业 PutMapping(/publish/{id}) public ResultVoid publishAssignment(PathVariable Long id) { boolean success assignmentService.publishAssignment(id); return success ? Result.success() : Result.error(作业不存在或已发布); } // 根据课程ID查询作业列表 GetMapping(/list) public ResultListAssignment listByCourse(RequestParam Long courseId) { // 使用MyBatis-Plus的Wrapper构建查询条件 LambdaQueryWrapperAssignment wrapper new LambdaQueryWrapper(); wrapper.eq(Assignment::getCourseId, courseId) .eq(Assignment::getDeleted, 0) .orderByDesc(Assignment::getCreateTime); ListAssignment list assignmentService.list(wrapper); return Result.success(list); } }其中Result是一个统一的响应包装类包含code,message,data字段。3. 处理文件上传Spring Boot 通过MultipartFile接口简化文件上传。// FileController.java package com.example.coursework.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.UUID; RestController RequestMapping(/file) public class FileController { Value(${file.upload-dir:./uploads}) // 从配置读取默认当前目录下的uploads private String uploadDir; PostMapping(/upload) public ResultString uploadFile(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { return Result.error(文件为空); } // 生成唯一文件名防止覆盖 String originalFilename file.getOriginalFilename(); String fileExtension originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString() fileExtension; File dest new File(uploadDir File.separator newFileName); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } try { file.transferTo(dest); // 返回文件的访问路径需要配置静态资源映射或使用云存储URL return Result.success(/uploads/ newFileName); } catch (IOException e) { e.printStackTrace(); return Result.error(文件上传失败); } } }需要在application.yml中配置上传路径和静态资源映射。4.2 前端页面与接口调用 (Vue 3 Element Plus)1. 作业列表页面 (AssignmentList.vue)template div classassignment-list el-button typeprimary clickhandleCreate发布新作业/el-button el-table :dataassignmentList stylewidth: 100% el-table-column proptitle label作业标题 / el-table-column propdeadline label截止时间 :formatterformatDate / el-table-column proppublishStatus label状态 template #defaultscope el-tag :typescope.row.publishStatus 1 ? success : info {{ scope.row.publishStatus 1 ? 已发布 : 草稿 }} /el-tag /template /el-table-column el-table-column label操作 template #defaultscope el-button sizesmall clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(scope.row)删除/el-button el-button v-ifscope.row.publishStatus 0 sizesmall typesuccess clickhandlePublish(scope.row)发布/el-button /template /el-table-column /el-table !-- 创建/编辑作业的对话框 -- el-dialog v-modeldialogVisible :titledialogTitle el-form :modelform :rulesrules refformRef el-form-item label作业标题 proptitle el-input v-modelform.title / /el-form-item el-form-item label截止时间 propdeadline el-date-picker v-modelform.deadline typedatetime placeholder选择截止时间 / /el-form-item el-form-item label作业要求 el-input v-modelform.content typetextarea :rows4 / /el-form-item el-form-item label附件 el-upload :actionuploadUrl :on-successhandleUploadSuccess el-button typeprimary点击上传/el-button /el-upload /el-form-item /el-form template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clicksubmitForm确定/el-button /template /el-dialog /div /template script setup langts import { ref, onMounted } from vue import { ElMessage, ElMessageBox } from element-plus import type { Assignment } from /types/api import { getAssignmentList, createAssignment, publishAssignment, deleteAssignment } from /api/assignment import { uploadFile } from /api/file const courseId ref(1) // 应从路由或状态管理中获取实际课程ID const assignmentList refAssignment[]([]) const dialogVisible ref(false) const form ref({ courseId: courseId.value, title: , content: , deadline: , attachmentUrl: }) const uploadUrl /api/file/upload // 后端文件上传接口 const loadData async () { const res await getAssignmentList(courseId.value) if (res.code 200) { assignmentList.value res.data } } const handleCreate () { dialogVisible.value true // 重置表单 } const handlePublish async (row: Assignment) { try { await ElMessageBox.confirm(确认发布该作业, 提示) const res await publishAssignment(row.id) if (res.code 200) { ElMessage.success(发布成功) loadData() } } catch (error) { // 用户取消 } } const handleUploadSuccess (response: any) { if (response.code 200) { form.value.attachmentUrl response.data ElMessage.success(附件上传成功) } } const submitForm async () { // 表单验证... const res await createAssignment(form.value) if (res.code 200) { ElMessage.success(创建成功) dialogVisible.value false loadData() } } onMounted(() { loadData() }) /script2. 封装 API 请求 (src/api/assignment.ts)import request from /utils/request import type { Assignment, Result } from /types/api export function getAssignmentList(courseId: number): PromiseResultAssignment[] { return request({ url: /assignment/list, method: get, params: { courseId } }) } export function createAssignment(data: any): PromiseResultnumber { return request({ url: /assignment, method: post, data }) } export function publishAssignment(id: number): PromiseResultvoid { return request({ url: /assignment/publish/${id}, method: put }) }4.3 解决跨域问题前后端分离开发时前端运行在localhost:5173后端在localhost:8080浏览器会因同源策略阻止请求。需要在后端配置 CORS。// src/main/java/com/example/coursework/config/CorsConfig.java package com.example.coursework.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); // 允许的源生产环境应配置为具体域名 config.addAllowedOriginPattern(*); // 允许的请求方法 config.addAllowedMethod(*); // 允许的请求头 config.addAllowedHeader(*); // 允许携带凭证如Cookie config.setAllowCredentials(true); // 预检请求的缓存时间秒 config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }注意生产环境中addAllowedOriginPattern(*)应替换为具体的前端域名如https://yourdomain.com以增强安全性。5. 系统安全、部署与常见问题排查一个完整的系统必须考虑安全性和可部署性。5.1 集成 Spring Security 实现认证授权添加依赖在pom.xml中加入spring-boot-starter-security。创建安全配置类继承WebSecurityConfigurerAdapter(Spring Boot 2.x) 或使用新式配置定义哪些路径需要认证如何配置密码加密、登录成功/失败处理等。实现 UserDetailsService从数据库加载用户信息用于认证。使用 JWT (JSON Web Token)在前后端分离项目中通常使用无状态的 JWT 替代 Session。用户登录成功后后端生成一个签名的 Token 返回给前端前端后续请求在Authorization请求头中携带此 Token。创建 JWT 工具类用于生成、解析和验证 Token。创建过滤器在 Spring Security 过滤器链中添加一个 JWT 认证过滤器在UsernamePasswordAuthenticationFilter之前用于解析请求头中的 Token 并设置安全上下文。前端存储 Token登录成功后将 Token 存储在localStorage或sessionStorage中并在axios请求拦截器中自动添加到请求头。5.2 项目打包与部署后端打包# 在项目根目录下 mvn clean package -DskipTests生成的coursework-backend-0.0.1-SNAPSHOT.jar位于target目录。可以通过java -jar命令运行。前端打包# 在前端项目根目录下 npm run build # 或 pnpm build生成的静态文件位于dist目录。部署方式开发/演示环境前后端分开部署。后端运行 Jar 包前端使用 Nginx 或直接通过 IDE 运行开发服务器并配置代理。生产环境后端将 Jar 包部署到 Linux 服务器使用systemd或supervisor等进程管理工具守护进程。配置application-prod.yml指定生产数据库、Redis如需缓存、文件存储路径等。前端将dist目录下的文件放置到 Nginx 的html目录下并配置 Nginx 将 API 请求反向代理到后端服务。数据库使用云数据库或自建 MySQL做好定期备份。5.3 常见问题与排查路径在开发联调和生产部署中以下几个问题是高频出现点。问题现象可能原因检查方式与解决方案前端请求后端接口出现4041. 后端服务未启动。2. 请求 URL 路径错误。3. 后端context-path配置与前端请求前缀不匹配。1. 检查后端控制台是否启动成功端口是否被占用。2. 核对浏览器开发者工具 Network 标签中的请求 URL 与后端RequestMapping路径。3. 检查后端server.servlet.context-path配置前端请求基地址需包含此路径。前端请求出现CORS跨域错误后端未正确配置 CORS或配置的源、方法、头不匹配。1. 检查后端CorsConfig是否生效允许的源 (AllowedOrigin) 是否包含前端地址。2. 复杂请求如带自定义头的PUT需要处理预检请求 (OPTIONS)。确保配置了addAllowedMethod(*)。文件上传失败大小受限上传文件超过 Spring Boot 默认大小限制 (1MB)。在application.yml中调整spring.servlet.multipart.max-file-size和max-request-size参数。同时Nginx 等反向代理也可能有大小限制需要同步调整。数据库连接失败1. 数据库服务未启动。2. 连接 URL、用户名、密码错误。3. 时区或 SSL 配置问题。4. 驱动版本与 MySQL 版本不匹配。1. 检查 MySQL 服务状态。2. 核对application.yml中的配置。3. 在连接 URL 中添加serverTimezoneAsia/ShanghaiuseSSLfalse尝试。4. 确保使用mysql-connector-java8.x 驱动连接 MySQL 5.7/8.0。插入或查询中文乱码1. 数据库、表字符集不是utf8mb4。2. 连接字符串未指定字符集。3. 服务端、客户端字符集不统一。1. 创建数据库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。2. 在 JDBC URL 中添加characterEncodingutf-8。3. 检查操作系统、IDE 终端字符集。前端页面刷新后路由丢失显示404Vue Router 使用了history模式但 Nginx 未配置try_files回退到index.html。在 Nginx 配置中对前端路由的 location 块内添加try_files $uri $uri/ /index.html;。登录成功后后续请求仍提示未登录1. Token 未正确存储或发送。2. Token 已过期。3. 后端 JWT 过滤器配置路径有误或 Token 解析失败。1. 检查前端请求拦截器是否在每次请求的 Header 中添加了Authorization: Bearer token。2. 检查 Token 有效期实现刷新 Token 逻辑。3. 查看后端日志确认过滤器是否被触发以及 Token 解析是否有异常。5.4 毕业设计答辩要点与扩展方向完成基础功能后若想提升项目深度以应对答辩可以考虑以下扩展方向引入 Redis缓存课程信息、用户信息、热点作业数据减轻数据库压力并用于实现分布式 Session 或 Token 黑名单。加入消息队列使用 RabbitMQ 或 Kafka将作业提交成功、批改完成等事件异步发送解耦系统模块并实现邮件或站内信通知。实现分页与复杂查询作业列表、学生列表使用 MyBatis-Plus 的分页插件并支持多条件组合查询。集成第三方登录如使用 OAuth2.0 接入学校统一认证系统或微信扫码登录。容器化部署编写Dockerfile将前后端分别容器化使用docker-compose.yml一键启动整个应用栈包含 MySQL、Redis。编写单元测试与集成测试使用 JUnit 5 和 Mockito 对 Service 层关键方法进行单元测试使用SpringBootTest进行集成测试提高代码质量。完善 API 文档使用 Swagger/OpenAPI 3 (SpringDoc) 自动生成在线 API 文档方便前端对接和答辩演示。在答辩时重点阐述你的技术选型理由、系统架构设计、数据库设计范式考量、解决的核心技术难点如文件上传、权限控制、前后端分离部署以及项目的可扩展性。清晰的流程图、ER 图和核心代码片段是很好的辅助材料。