ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue智能学习平台管理系统:前后端分离实战全解析

SpringBoot+Vue智能学习平台管理系统:前后端分离实战全解析 最近很多朋友在找能直接跑通的前后端分离练手项目我整理了自己写的一套基于SpringBootVue的智能学习平台管理系统。这套项目技术栈是JavaMySQLMyBatis从数据库表设计、后端接口到Vue管理页面完整链路都有适合正在准备毕业设计、想补全后端知识的前端同学以及要快速搭建一个管理系统原型的人。它的核心不是堆算法而是把用户、课程、学习进度、在线考试这些业务管理起来属于非常典型的企业级管理系统形态。这篇文章我按自己的实操顺序把设计思路、核心代码、部署打包和踩过的坑全部拆开讲。1. 项目整体设计与技术选型1.1 为什么押注SpringBootVue这套组合先回答一个很多人纠结的问题Java后端方案那么多为什么偏偏是SpringBootVue我自己的判断很简单这套组合在当前技术生态里属于“投入产出比”最高的选择。后端用SpringBoot好处是自动装配和内嵌Tomcat省掉了大量XML配置。对比传统的SSMSpringSpringMVCMyBatis项目光是一个spring-mvc.xml、mybatis-config.xml的配置就能劝退不少新手SpringBoot用application.yml一个文件就解决大半。对比JPA/HibernateMyBatis虽然要手写SQL但对学习平台这种查询条件多变的场景反而更舒服比如课程列表要按分类、关键词、难度、状态动态筛选用MyBatis的动态SQL拼条件非常直观SQL写出来自己就能预判性能。前端用Vue也同理。Vue的响应式机制和组件化开发特别适合管理系统这种表单、列表、弹窗密集的场景。加上Element Plus这类组件库后台管理的页面可以快速搭出来。这里我不推荐用Vue去套原生JS或者jQuery那套思路既然前后端分离就把Vue的工程化能力用满路由、状态管理、组件复用一起上。MySQL是这套系统的固定搭配。学习平台的数据核心是强关系型数据用户选课、章节学习记录、考试答题记录这些数据之间有明确的外键关系MySQL的事务保障可以避免出现“学习记录写到一半、课程状态没更新”这种数据不一致问题。1.2 功能模块怎么拆才合理拿到“智能学习平台管理系统”这个标题第一件事不是写代码而是拆功能。我把它分成用户端和管理端两条线。用户端的核心场景是注册登录、浏览课程、进入课程学习章节、系统自动记录学习进度、参加在线测评、查看考试成绩和个人学习统计。这里有个关键点学习进度不能只靠用户手动点击“已完成”更合理的做法是进入章节页面后自动上报进度由后端保存前端再从接口读取刷新页面也不丢。管理端的核心场景是用户管理禁用/启用、课程分类和课程内容管理、章节资料维护视频地址、文档附件、题目和试卷管理、考试发布与成绩查询、公告管理再加一个数据看板统计注册人数和学习人数。接口权限我用两个角色区分普通用户ROLE_USER和管理员ROLE_ADMIN管理员接口统一加/api/admin前缀通过后端拦截器校验。角色权限表先定下来后面前端路由守卫和后端接口拦截都用它。模块接口路径方法说明权限认证/api/auth/loginPOST登录返回JWT token公开课程/api/course/listGET分页查询课程列表登录学习/api/learning/progressPOST上报学习进度登录考试/api/exam/submitPOST提交试卷答案登录用户管理/api/admin/user/pageGET分页查询用户管理员课程管理/api/admin/course/savePOST新增/更新课程管理员1.3 数据库表设计的前期规划数据库设计是这个项目的地基我建议先画ER图再动手建表。整套系统核心就8张表用户表sys_user、课程分类表course_category、课程表course、课程章节表course_chapter、学习记录表learning_record、题目表question、考试记录表exam_record、公告表notice。学习记录表单独拆出来是关键决策。一个用户会学多门课程一门课程下有多个章节如果把进度字段塞进用户表数据会横向膨胀且难以维护。单独建学习记录表用user_id course_id做联合唯一索引一个用户同一门课程只保留一条记录上报新章节时做更新而不是插入这样既能快速查询整体进度又能控制表体量。几个具体的设计细节密码字段永远不要明文存储后端用BCrypt加密后再入库登录时用matches方法校验。状态字段我用tinyint而不是boolean课程状态以后可能要扩展成“草稿/发布/下线”只靠0和1不够用。业务字段逻辑删除优于物理删除每张表预留deleted字段避免误删数据后无法恢复。字符集统一utf8mb4排序规则用utf8mb4_general_ci别问我为什么不用utf8遇到表情符号存不进去你就知道了。2. 后端核心实现细节2.1 项目结构、Maven依赖与配置后端工程结构我按经典分包方式组织controller只负责接收参数和返回结果service层写业务逻辑mapper层只访问数据库entity对应数据库表字段另外单独拆了dto和vo一个管入参校验一个管返回视图。千万别把业务逻辑写在Controller里短期看方便项目一复杂就全乱。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency版本这里要特别提醒如果你想用SpringBoot 3.xMyBatis对应的starter必须升级到3.0以上版本同时JDK也要切到17而且javax.servlet要整体换成jakarta.servlet很多老教程的代码直接搬过来会编译报错。我的建议是练手项目先锁死SpringBoot 2.7.x JDK 8资料最多踩坑成本最低。application.yml里有两个配置容易被忽略。一个是数据源连接串必须带useUnicodetruecharacterEncodingutf-8否则插入中文会乱码另一个是MyBatis的SQL日志开发阶段打开后控制台直接打印SQL语句排查问题效率高出一大截。spring: datasource: url: jdbc:mysql://localhost:3306/learning_platform?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl2.2 用户认证手写JWT拦截器登录认证我一开始用的是Session方案后来发现前后端分离项目里维护Session太别扭干脆换成了JWT。原理很简单用户登录成功后后端生成一个自带签名和过期时间的token返回给前端前端存在localStorage里后续请求在Header带上Authorization后端拦截器解析token并验证签名合法就放行。JWT工具类核心代码public class JwtUtil { // 实际项目中密钥要放到配置中心或环境变量不要硬编码在源码里 private static final String SECRET your-secret-key; private static final long EXPIRE 7 * 24 * 60 * 60 * 1000L; public static String createToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } }拦截器实现時需要注意几个坑。第一preHandle里解析token失败要区分两种情况token过期返回提示“请重新登录”token非法返回“认证失败”别都返回401前端不好提示。第二白名单接口要放行比如/api/auth/login和/api/course/list登录接口本身肯定不能拦。第三把解析出的userId放到ThreadLocal里后续Service层直接用UserContext.getUserId()就能拿到当前用户避免每个接口都重复解析一遍token。2.3 学习进度上报事务和动态SQL的实战进度模块是学习平台的业务亮点也是技术难点。前端每进入一个新章节就调用进度上报接口后端接收userId、courseId、chapterId和进度百分比在learning_record表里做“存在则更新、不存在则插入”的操作。Mapper的SQL我直接写了注意ON DUPLICATE KEY UPDATE可以用一条SQL同时覆盖插入和更新场景但用之前必须确认联合唯一索引已经建好CREATE TABLE learning_record ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 用户ID, course_id bigint NOT NULL COMMENT 课程ID, chapter_id bigint NOT NULL COMMENT 当前章节ID, progress int NOT NULL DEFAULT 0 COMMENT 进度百分比 0-100, status tinyint NOT NULL DEFAULT 0 COMMENT 0学习中 1已完成, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_course (user_id, course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;Service层这里体现事务的价值如果用户第一次学习插入记录同时还要更新课程的学习人数统计两步操作要么一起成功要么一起回滚。在方法上加上Transactional注解由Spring统一管理事务边界。很多人忽略的一点是Transactional默认只回滚RuntimeException如果业务里抛出的是自定义Checked Exception记得显式声明rollbackFor。课程列表的分页查询也用到了MyBatis动态SQL比如按课程名称模糊搜索、按分类过滤两个条件可能同时出现也可能都不出现select idselectCoursePage resultTypecom.example.learning.vo.CourseVO SELECT * FROM course where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND title LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY create_time DESC /select3. 前端Vue实现要点3.1 初始化项目与基础依赖安装前端我用的Vite创建项目相比Vue CLI启动速度快很多。环境准备上Node.js版本不要装太老我用的是18以上。创建命令npm create vitelatest learning-front -- --template vue cd learning-front npm install npm run dev装完后引入vue-router、pinia和axios三个基础库。组件库我选的Element Plus管理系统的表格、表单、弹窗这些组件开箱即用。UI框架准备阶段就要想好后面写页面不用到处手搓样式开发效率会差好几倍。3.2 axios封装和请求拦截器前后端分离项目的接口调用必须要有一个统一封装的axios实例。基础路径我用的相对路径/api这样开发环境下通过代理转发到后端生产环境打包进SpringBoot后也不用改代码。拦截器里自动从localStorage取token并附加到Header响应拦截器统一处理401跳转。import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization token } return config }) request.interceptors.response.use( response { return response.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.response?.data?.msg || 请求失败) return Promise.reject(error) } )把token放在localStorage这个方案练手项目足够了。但如果做生产项目要关注XSS风险更稳妥的做法是用HttpOnly的Cookie让浏览器自动携带前端拿不到也就更安全。两种方案各有利弊我的建议是先把localStorage跑通理解了后再进阶。3.3 路由守卫与动态菜单Vue Router的路由配置核心是登录守卫。我加一个全局前置守卫访问需要登录的页面但本地没有token就重定向到/login已经登录的人访问登录页自动跳回首页。这段逻辑代码很短但系统能不能用就靠它兜底。管理端菜单我用的是动态路由方案。普通用户登录后路由只有课程、学习记录、个人中心管理员登录后路由额外挂载用户管理、课程管理、考试管理。实现方式是在路由表里把需要管理员权限的页面标记meta.roles判断当前用户的角色不在允许列表里就直接跳403页面。3.4 核心页面实现细节课程列表页是典型的“表格分页搜索条件”组合。前端搜索栏把分类下拉框、关键词输入框、查询和重置按钮放一排参数传到/api/course/list分页接口。这里有个细节查询条件的参数要放进reactive对象统一管理重置按钮直接把对象清空再调用一次查询代码会干净很多。课程详情页的学习区我做了两个能力。一个是视频播放如果课件视频转成了m3u8格式前端用hls.js解析播放实现代码也就是Hls.isSupported()判断后attachMedia到video元素。另一个是PDF预览课件如果是PDF用iframe加载/static/pdf/xxx.pdf创建http://localhost:8080/api/course/chapter/1的接口开发环境下/api就会自动转发到后端。Vite的代理配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }如果后端开启了CORS跨域配置开发环境就不需要Vite代理两个都配也没问题但要注意别把逻辑搞混。我的习惯是开发用代理生产打包进SpringBoot后走同源全局CORS只在上线后配合网关用。4.2 Vue打包放进SpringBoot的两种方式开发完成后的部署我试过两种方案。第一种是把前端打包产物直接放进SpringBoot静态目录里最省事。执行npm run build把生成的dist目录里的内容复制到src/main/resources/static下重启后端访问http://localhost:8080就能看到整个系统。但这里有个经典的坑如果Vue Router用了history模式去掉了地址栏的#号刷新某个子页面时会404因为SpringBoot找不到对应的后端路由。解决办法是加一个转发控制器把非/api开头的路径统一转发到index.htmlController public class PageForwardController { RequestMapping(value /{path:[^\\.]*}) public String forward() { return forward:/index.html; } }这个正则的关键是[^\\.]*排除带点号的静态资源路径否则JS、CSS文件也会被转发。第二种方式是用Nginx部署前端将dist目录指向Nginx静态站点/api反向代理到SpringBoot的8080端口同时配置try_files $uri $uri/ /index.html解决刷新404问题。这种方式前后端完全分离后端只提供API以后即使换前端框架也互不干扰。4.3 SpringBoot版本太高引起的坑新建SpringBoot项目别无脑选最新正式版版本越高坑越多。我实际遇到过一个典型问题SpringBoot 3.x把javax.*换成了jakarta.*老教程里的import javax.servlet.*全部失效拦截器、过滤器代码一编译就报错其次MyBatis的starter也要换成新版否则连数据库都连不上。如果你已经在写SpringBoot 3.x其实也能跑通但需要做三件事JDK升到17、依赖版本全部核对一遍、遇到javax开头的包名都改成jakarta。对新手我建议先锁SpringBoot 2.7.x先把业务逻辑跑通之后再研究版本迁移这才是性价比最高的学习路径。4.4 常见问题排查速查表下面这个表是我在实际带项目过程中整理的高频问题几乎每个人都会碰到问题现象可能原因解决方案前端请求后端接口401token过期或没传到后端检查拦截器逻辑和前端Header是否携带Authorization刷新子页面404Vue Router history模式未配置回退后端加页面转发Controller或Nginx配置try_files中文乱码数据库连接串缺少编码参数URL加useUnicodetruecharacterEncodingutf-8连不上数据库账号权限、端口或密码错误先ping、再telnet 3306最后核对配置端口被占用8080被其他程序占用netstat -ano查PID改server.port或杀进程MyBatis报Invalid bound statementMapper接口和XML没有正确映射检查mapper-locations路径、接口方法名和XML的id一致前端依赖安装失败npm镜像源访问慢设置镜像源npm config set registry https://registry.npmmirror.comElement Plus组件没样式按需导入配置缺失跟着自动导入插件重新配置或全局导入5. 项目进阶方向与个人实操补充5.1 这套系统后续还能怎么扩展学习平台这个主题能延伸的方向不少。我自己给这个项目规划了几个升级路径你可以根据精力选择性落地。第一个方向是引入Redis。当前token校验每次都走JWT解析性能本身没问题但加上Redis之后可以做两件更有价值的事一是存储验证码登录时把验证码放进Redis并设置过期时间二是把学习热榜、课程访问量这类高频读数据缓存起来减轻MySQL压力。Redis数据结构里的zset很适合做排行榜学习平台完全可以做个“本周学习之星”功能。第二个方向是接入Spring Security替换手写拦截器。现在项目里权限控制是手写拦截器实现的能跑但不够体系化。Spring Security的过滤器链、权限表达式、方法级鉴权都更成熟迁移后管理代码会清爽很多。当然学习成本也会上来作为进阶练习很合适。第三个方向是文件存储拆分。目前视频和PDF课件一般存在服务器本地生产环境肯定不行。可以把静态文件迁到阿里云OSS或MinIO这类对象存储后端只维护URL地址。视频如果要做流畅播放还要转码成m3u8切片这个和前端的hls.js播放器是配套的。第四个方向是智能答疑。平台既然叫“智能”学习平台总得有智能化元素。我建议可以在学习记录、答题数据的基础上做一个简单的错题分析模块统计用户高频错题推荐对应章节重新学习。甚至后续可以接大模型接口做答疑机器人但初期先做规则引擎别一上来就上算法。5.2 给后来者的几点实操建议项目骨架完整跑起来之后我复盘了一下整个开发过程有几个经验特别想分享。第一动手写代码之前务必先把接口文档定好。我最初是边写后端边定义接口结果前端开发时需要改字段名后端要跟着改来回拉扯浪费了大量时间。后来用Swagger注解把每个接口的参数和返回结构标清楚前后端联调顺畅了至少一倍。对新手尤其如此接口先行是前后端分离项目的铁律。第二数据库变更一定要用脚本管理不要直接在Navicat里改表。我吃过一次亏本地测试改了一个字段类型上线时忘了同步生产环境直接报错。从那以后每次表结构变更我都习惯写成alter.sql放到项目的sql目录下和源码一起走版本管理。第三登录权限链路一定要最先打通。先实现登录接口、JWT签发、拦截器校验、前端登录页、路由守卫、axios携带token这整条闭环再开始写课程模块。权限链路没通之前写的业务页面全是空中楼阁最后还得回头补登录逻辑改动成本极高。我当时先花半天打通认证链路后面所有业务接口开发都是顺水推舟。第四善用Git做节点管理。每个功能模块完成并自测通过后打一个commit写清楚改动内容。后面出了问题直接回滚到一个稳定节点比在满是改动的代码里大海捞针高效得多。这套系统从设计到落地我踩过不少坑也收获了不少经验。如果你正在做类似的课程设计、毕业设计或者只是想完整走一遍前后端分离的管理系统开发流程照着这个思路从数据库设计开始一步步搭比下载一个现成项目照着跑收获大得多。真正有价值的从来不是那份源码而是你亲手把每个模块做出来的过程。
返回列表