很多人拿到一套企业级在线学习系统的源码,第一反应是赶紧跑起来看看界面长什么样。但真正把这套 SpringBoot + Vue + MyBatis + MySQL 架构吃透,才是让它在简历上、在真实项目里真正值钱的关键。这套源码覆盖了在线教育最常见的业务闭环:课程展示、在线学习、互动答疑、后台管理,而且前后端分离、接口清晰。我不打算只给你罗列功能清单,而是从一个实际开发者的角度,把架构设计、核心代码思路、数据库表结构、部署踩坑一次讲明白。无论你是准备二次开发的程序员,还是想搞懂在线学习系统原理的产品经理,这篇文章都值得你花十分钟静下心看完。
1. 内容整体设计与思路拆解
1.1 为什么是 SpringBoot + Vue + MyBatis 这套组合
先说后端。SpringBoot 在 Java 生态里的地位不用我多赘述,它解决了传统 SSM 框架大量 XML 配置的痛点,“约定大于配置”的设计理念让项目启动和部署都变得极其轻量。内嵌的 Tomcat 容器让开发者不再需要单独配置外部服务器,打包成一个 jar 包就能直接跑,这对企业级项目的交付部署来说,节省的运维成本是实打实的。
Vue 作为前端框架,核心优势在于它的响应式数据绑定和组件化开发。在线学习网站涉及到的页面类型非常多:首页、课程列表、课程详情、视频播放页、个人中心、后台管理界面。如果是传统 jQuery 写法的多页面应用,光是维护页面之间的状态同步就能让开发者崩溃。Vue 的单文件组件(SFC)把 HTML、CSS、JavaScript 都封装在一个.vue文件里,加上 Vuex 或 Pinia 做全局状态管理,前后端通过 RESTful API 通信,开发效率完全是两个量级。
MyBatis 在这套架构里的角色是半自动 ORM 框架。相比 Hibernate 的全自动映射,MyBatis 把 SQL 的控制权完全交还给开发者。在线学习系统的查询场景极其复杂:课程的多条件筛选、学习进度的多表关联查询、用户行为的统计分析,这些场景下 SQL 的可控性比开发效率更重要。而且 MyBatis 的动态 SQL 功能能够在不写大量 Java 代码的情况下处理复杂的查询条件拼接,配合二级缓存,性能表现也很稳妥。
1.2 企业级项目在架构上必须考虑什么
这套源码既然标注了“企业级”,那它的设计目标就不同于学校作业或者个人 Demo。企业级意味着三点:第一是高可用,系统不能动不动就宕机;第二是可维护性,代码结构要清晰到后来接手的程序员能快速上手;第三是可扩展性,业务增长了系统能平滑升级而不是推倒重来。
从源码的包结构来看,它采用的是标准的分层架构:Controller 层负责接收请求和参数校验,Service 层处理业务逻辑,Mapper(DAO)层只负责数据库交互。这种分层的核心价值在于解耦。举个例子,如果将来想把学习记录从 MySQL 迁到 Redis 或者增加消息队列,只需要改动 Service 层和 Mapper 层,Controller 层完全不用动。
前端方面,Vue Router 的路由守卫设计做得比较到位。在线学习系统的权限控制涉及多个角色:未登录用户只能看课程介绍,登录用户可以学习课程,讲师可以上传课程,管理员可以管理整个平台。路由守卫配合后端的拦截器做双重验证,前端控制"能不能看到这个页面",后端控制"能不能执行这个操作",安全体系才算是完整的。
数据库层面,源码采用的是 MySQL 的 InnoDB 引擎,支持事务和外键约束。课程购买、学习进度更新、问答互动这些操作都涉及多条记录的更新,事务的 ACID 特性保证了数据的一致性。举个例子,用户下单购买课程时,需要同时更新订单表、用户课程关联表、课程销量统计表,如果不用事务,任何一步失败都会导致数据脏读或者错乱。
1.3 这套系统解决了什么真实痛点
市面上很多在线学习平台要么是纯内容展示的“视频网站”,要么是只有后台管理的“半成品”。这套源码打通了用户从浏览课程到购买学习,再到提问互动、完成学习的完整闭环。从用户角度看,它解决了“课程资源分散、学习过程无记录、问题无处解答”的痛点;从管理员角度看,它解决了“课程上下架管理混乱、用户数据不透明、内容审核缺乏工具”的痛点。
尤其值得说的是它的互动设计。在线学习最怕的是“看完视频就结束了”,没有练习、没有答疑、没有交流,学习效果大打折扣。我在源码里看到课程评论、问答社区、学习笔记这些模块,说明设计者在关注“学习”而不是单纯的“看课”。这些互动数据反过来又能成为平台运营的分析素材,哪些课程讨论热度高、哪些课程差评多,一目了然,这在真实运营场景中非常重要。
2. 核心功能模块与数据库设计
2.1 系统功能地图全览
我先画一张功能地图让你的脑海里有整体概念。这个系统分为前台用户端和后台管理端两个大区。
前台用户端包含:
- 用户注册登录(手机号/邮箱验证)
- 课程浏览搜索(分类筛选、关键词检索)
- 课程详情页(视频播放、课程目录、讲师信息、用户评价)
- 在线购买下单(模拟支付流程)
- 个人学习中心(我的课程、学习进度、我的笔记、我的问答)
- 互动社区(课程问答、学习笔记分享)
后台管理端包含:
- 统计分析仪表盘(用户增长、课程销量、活跃度)
- 课程管理(课程录入、章节维护、上下架操作)
- 用户管理(用户列表、状态管理、角色分配)
- 订单管理(订单查询、退款处理)
- 内容审核(评论审核、问答审核)
- 讲师管理(讲师入驻、课程归属)
2.2 数据库表结构剖析
这套源码的数据库设计的核心价值,也是面试官最爱深挖的地方。我基于常见的企业级设计规范,结合源码的业务逻辑,整理出核心表的清单:
| 表名称 | 核心用途 | 关键字段说明 |
|---|---|---|
user | 用户信息表 | id、username、password(加密存储)、role(角色)、status(状态) |
course_category | 课程分类表 | id、parent_id 支持多级分类 |
course_info | 课程信息表 | id、title、price、cover、status、teacher_id、category_id |
course_chapter | 课程章节表 | id、course_id、chapter_name、sort_order |
course_lesson | 课程课时表 | id、chapter_id、lesson_name、video_url、duration |
user_course | 用户选课关联表 | id、user_id、course_id、learn_status、progress |
order_info | 订单信息表 | id、order_no、user_id、course_id、amount、status |
course_comment | 课程评论表 | id、course_id、user_id、content、parent_id、praise_count |
learning_note | 学习笔记表 | id、user_id、course_id、lesson_id、content、is_public |
question_answer | 问答表 | id、course_id、user_id、title、content、resolve_status |
设计上比较巧妙的地方在于user_course关联表的用途。用户购买课程后会产生一条关联记录,后续所有操作比如学习进度更新、课程评价权限、笔记归属都基于这张表进行校验。如果只是简单地用订单表判断“买没买”,后续扩展会出现很多逻辑漏洞。
course_chapter和course_lesson的父子关系用的是sort_order字段排序,而不是直接依赖自增 ID 排序。这个细节很关键,因为章节支持插入、移动操作时,自增 ID 无法体现业务排序意图,单独维护一个排序字段才能在界面上灵活调整顺序。
订单号的设计我建议参考源码中的“时间戳+随机数”方案,不要用自增 ID 直接作为订单号暴露给用户。因为订单号会出现在支付回调、客服查询等多个场景,自增 ID 容易被遍历,有数据泄露风险。订单号还应当加入用户 ID 的加密片段,用于快速追溯归属。
2.3 设计规范与索引优化建议
在线学习系统数据库最容易出性能问题的场景是课程列表页的筛选查询和用户学习记录的频繁更新。我建议重点给以下字段建索引:
course_info表的category_id、status联合索引(用于课程列表分类筛选)course_comment表的course_id索引(用于查看某课程全部评论)user_course表的user_id和course_id联合唯一索引(防止重复选课)
字符集选择上,MySQL 要使用 utf8mb4 而不是 utf8,核心原因是 utf8 在 MySQL 中最多支持 3 个字节,导致常见的 emoji 表情无法存储。用户评论和昵称里出现 emoji 是大概率事件,如果选错字符集,到时候线上会报Incorrect string value错误,排查起来非常麻烦。
3. 环境搭建与核心实现细节
3.1 开发环境版本选择经验
这套技术栈对环境版本比较敏感,我列一个实测稳定的版本组合供你参考:
- JDK 1.8 或 11
- Maven 3.6+
- Node.js 14.x 或 16.x
- MySQL 5.7+ 或 8.0
- Redis(可选,用于验证码缓存和会话管理)
JDK 版本不要追求过新,有些 SpringBoot 版本在高版本 JDK 上会有兼容问题。同样的道理,Node.js 版本过高可能遇到 node-sass 编译失败的老大难问题。我建议先用这套保守的版本组合把项目跑起来,后续再逐步升级。
3.2 后端项目初始化与配置解读
拿到源码后,第一步是用 Maven 命令编译项目依赖,不要直接在 IDE 里等自动导入,那个过程容易卡住而且看不到错误信息。
mvn clean install -DskipTests后端核心配置文件是application.yml,重点修改数据源配置:
spring: datasource: url: jdbc:mysql://localhost:3306/learning_platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver这里有几个容易踩坑的细节:
serverTimezone必须设置,否则 MySQL 8.0 会因为时区问题报错characterEncoding=utf8不是 utf8mb4,但连接层面一般用这个就够了,真正决定存储的是表结构字符集- MySQL 8.0 的驱动类名是
com.mysql.cj.jdbc.Driver,老版本 MySQL 驱动才是com.mysql.jdbc.Driver,写错的话启动直接报错
MyBatis 的配置关注两个点:别名包扫描和 Mapper XML 位置。源码里通常会在application.yml中配置:
mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case这个配置非常关键。数据库字段是user_name这种下划线命名,Java 实体是userName驼峰命名,没有这个配置的话,每次查询结果都映射不上,出现全是 null 的诡异现象。
3.3 前端项目初始化与接口联调
前端的启动流程通常是进入frontend目录(源码的命名可能是web或vue-app):
npm install npm run devnpm install如果速度很慢建议切换淘宝镜像源:
npm config set registry https://registry.npmmirror.com前后端联调时烦人的就是跨域问题。开发环境下源码通常已经配置了代理转发,配置文件在vue.config.js:
module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }这个代理配置的意思是,前端页面访问/api/course/list的时候,实际转发给后端http://localhost:8080/api/course/list。changeOrigin设置为 true 是为了让请求头中的 Host 字段指向目标地址,避免后端某些鉴权逻辑误判。如果跨域请求还是报错,优先检查代理配置而不是在后端写@CrossOrigin注解,后者的方案在线上环境会有安全风险。
3.4 核心功能实现思路:课程学习进度
学习进度是整个系统的核心逻辑之一。视频播放时前端要定时上报进度,后端记录“学到第几节课、看了多久”。源码的实现思路一般是这样:
前端通过定时器每 15 秒向后端发送一次心跳:
setInterval(() => { const currentTime = player.getCurrentTime(); axios.post('/api/learn/progress', { courseId: this.courseId, lessonId: this.currentLessonId, duration: currentTime }); }, 15000);后端接口接收后更新user_course表:
@PostMapping("/api/learn/progress") public Result updateProgress(@RequestBody ProgressVO progressVO) { UserCourse userCourse = userCourseMapper.selectByUserAndCourse(progressVO.getUserId(), progressVO.getCourseId()); if (userCourse == null) { // 未选课用户不能上报学习进度 return Result.error("请先购买课程"); } userCourse.setProgress(progressVO.getDuration()); userCourse.setUpdateTime(new Date()); userCourseMapper.updateById(userCourse); return Result.ok(); }这里涉及到一个判断逻辑:如果用户没有购买课程,后端有权拒绝写入学习进度。放在真实项目里,还要加一层“试看”限制,即未购买用户最多观看前 5 分钟。这个判断在视频加载时就要做好,而不是等进度上报才处理。
3.5 后端鉴权的完整链路
企业级系统的鉴权链路我建议你用这条思路去理解源码:登录获取令牌(Token),后续请求带上令牌,后端解析令牌获得用户身份。源码如果用的是 JWT,那么工具类中一般会有三个方法:
- 生成令牌:用户登录成功后,把用户 ID 和角色信息放进 payload
- 解析令牌:从请求头中取出令牌,验证签名并提取用户信息
- 过期判断:设置过期时间(常见是 2 小时),过期后前端跳到登录页
public String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(userId.toString()) .claim("role", role) .setExpiration(new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器实现里需要配置哪些路径放行、哪些路径必须登录、哪些路径只允许管理员:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/api/**") .excludePathPatterns("/api/login", "/api/register", "/api/course/list", "/api/course/detail/**"); }这里我遇到的常见坑是:用户删除了本地 Token 中的某个数据,但 Token 本身还有效,后端无法感知。解决思路是把 Token 的剩余有效时间缩短,配合 Redis 做服务端状态管理。企业级项目实质上更倾向于 Redis + Token 的双重校验,但源码为了降低部署复杂度,通常用纯 JWT 方案。你二次开发的时候可以根据业务要求升级。
4. 部署上线流程与性能调优
4.1 从开发环境到生产环境的配置迁移
开发环境跑通只是第一步,真正磨人的是部署上线。开发环境里数据库密码、Redis 地址都写在application.yml里,但生产环境不能这么干。正确做法是使用 SpringBoot 的多环境配置文件:
application-dev.yml:开发环境application-prod.yml:生产环境application.yml:公共配置
启动时通过参数指定环境:
java -jar learning-platform.jar --spring.profiles.active=prod生产环境的数据库地址、密码建议通过环境变量传递,比如:
spring: datasource: url: jdbc:mysql://${DB_HOST}:${DB_PORT}/learning_platform username: ${DB_USERNAME} password: ${DB_PASSWORD}这样配置文件和代码分开,数据库密码不会泄露到代码仓库里。我在实际部署时吃过这个亏——公司内部 Git 仓库里明文存了生产数据库密码,后来做安全审计时被点名整改。这些细节既是开发规范问题,也是安全意识问题。
4.2 前端构建与部署
前端部署的第一步是构建静态资源:
npm run build构建产物会生成在dist目录,里面是纯静态文件(HTML、CSS、JS)。部署方式推荐用 Nginx 托管静态文件,同时配置反向代理把/api请求转发到后端服务。
Nginx 的配置示例:
server { listen 80; server_name your-domain.com; root /var/www/learning-platform/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }try_files $uri $uri/ /index.html这行配置对 Vue Router 的 history 模式是必须的。如果不加,刷新页面到/course/101这类路由时,Nginx 会返回 404,因为服务器上并不存在这个路径对应的物理文件。加了这条规则后,所有不存在的路径都回退到index.html,由前端路由接管。
前端部署一个容易忽略的坑是——图片和视频资源不能全放在dist里,课程视频这种大体积资源应该走独立的对象存储或者 CDN,否则应用服务器的带宽会被视频流量打满。课程视频建议使用云厂商的存储服务,数据库里只保存视频的 URL 地址。
4.3 数据库迁移与初始化数据
生产环境建库的推荐思路是:先执行源码提供的init.sql脚本建好表结构,再核对是否需要变更。源码自带初始数据通常是账号体系和课程分类数据,其中初始管理员账号默认密码务必在第一次登录后修改。
数据库迁移过程中有一个有时间效果的小技巧:用mysqldump导出生产库数据时,加上--single-transaction参数可以在不锁表的情况下导出,避免影响线上业务。
mysqldump -u username -p --single-transaction --databases learning_platform > backup.sql导出的 SQL 导入到新库时,加引号的utf8mb4条件直接决定后续数据是否能正常存中文和 emoji。别小看这个步骤,数据导入后才发现表结构字符集不对,把所有表重建一遍的事情我干过一次,光耗时就是一个下午,这个教训相当深刻。
4.4 性能调优的针对性方案
在线学习系统最消耗性能的环节是视频播放和课程列表查询。视频播放的优化思路是转码和分发,和代码关系不大;课程列表查询就是实打实的数据库优化了。
课程列表如果数据量上了十万条,直接SELECT * FROM course_info WHERE category_id=?是撑不住的。常见的优化手段有两个:
第一,分页查询一定要用“覆盖索引”。所谓覆盖索引,就是查询的字段全部命中索引,不需要回表查数据行。比如列表页只需要显示课程 id、标题、封面、价格,那么建一个(category_id, status, id, title, cover, price)的复合索引,查询就会快很多。
第二,热点数据加缓存。首页展示的课程列表、热门课程排行榜这类访问频繁且更新不频繁的数据,用 Redis 缓存起来可以把响应时间从几百毫秒降到十几毫秒。缓存更新策略用“先更新数据库,再删除缓存”的套路,可以规避缓存与数据库不一致的大部分风险。
还记得我之前提到的user_course关联表吗?它在学习进度查询时同样是高频热点。用户每次进入“我的课程”页面都要查这张表,如果不加缓存,数据库压力会逐渐增大。但这个表的更新频率也高(学习进度在变),缓存过期时间设置短一点(比如 5 分钟),既能扛住读压力又能接受数据短暂延迟。
5. 常见问题与排查技巧实录
5.1 后端启动失败的三个高频原因
端口被占用。SpringBoot 默认使用 8080 端口,如果本机已经有服务占用了,日志里会看到Port 8080 was already in use。解决方式:
# 查找占用进程 lsof -i :8080 # 杀掉占用进程 kill -9 PID或者直接修改配置文件中的端口。我个人不推荐改端口来躲避问题,因为后续联调时的接口地址、前端代理配置都要跟着改,容易出不一致的问题。
数据库连接不上。报错信息一般是Communications link failure或者Access denied for user。先检查 MySQL 服务是否启动:
systemctl status mysql再检查账号密码和访问权限。MySQL 8.0 默认的认证插件是caching_sha2_password,某些旧版数据库驱动不支持,需要在 MySQL 里执行:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';不过如果你用的是最新版 MySQL Connector/J 驱动,这个问题通常不会遇到。
MyBatis Mapper 找不到。报错信息是Invalid bound statement (not found)。先确认application.yml里的mapper-locations路径是否和 Mapper XML 实际存放路径一致,再看 XML 里的 namespace 是否和 Mapper 接口的全限定名完全一致。这两个不一致是百分之百报这个错误的。
5.2 前端联调时的跨域和接口 404
前端页面能打开但接口全部 404,基本可以确定是代理配置问题。我在 3.2 节写过vue.config.js的代理配置,检查一下代理前缀是否和后端 Controller 的 RequestMapping 前缀一致。
如果接口报 401 或者 403,多半是 Token 在请求头里没带或者过期。前端封装 Axios 时通常会设置请求拦截器:
axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; });如果 Token 已经存入 localStorage 但还是报 401,回后端看一眼拦截器里 Token 解析的逻辑:是从 Header 的Authorization字段取,还是从请求参数里取,两边必须对齐。
5.3 视频播放不了或者加载缓慢
视频播放问题要区分是本地测试还是线上部署。
本地测试时视频加载不了,原因大概率是视频文件存放路径和代码中的映射路径不一致。源码通常会配置静态资源映射:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/video/**") .addResourceLocations("file:D:/upload/video/"); } }这个配置的意思是,浏览器访问/video/abc.mp4时,后端会到本机D:/upload/video/abc.mp4这个路径读取文件。如果路径不匹配,返回 404 是必然的。
线上部署时视频加载缓慢,大概率是带宽瓶颈。视频文件应该走 CDN 或者对象存储。真要临时处理的话,可以在 Nginx 配置视频文件的缓存:
location /video/ { expires 7d; add_header Cache-Control "public"; }这样至少能让用户第二次访问走浏览器缓存,减轻服务器带宽压力。
5.4 数据库乱码和时区问题
数据库插入中文数据后显示???,这是连接字符集没有指定的经典症状。检查application.yml里的数据源 URL 是否有characterEncoding=utf8,以及表结构的字符集是否是utf8mb4。
修改表字符集:
ALTER TABLE course_info CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;时区问题表现在new Date()存入数据库的时间和北京时间差了 8 个小时。排查思路也是两处:数据源 URL 是否加了serverTimezone=Asia/Shanghai,以及 JDBC 驱动版本是不是太老。
5.5 经验教训:日志是你最好的排障工具
排查问题时的第一件事不是看代码,而是看日志。SpringBoot 内置了logback日志框架,源码里通常会在application.yml配置日志级别和输出位置:
logging: level: com.example.mapper: debug file: name: logs/learning-platform.log把 Mapper 层日志级别调成 debug 后,MyBatis 执行的每一条 SQL 以及传入的参数值都会打印出来。这个操作的排查价值非常大。无论是数据查询结果不对,还是 SQL 语法报错,日志里都能直接看到“实际执行了什么样的 SQL、用了什么参数”。用上这个功能,排查时间至少能缩短一半。
还有一个小技巧:Java 后端异常堆栈信息里,找到第一行带com.example包名的位置,那个才是真正出错的位置。剩下的框架层报错信息大多是 Java 反射调用引起的,排查价值有限。经验不足的开发者经常盯着最底下一长串看,耗时耗力还不解决问题。
6. 二次开发扩展建议与运营视角
6.1 源码的二次开发方向
这套系统扩展性最好的模块是课程和用户之间松耦合的关联设计。如果你接到了一个真实的教育公司项目,最常新增的需求会有这几个方向:
第一,接入真实支付。源码中的支付流程通常是模拟的,二开时只需要替换成微信支付或支付宝支付的 SDK 调用。设计思路上要保证“订单创建”和“支付回调”的职责分离:订单创建是用户点击购买时同步完成的,支付回调是异步通知的。
第二,增加直播功能。在线互动学习对直播的需求近年来越来越强。二开时可以接入第三方直播服务的 SDK,在课程章节表中增加live_url和live_time字段,复用用户选课关联表的权限控制。
第三,引入消息推送。用户在问答区提问后,讲师需要第一时间收到通知。二次开发时引入 WebSocket 或者推送服务,实现“提问-回答-通知”的闭环。
6.2 从运营角度理解系统设计
做技术开发的容易陷入一个误区:只关心功能实现得好不好,不关心业务跑不跑得通。但企业级系统的源码价值,恰恰在于它的业务闭环是否完整。
从运营视角看这套系统:
- 课程分类体系决定了用户能不能快速找到想学的内容
- 课程的试看机制决定了用户愿不愿意留下来转化为付费用户
- 问答互动区的活跃度决定了用户粘性和复购率
- 后台的学习数据统计决定了运营人员能不能及时调整教学内容
如果站在这个角度去阅读源码,你会更清楚为什么课程表里要有status(上架/下架),为什么用户表里要有status(正常/禁用),为什么订单表里要有status(待支付/已支付/已退款)。每一个状态字段背后都有一个业务操作入口,理解了业务流转,你才算真正读懂了一套企业级源码。
6.3 上线前的安全检查清单
收到任何一套源码,上线前要做的就是安全自查。基于经验,建议按这个清单逐项确认:
- 默认管理员密码是否已更改
- 数据源密码是否由环境变量注入,没有明文写在配置中
- 前端请求是否有接口鉴权(防直接通过 URL 访问后台接口)
- 文件上传是否有大小限制和类型白名单
- SQL 语句是否全部使用预编译(MyBatis 的
#{}是预编译,${}是字符串拼接,有 SQL 注入风险) - 日志中是否可能打印用户明文密码
最后一项值得展开一下。我在真实项目中见过日志里打印登录请求参数的案例,用户密码明文被打进了日志文件。如果这个日志文件泄露出去,所有用户的账号信息都会暴露。安全无小事,尤其要守住“生产环境日志不打印敏感信息”这条底线。
6.4 数据备份与容灾
企业级系统的另一层保障是备份策略。只把代码跑起来不算交付,必须考虑数据万一丢了怎么办。MySQL 的备份至少要有两种:
逻辑备份(用 mysqldump 导出 SQL 文件),适合数据量不大的时候,恢复灵活;物理备份(直接复制 MySQL 数据目录),适合大数据量的快速恢复场景。备份策略最好是每天全量 + 每小时增量(依赖 MySQL binlog),恢复时最多丢不到一小时的数据。
定时备份可以用 Linux 的 crontab:
0 2 * * * /usr/bin/mysqldump -u backup_user -p'password' learning_platform | gzip > /backup/learning_platform_$(date +\%Y\%m\%d).sql.gz备份文件不要和数据库放在同一台服务器上,否则服务器磁盘损坏时数据就全都丢了。这一点虽然不是代码问题,却是“企业级”这三个字逃不掉的责任。
7. 写在最后的一点体会
把这套源码完整跑起来、读明白、再动手改一遍,整个过程走下来,你对 SpringBoot+Vue+MyBatis+MySQL 这套技术栈的理解会上一个台阶。这套源码本身是个“项目结晶”——它把在线教育领域最常见的业务模式、权限模型、数据关系都固化在代码里了。相比网上拼凑的零散教程,阅读一套结构完整的系统源码,学习效率完全不一样。
我个人建议的学习路线是:先跑通再读代码,先读后端再读前端,先搞懂数据表再深入业务逻辑。遇到报错不要急着百度,先看日志、理思路,大多数问题你自己能定位到七八成。源码只是起点,把它消化后能在真实业务场景中举一反三,那才算真正把价值装进了自己的脑袋。比如后面你再去写商城系统、预约系统、内容管理系统,都会发现这套在线学习系统的影子——用户体系、商品(课程)体系、订单体系、互动体系,所有的交易类平台核心骨架都是类似的。把这套东西吃透了,比东一榔头西一棒子看一百个教程有用得多。