SpringBoot+Vue3教学辅助系统源码实战:从环境搭建到前后端联调
这套教学辅助系统源码我用周末时间完整跑通了一遍技术栈写得很清楚SpringBoot2 Vue3 MyBatis-Plus MySQL8.0包里还带了一份完整的开发文档。整体看下来它不像很多毕设项目那样只有一个空壳而是把教师、学生、管理员三类角色的核心教学场景都做成了可运行的闭环。如果你正在找前后端分离项目的练手素材或者准备拿它做毕设底子这篇文章能帮你省不少摸索时间。我会把从环境准备、数据库初始化、后端启动、前端跑通到核心业务代码拆解、跨域和时区等高频问题全部按实操顺序写一遍。1. 拿到源码先别急着跑先看懂整体设计1.1 教学辅助系统解决的是真实业务痛点很多初学者拿到一个“教学辅助系统”标题第一反应是“这不就是个后台管理吗”。其实它比普通后台管理多了非常明确的业务语义。传统线下教学中教师布置作业、学生提交作业、管理员管理教师和学生账号这些操作分散在微信群、邮件、Excel表格里时间一长就混乱。这套系统的价值就是把“课程、作业、提交、成绩、公告”这些教学环节全部搬到线上并且按照用户角色做权限隔离。从我拆代码的角度看它至少覆盖了几个典型场景教师创建课程后可以在课程下发布课件和作业学生登录后看到自己的课程列表进入课程详情后可以下载课件、提交作业教师端能查看某个作业下所有学生的提交记录并给出评语和分数管理员则负责维护用户账号和基础数据。这套流程不算复杂但它是Java Web领域最有代表性的业务闭环之一非常适合用来理解数据表设计、接口设计和页面交互之间的关系。1.2 为什么是SpringBoot2Vue3MyBatis-PlusMySQL8.0技术选型不是越新越好而是要保证资料多、上手快、能跑通。这套源码选用的组合我认为是很稳妥的层级技术选择理由后端框架SpringBoot2启动简单、约定优于配置国内生产环境和教程资源都很丰富前端框架Vue3组合式API、setup语法糖配合Vite开发效率高组件生态成熟ORM框架MyBatis-Plus单表CRUD不用写SQL复杂查询又能保留XML内置分页、逻辑删除、自动填充数据库MySQL8.0免费、普及率高JSON支持和时区处理比5.7更舒服SpringBoot2.7虽然不是最新但对应JDK8/11跑起来最稳很多第三方集成的兼容性问题最少。Vue3配合Vite启动速度比之前的Webpack方案快很多。MyBatis-Plus则把重复的增删改查代码砍掉了大半开发者可以把精力放在真正的业务逻辑上。MySQL8.0在性能和功能上都领先旧版本新项目直接上8.0基本不会踩坑。1.3 数据模型与权限骨架看懂表就懂了一半我拆源码第一件事是先看数据库脚本因为所有业务逻辑最后都要落在一张张表的关系上。这套教学辅助系统核心表大致如下用户表保存登录账号、密码、真实姓名、角色类型管理员/教师/学生课程表保存课程名称、课程编码、教师ID、课程简介课件表保存课程下的附件资料关联课程ID和上传教师ID作业表保存课程下发布的作业标题、要求、截止时间关联课程ID提交记录表保存学生针对某个作业的提交内容、附件路径、提交时间公告表保存系统级或课程级通知通常管理端发布权限模型不需要一开始就设计成复杂的用户-角色-菜单-权限五张表。很多教学辅助系统会在用户表里直接放一个role字段再配合后端拦截器做接口权限控制。这样简单直接对于单体和中小型系统完全够用。我在看源码时发现它也是这样设计的所以后面做权限扩展时不必推倒重来。2. 从0到1跑起来环境、数据库和后端启动2.1 版本匹配清单第一次运行这种项目最容易翻车的不是业务代码而是环境版本不一致。我建议按下表准备软件推荐版本注意事项JDK8或者11SpringBoot2.7兼容但不要用太激进的新版本Maven3.6.3以上配置好国内镜像源下载依赖会快很多MySQL8.0.x使用com.mysql.cj.jdbc.Driver驱动Node16或18太新的Node版本可能导致Vite启动失败开发工具任意支持Maven的IDE后端用IDE前端用代码编辑器即可这里特别强调一下不要一开始就使用JDK17或者Node20除非你对依赖兼容性非常熟悉。我见过太多人卡在启动阶段最后发现是版本太高部分依赖没有适配。2.2 数据库初始化的正确姿势数据库初始化是启动的第一步也是最容易忽略细节的地方。通常源码文档里会给出一个init.sql或者schema.sql。我的建议是先用Navicat或命令行创建一个独立数据库再导入SQL脚本。CREATE DATABASE edu_assistant DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;字符集必须用utf8mb4因为老式的utf8不支持emoji和一些生僻字而且utf8mb4是MySQL8.0的默认推荐。导入脚本后到后端配置里检查数据库名、用户名、密码是否一致。重点看一下JDBC连接串spring: datasource: url: jdbc:mysql://localhost:3306/edu_assistant?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.DriverserverTimezoneAsia/Shanghai必须加否则后面会遇到时间差8个小时的问题。useSSLfalse是避免本机自签名证书提示。MySQL8.0的驱动类名是com.mysql.cj.jdbc.Driver不是老款的com.mysql.jdbc.Driver。2.3 后端启动三步走第一次启动后端不要直接按运行按钮按顺序操作用IDE打开后端工程等待Maven重新导入依赖。检查application.yml中的数据库配置、端口配置。启动启动类Application.java观察控制台日志。正常情况下日志会打印出启动时间、端口以及项目里配置的接口文档地址。如果用了Swagger还可以在浏览器里访问/swagger-ui.html或/doc.html查看接口列表。如果启动报错重点看两处一是是否所有依赖都下载成功二是数据库连接是否成功。控制台报错信息会直接告诉你问题是出在端口占用、数据库连接还是某个类找不到。先别急着谷歌把异常信息看一遍大多数时候答案就在报错最后几行。2.4 前端启动与接口代理前端部分跑起来比后端还要简单但有一个关键配置往往被忽略接口代理。Vue项目的Vite配置里通常会这样写export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这段配置的意思是前端往/api开头的路径发请求会被Vite开发服务器转发到后端的8080端口。所以前端代码里请求后端的地址不要写完整的http://localhost:8080而应该直接写/api/xxx。然后在终端执行npm install npm run dev如果npm install速度太慢可以临时换镜像源。启动后访问http://localhost:5173看到登录页就说明前后端已经连通了。3. 核心业务模块拆解从登录到作业闭环3.1 登录认证与JWT拦截器的实现教学辅助系统里有不同角色登录是整个系统的入口。我看到这套源码不是用传统Session而是用JWT保存用户身份。整体流程是用户提交用户名和密码后端校验成功后生成一个Token返回给前端前端每次请求在请求头里带上Authorization: Token串后端通过拦截器解析Token并识别当前用户。拦截器核心逻辑类似这样public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !jwtUtil.validate(token)) { response.setStatus(401); return false; } Long userId jwtUtil.getUserId(token); UserContext.set(userId); return true; } }配置拦截器时登录接口、注册接口、静态资源路径要放行否则用户还没登录就被挡在外面。另外UserContext用ThreadLocal保存当前用户信息在Service层可以随时拿到当前登录人的ID。这套写法在单体项目里非常好用比在每个接口里手动解析Token要优雅得多。3.2 课程/作业/提交一条完整的业务链路我从拆源码的角度给你还原一条完整链路教师创建课程后进入课程详情页发布一条作业。前端把作业标题、要求、截止时间发给后端后端Controller接收参数后调用ServiceService层把作业信息保存到homework表同时关联course_id。学生端看到这条作业后点击提交上传一个文件附件并填写备注。提交接口会把文件存到服务器指定目录把文件路径和描述存到homework_submit表。教师端打开该作业详情时会查询所有学生的提交记录按提交时间倒序排列教师可以逐个批阅并打分。这条链路覆盖了业务系统最经典的分层结构Controller负责接收参数、Service负责业务规则、Mapper负责数据持久化。很多新手写代码时喜欢把业务逻辑全塞在Controller里这套源码能帮你建立正确的分层意识。我们在二次开发时也应该遵循同样的分层不要破坏这条链路。3.3 MyBatis-Plus高频实战分页、条件构造器、自动填充MyBatis-Plus是这套源码效率高的核心原因。它有三个使用细节非常关键。分页功能必须手动配置插件否则selectPage不会生效。在配置类里加Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }条件构造器推荐用LambdaQueryWrapper避免把数据库字段名写死成字符串不容易因改名而出错。比如查询某个课程下的作业LambdaQueryWrapperHomework wrapper new LambdaQueryWrapper(); wrapper.eq(Homework::getCourseId, courseId) .orderByDesc(Homework::getCreateTime);自动填充功能用来处理create_time和update_time这种通用字段。实现MetaObjectHandler接口后每次新增和更新都会自动带上时间不需要每次手动set。还有逻辑删除字段TableLogic用户删除操作变成更新操作数据不会真的消失这对教学系统很友好至少不会误删学生提交记录。4. 前后端联调避坑指南4.1 跨域与代理最常见的联调问题前后端分离项目浏览器跨域问题十有八九会遇到。前端跑在5173端口后端跑在8080端口两个端口不一致浏览器就会拦截跨域请求。解决办法有两个一个是在后端写CORS配置另一个是用前端Vite代理。这套源码用的是代理方式配置上面已经写过核心是让浏览器只认前端这一个源后端请求由Vite代发。如果你自己在改造时想用后端CORS需要在后端加一个配置类但要注意允许的请求头、请求方法范围。我建议小项目直接用代理方案部署到服务器时再用Nginx做反向代理思路一致问题最少。4.2 MySQL8.0时区与LocalDateTime序列化这是很多项目跑通后第一个发现的怪问题接口返回的时间总是比正常时间少8个小时或者前端提交时间后数据库里存的时间不对。原因基本在两层JDBC连接串没写serverTimezoneAsia/Shanghai或者Jackson序列化没设置时区。后端配置里加这一段spring: jackson: time-zone: GMT8 date-format: yyyy-MM-dd HH:mm:ss实体字段如果是LocalDateTime还可以用JsonFormat(pattern yyyy-MM-dd HH:mm:ss)单独控制格式。这样前端拿到的就是可读时间字符串而不是一串数组。遇到时间问题先检查连接串再检查jackson配置基本能解决90%的情况。4.3 文件上传大小限制与存储路径教学辅助系统肯定涉及上传课件、上传作业附件。SpringBoot默认最大上传文件只有1MB超过了直接报错。配置里显式放大spring: servlet: multipart: max-file-size: 200MB max-request-size: 200MB另一个容易被忽略的是文件保存路径。开发阶段很多人直接写相对路径比如uploads/本地跑没问题打成jar包部署后就找不到目录。我的建议是把存储路径做成配置项file: upload-path: /data/edu-assistant/upload在代码里从Value(${file.upload-path})读取拼接文件时用Paths.get()处理这样在Windows和Linux上都不会有路径分隔符问题。前端上传成功后后端返回一个相对URL页面再拼上固定的访问前缀即可。4.4 接口404/500的定位顺序新手拿到陌生源码后遇到报错容易慌。我提供一个通用排查顺序现象大概率原因处理方向后端启动报ClassNotFoundException依赖没下载完整Maven重新导入检查网络镜像前端请求接口404Controller路径没对上或代理配置缺失查看请求的URL和后端RequestMapping接口返回500SQL语句或空指针看控制台堆栈把SQL复制到数据库执行登录后请求返回401Token过期或拦截器没放行该路径检查前端是否携带Header后端放行规则中文乱码连接串没加characterEncodingutf8修改JDBC连接串和数据库字符集这套排查思路不仅适用于教学辅助系统任何前后端分离项目几乎都能通用。重要的是先分清问题在前端还是后端通过浏览器的F12开发者工具看请求和响应状态能省非常多时间。5. 二次开发与毕设加分方向5.1 新增“在线答疑”模块的最短路径假如你想在现有系统里加一个“在线答疑”模块最稳妥的方式不是从零手写而是参照已有的作业模块复制一套。步骤很清楚新增数据库表question和reply。编写对应的实体类、Mapper接口。编写Service和ControllerController路径用/api/question。前端新增Question管理页面复制一个已有列表页改成表单。在路由和侧边栏菜单注册新页面。很多同学容易被“设计模式”吓到其实这种业务模块的骨架高度相似。我实际操作时先复制作业模块再逐个改字段前后不到半天就能让新模块跑起来。等跑通后再去优化细节比如提问通知、未回复状态红点会有动力得多。5.2 从登录鉴权走向细粒度角色权限这套源码如果只有登录校验那每个老师都能访问管理接口。一个低成本但有效的改进是写一个自定义注解RequireRole标记在需要指定角色的接口方法上。拦截器里先判断方法有没有这个注解有就继续校验当前用户角色是否匹配。示例伪代码如下RequireRole(teacher) PostMapping(/homework) public Result saveHomework(RequestBody HomeworkVO vo) { ... }在拦截器里通过反射获取当前方法的注解RequireRole role method.getAnnotation(RequireRole.class); if (role ! null !user.getRole().equals(role.value())) { response.setStatus(403); return false; }这样只用少量代码就能做到接口级别的权限控制比直接引入Spring Security要轻得多。如果后续需要按钮级别权限或者更复杂的菜单权限再考虑重构成正规的RBAC权限模型。5.3 统计报表让项目更有亮点这类教学辅助系统积累了丰富数据天然的加分项是统计模块。比如统计每个课程的学生人数、每个作业的提交率、所有学生的成绩分布。后端写一个统计Mapper用GROUP BY和聚合函数前端用常见的图表库展示柱状图、饼图即可。作业提交率可以这样实现SELECT h.id, h.title, COUNT(s.id) AS submit_count, (SELECT COUNT(*) FROM student_course sc WHERE sc.course_id h.course_id) AS total_students FROM homework h LEFT JOIN homework_submit s ON s.homework_id h.id GROUP BY h.id;这个功能看起来不复杂但能体现你对业务数据的理解在答辩或总结时也很容易讲出价值。整套源码我实际梳理下来最大的收获是理解了单体Java Web项目里数据如何围绕“教学场景”流转。很多教程只讲单独的技术点却很少把SpringBoot、Vue、MyBatis-Plus、MySQL串成一个完整的业务流程。这套教学辅助系统恰好把这些点全部放在一个可运行的落地项目里非常适合用来建立“全栈视角”。如果你拿到的版本里和我一样带着文档先别急着改代码建议从登录接口开始跟着一个“学生提交作业”的完整流程把数据从浏览器到数据库怎么走、每一步经过哪些类全部走一遍。这个过程走通了后面再扩展任何功能都会顺畅得多。