多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SpringBoot+Vue在线教育系统源码:从环境搭建到二次开发全攻略

SpringBoot+Vue在线教育系统源码:从环境搭建到二次开发全攻略 把这套项目真正跑起来之前我先说一句大实话网上号称可直接运行的源码很多但绝大多数你都要花一晚上解决数据库版本、端口冲突、前端代理这三个问题。这套在线教育系统信息管理系统源码SpringBoot 后端 Vue 前端 MySQL我在本地从零完整跑通过目录结构、初始化脚本、接口路径都是按真实业务设计的不是那种只有登录注册的教学 Demo。如果你正在选型一套可以做二次开发的在线教育系统想省掉从零搭建框架的时间这篇文章值得你花几分钟看完。我会从系统定位、技术选型、后端模块、前端页面、数据库规划、启动步骤到上线前的调优把我实际跑通这套源码时验证过的东西全部写出来。无论你是刚接触全栈项目的新手还是需要给团队快速搭建基础框架的开发者按这篇文章的顺序走一遍基本不会卡壳。1. 这套系统的整体定位不是课程点播是完整的教务管理闭环1.1 它解决的是教学管理而不是视频播放问题很多刚接触在线教育项目的朋友第一反应是这不就是个视频网站吗。实际上市面上一套合格的在线教育信息管理系统核心价值在教务管理也就是围绕人和教学过程的信息流。视频播放只是其中一个功能点真正复杂的是学员、教师、课程、作业、成绩、公告这些数据怎么流转。这套源码覆盖的角色分成三类管理员、教师、学员。管理员负责基础数据维护和系统配置教师负责开课、上传课件、布置作业、录入成绩学员负责选课、学习、交作业、查成绩。三个角色看到的页面和可操作的接口完全不同这就倒逼权限设计必须做扎实不是前端隐藏几个按钮那么简单。1.2 业务的完整闭环长什么样我用这段流程来描述它的业务闭环管理员在后台创建学期和课程分类教师申请开课并维护课程章节学员在前台浏览课程列表并完成选课教师发布作业后学员在线提交教师批改并给出成绩最终学员在个人中心看到自己的学分和成绩单。这个闭环的每一步都对应一组后端接口和一张数据库表。课程、选课、作业、成绩这四块是系统的脊柱公告和用户管理是辅助模块。我看到这套源码时比较满意的一点是它的接口命名和表结构没有为了凑功能而做多余的重复设计每个模块的边界都相对清晰。这一点对于想拿它做二次开发的人来说特别重要——改起来不费劲。2. 技术选型复盘为什么是 SpringBoot Vue MySQL 这套组合2.1 三件套各自承担什么职责先聊聊技术选型的逻辑。SpringBoot 负责提供 RESTful 接口、处理业务逻辑、操作数据库Vue 负责渲染页面、管理前端状态、通过 Axios 调用后端接口MySQL 负责持久化所有业务数据。三者之间通过 JSON 格式的 HTTP 请求进行通信前后端完全分离这意味着后续你想把前端部署到 Nginx、后端部署到云服务器架构上不需要做任何改动。这套组合在中小型管理系统里几乎是标准答案因为它足够成熟。SpringBoot 内置了 Tomcat打成一个 Jar 包就能跑不需要额外配置 Servlet 容器Vue 的组件化开发让课程管理、成绩管理这类页面可以拆成独立组件复用MySQL 则完全能支撑几千人的在线教育平台的数据量配合合理的索引设计日常业务接口响应都在毫秒级。2.2 为什么不选更重的微服务架构这里有一个很关键的判断像在线教育信息管理系统这种业务体量一上来就拆微服务、上消息队列、搞分布式事务是典型的过度设计。因为它的核心业务是教务数据管理并发量集中在选课高峰期单体应用配合连接池和缓存完全可以扛住。等真的到了需要拆分的时候这套系统的边界清晰再按模块拆成独立服务也不难。所以这套源码选择单体的 SpringBoot 工程是合理的。它的好处明显部署简单、调试方便、依赖问题少。对于学习 SpringBoot 的开发者来说单体工程里你能完整看到 Controller、Service、Mapper、Entity 的全链路这在微服务架构里反而容易被各种中间件干扰。3. 后端工程结构拆解从目录设计到核心接口实现3.1 标准分层与包结构拿到源码后先看后端的包结构。典型的目录大致如下edu-system/ ├── src/main/java/com/edu/ │ ├── controller/ # 接口层只做参数接收和结果返回 │ ├── service/ # 业务层核心逻辑都在这 │ ├── mapper/ # 数据访问层对应 MyBatis 接口 │ ├── entity/ # 实体类与数据库表字段对应 │ ├── config/ # 配置类如跨域、拦截器、文件上传 │ ├── common/ # 统一返回结果、异常处理、工具类 │ └── EduApplication.java ├── src/main/resources/ │ ├── application.yml # 核心配置文件 │ └── mapper/ # MyBatis XML 文件 └── pom.xml这个分层最大的优点是职责单一。Controller 只做参数校验和调用 ServiceService 里处理事务和业务规则Mapper 只和数据库打交道。我见过不少新手项目Service 里直接拼 SQLController 里写业务逻辑最后改一个需求要动三层代码。这套源码保持了相对干净的分层你要加一个课程审核功能时改动范围可以控制在 Service 层和对应的 Mapper 方法里。3.2 登录认证基于 JWT 的无状态权限校验登录是任何管理系统绕不开的部分。这套源码没有用传统的 Session 方案而是采用 JWT 令牌。流程是这样用户提交账号密码后端校验通过后生成一个包含用户 ID、角色、过期时间的 Token 返回给前端前端存在 localStorage 里每次请求在请求头带上 Authorization 字段后端通过拦截器统一校验。为什么用 JWT 而不是 Session因为前后端分离部署后后端可能出现多个实例Session 需要额外引入 Redis 做共享存储而 JWT 是无状态的后端不需要存任何登录信息只要验签通过就放行。代价是 Token 无法主动失效这一点在源码里用设置较短过期时间的方式做了补偿一般两小时重新登录一次。我建议你重点看一下拦截器对白名单的处理。登录接口、注册接口、课程公开列表这些不需要鉴权的路径要放行其余接口全部走 Token 校验管理员接口再叠加角色判断。这块逻辑是整套系统安全的基础你自己二次开发时新增接口第一件事就是确认它需不需要加进鉴权范围。3.3 典型业务接口课程发布与选课的事务处理我选课程发布和选课这两个接口展开讲因为它们代表了两类典型的业务场景。课程发布接口的逻辑是教师提交课程基本信息标题、分类、封面图、简介后端先校验当前用户角色是否为教师然后判断同一学期是否已经发布了同名课程。通过校验后课程状态设为待审核管理员审核通过后才能在学员端展示。这里涉及一个细节课程封面图上传是单独的文件接口课程发布接口只接收图片 URL而不是二进制数据。文件上传接口单独处理统一返回可访问的 URL这样课程表里只存字符串查询性能更好。选课接口则涉及事务。学员点击选课时后端要做三件事校验该课程是否已满员、往选课表插入一条记录、将课程表的已选人数加一。这三步必须放在同一个事务里否则会出现人数加了但选课失败或者选课成功但人数没变的数据不一致问题。源码里用Transactional注解统一管理这一点非常关键。如果你要扩展功能比如加一个选课成功后自动发送通知就要注意事务提交的时机通知发送最好放在事务提交后的回调里避免消息发出去了但事务回滚导致数据对不上。4. 前端工程化实践Vue 页面组织与接口联调的细节4.1 路由设计前端为什么要做权限控制前端部分的技术栈是 Vue 全家桶搭配 Element UI 组件库。路由设计上采用了动态路由的思路登录成功后后端根据用户角色返回可访问的菜单列表前端用router.addRoutes动态注册而不是把所有路由一次性写死在代码里。这样做的好处有两个。第一管理员的系统管理菜单和学员的我的课程菜单天然隔离不会在侧边栏漏出无权限的入口第二即使有人手动在地址栏输入某个页面的路径由于路由根本没有注册会被重定向到 404 页面。当然要强调一点前端路由控制只是体验层面的真正的防越权必须靠后端接口的角色校验这一点前后端不能搞反。来看一个典型的路由配置写法const routes [ { path: /login, component: () import(/views/Login.vue), hidden: true }, { path: /dashboard, component: () import(/layout/Layout.vue), children: [ { path: , name: Dashboard, component: () import(/views/Dashboard.vue), meta: { title: 首页, icon: home } } ] } ];注意里面用了懒加载写法() import()这是 Vue 官方推荐的路由级代码分割方案首屏只加载必要的 JavaScript打开登录页和打开完整后台管理的速度差别还是很明显的。4.2 Axios 请求封装拦截器是联调的关键前端通过 Axios 调用后端接口源码里对 Axios 做了一层统一封装。核心逻辑集中在两个拦截器里请求拦截器负责从 localStorage 取出 Token拼到请求头的 Authorization 字段响应拦截器负责统一处理后端返回的 code如果遇到 401 就跳转登录页如果遇到业务错误就弹出 Message 提示。这里我要提一个实际联调中容易踩的坑后端接口返回的数据结构必须是统一的。比如定义返回值格式为{ code: 200, message: success, data: {...} }那么所有接口都要按这个格式返回。这套源码在 common 包里有一个Result类统一包装返回值前端响应拦截器才能用统一的方式解析。如果你接手后新增接口忘了用 Result 包装前端轻则拿不到数据重则整个页面报错排查起来相当费劲。我再分享一个调试技巧开发阶段把响应拦截器里加一段console.log打印完整响应联调的时候把浏览器控制台和后端日志对照着看绝大部分接口问题都能快速定位是参数格式不对、还是后端逻辑报错、还是 Token 失效。4.3 学员端和管理员端的差异化管理同一个系统里学员端和管理员端在前端呈现上差异很大。学员端偏浏览和操作比如课程列表需要卡片式展示、课程详情要有章节目录和作业入口管理员端偏表单和表格比如用户列表要有分页和搜索、课程审核需要批注和通过/驳回按钮。这套源码把这两块拆成了不同的视图目录共用一套请求封装和路由基础。实际开发中我建议保持这个习惯不要把所有页面堆在一个目录里。因为后端的接口是按角色区分的前端页面如果混在一起很容易在「管理员页面里调用学员接口」这种问题上纠缠不清。目录分开了代码归属一目了然。5. 数据库设计一张图画清所有业务关系5.1 核心表结构与字段规划数据库设计是这套系统最值得细看的部分。整套系统的表可以分成四类用户类、课程类、教学行为类、系统配置类。我列一下核心表清单表名作用关键字段sys_user用户表id, username, password, role, real_name, phonecourse课程表id, teacher_id, category_id, title, cover, status, student_countcourse_chapter章节表id, course_id, title, video_url, sort_ordercourse_enroll选课表id, course_id, user_id, enroll_timehomework作业表id, course_id, teacher_id, title, deadlinehomework_submit作业提交表id, homework_id, user_id, content, file_url, scorenotice公告表id, title, content, create_time用户表是系统的基础role字段用 int 类型区分角色1 表示管理员、2 表示教师、3 表示学员。课程表通过teacher_id关联用户表通过category_id关联分类表通过status字段控制课程状态流转待审核、已发布、已下线。这里有一个很好的设计细节选课表course_enroll里没有冗余储存课程名称和学员姓名而是只存两个外键 ID。查询的时候用 JOIN 关联出名称。有些新手会想直接冗余一个课程名字段查询多方便但在线教育的选课记录量大冗余字段会导致更新课程名称时要同步改一大批历史数据纯属给自己挖坑。5.2 容易被忽略的索引与约束设计看表结构时除了字段类型还要关注两个东西索引和外键约束。外键类字段如teacher_id,course_id,user_id必须建索引。原因是这些字段是高频查询条件比如查某个教师名下所有课程查某个学员选了哪些课。如果没建索引数据量过万后这类查询会走全表扫描响应时间从几毫秒飙到几百毫秒。源码里这些字段都建了普通索引符合规范。约束方面选课表要加唯一索引UNIQUE KEY (course_id, user_id)从数据库层面保证一个学员不能重复选择同一门课。这比只在代码里判断要可靠得多——因为并发请求时两个请求可能同时通过代码校验但唯一索引会拦住第二个插入。这也是为什么我之前强调选课接口要开事务数据库约束和事务配合才能真正保证数据不错。5.3 初始化 SQL 脚本的价值源码包里自带一个sql/edu.sql初始化脚本这是可直接运行的关键保障。脚本里包含了建库、建表、初始数据的完整内容。初始数据至少包括一个管理员账号、一个测试教师账号、一个测试学员账号以及几条示例课程数据。我拿到任何项目源码第一件事就是打开初始化 SQL检查三件事第一字符集是否统一设置为 utf8mb4否则中文和表情符号会乱码第二初始账号的密码是否加密存储如果存的是明文说明项目不成熟第三外键字段的数据类型是否和主表一致类型不一致会导致关联查询报错。这套源码在三点上都做得规范我导入过程没有遇到一个报错。6. 从源码到登录页完整的环境配置与启动步骤6.1 环境准备清单这一步我按实际踩坑经验来写。需要准备的工具和版本如下工具推荐版本说明JDK1.8 或 11SpringBoot 2.x 首选部分高版本 SpringBoot 需要 JDK 17Maven3.6负责拉取后端依赖MySQL5.7 或 8.05.7 更稳8.0 需注意驱动配置Node.js14 或 16前端构建环境数据库客户端Navicat 或命令行执行初始化脚本我个人的建议如果你对版本不熟悉直接采用 MySQL 5.7 JDK 1.8 Node.js 16 这个组合。这套组合兼容性最好网上遇到问题时搜到的解决方案也最多。Node.js 版本不要盲目追新因为部分老项目依赖的 Webpack 版本在高版本 Node 下会报 OpenSSL 错误。6.2 后端启动常规三步到位第一步用数据库客户端新建一个名为edu_db的数据库字符集选择 utf8mb4然后导入sql/edu.sql脚本。导入完成后确认几个核心表已生成。第二步修改后端application.yml里的数据库连接配置。重点看这几行spring: datasource: url: jdbc:mysql://localhost:3306/edu_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你的密码这里我特别解释两个参数serverTimezoneAsia/Shanghai必须加不然 Java 连接 MySQL 8 时会因为时区问题直接报错allowPublicKeyRetrievaltrue也要加这是 MySQL 8 使用 caching_sha2_password 认证插件时的必要配置。这两个参数是我见过的项目里最容易漏掉的。第三步在项目根目录执行mvn spring-boot:run。如果 Maven 已经配置好看到控制台输出Started EduApplication就说明后端启动成功端口默认 8080。也可以先mvn package打成 Jar 包再java -jar运行正式环境通常用这种方式。6.3 前端启动注意代理配置前端目录下执行npm install安装依赖。如果网速慢建议先配置 npm 国内镜像再把依赖装上。安装完成后执行npm run dev默认端口通常是 8081 或 9527。这里有一个关键配置前端开发服务器需要把接口请求代理到后端端口。在vue.config.js里配置如下module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };这样前端请求/api/course/list时开发服务器会把请求转发到后端http://localhost:8080/api/course/list并且修改请求头里的 Host 字段避免后端做域名校验时误判。没有这一步前端直接请求后端会触发跨域问题浏览器控制台报 CORS 错误。启动完成后浏览器访问前端地址用初始管理员账号登录看到后台首页和菜单就说明整套系统跑通了。7. 实际运行中的典型问题与上线前的优化建议7.1 启动阶段最常见的三个报错及排查思路我在跑这套源码时以及在帮别人排查时最常遇到三类问题。第一类是数据库连接失败错误信息通常是Access denied for user或者Communications link failure。前者是账号密码写错后者是数据库没启动或者端口不对。排查顺序是先用数据库客户端本地连接测试确认账号密码无误再看application.yml里的 IP、端口、数据库名是否和本地一致。第二类是端口被占用。后端启动时提示Port 8080 was already in use。这是因为我本地有其他服务占用了 8080 端口。解决办法是在application.yml里修改端口号但要记得同步修改前端代理的 target 地址。反过来如果前端端口被占则修改vue.config.js里的 port。第三类是前端依赖安装报错。Node.js 版本过高时安装老项目的依赖会出现digital envelope routines::unsupported错误。这个问题的本质是 OpenSSL 版本变更导致 Webpack 4 不兼容。解决办法有两个把 Node 版本降到 16或者 Windows 下执行set NODE_OPTIONS--openssl-legacy-provider npm run dev。我实测降 Node 版本最省心一劳永逸。7.2 从开发环境到上线部署要做的五件事开发环境跑通只是第一步真正上线时要处理的事情还不少。我按优先级列一下第一修改默认密码并把密码字段的加密强度提上去。源码里初始账号密码是固定的上线第一天就要让所有用户改密码管理员账号建议开通两步验证。第二配置 HTTPS 和域名访问。学员端涉及密码、成绩等敏感信息明文 HTTP 在公网环境是不合格的。用 Nginx 做反向代理同时托管前端静态文件和转发后端接口。第三开发环境与生产环境的配置文件分离。SpringBoot 支持application-dev.yml和application-prod.yml多环境配置数据库密码不能直接写在可能被提交到代码仓库的配置文件里生产环境用环境变量注入。第四定期备份数据库。在线教育系统的选课记录、成绩数据丢了几乎是灾难性的。最少每天做一次全量备份保存至少两周的备份文件。第五关注慢查询与接口性能。上线后用 MySQL 的慢查询日志分析哪些接口响应慢针对性加索引或者加缓存。比如课程列表这类读多写少的接口可以引入 Redis 做缓存选课接口如果在大型活动时有抢课场景可以在事务里用乐观锁或者行锁控制并发避免超额选课。7.3 二次开发的话优先从这几个方向切入如果你拿到这套源码不只是学习而是要做成自己的产品我建议按用户量从少到多的节奏逐步扩展。第一阶段先做课程搜索和筛选给课程表的关键字段加索引第二阶段引入消息通知模块把作业批改、课程审核结果主动推送给用户第三阶段再做在线支付和订单管理把单纯的教务系统升级为可盈利的在线教育平台。每次扩展都要守住一个原则先确认表结构再写接口最后做前端页面。因为前端页面是最容易改的表结构却是牵一发动全身。我个人做二次开发时会先把要新增的表画成关系图跟现有的course_enroll、sys_user理清楚关联再动代码基本不会返工。最后再说一句运行层面的话。这套源码我本地跑通用的时间大概二十分钟大部分时间花在npm install上。像这种前后端分离的工程最重要的就是把数据库脚本、配置文件、代理设置这三样理顺。理顺之后它就是一套非常扎实的在线教育管理系统地基往上盖什么楼就看你的业务想象力了。
返回列表