SpringBoot+Vue+MyBatis+MySQL在线教育平台系统完整开发实战

发布时间:2026/9/15 2:44:40
SpringBoot+Vue+MyBatis+MySQL在线教育平台系统完整开发实战 做Java开发这些年前后端分离的项目我经手了不少但真正让我觉得“拿得出手、有完整业务闭环、还能直接部署上线”的还得是这套基于SpringBootVueMyBatisMySQL的在线教育平台系统。这个组合几乎是当前国内Java后端岗位最主流的标配网上教程一大堆但大多停留在“跑个HelloWorld”或者“做个单表CRUD”的层面真正能串起用户、课程、视频播放、订单支付、后台管理这一整条业务链的项目并不多见。今天我把这套系统的架构思路、核心代码逻辑、前后端对接的Token处理方案以及从零到上线的完整部署步骤全部拆开来讲适合正在学SpringBoot和Vue、想找项目练手或者准备毕业设计的开发者参考。你跟着走一遍不只是会写接口而是能理解一个真实商业项目是怎么从设计到落地跑起来的。1. 项目定位一个在线教育平台到底包含哪些核心能力1.1 核心需求拆解不是简单CRUD而是一条完整业务链做项目最忌讳一上来就写代码。先想清楚这个系统要服务谁、解决什么问题。在线教育平台面向三类角色普通用户学生、讲师管理员、系统运营方。学生要能浏览课程、查看课程详情、试看视频、购买课程、学习已购课程讲师要能管理自己名下的课程和章节运营方则要处理课程上下架、用户管理、订单管理这些后台事务。我在这套系统里把业务模块划分成五块用户认证模块、课程内容模块、视频播放模块、订单交易模块、后台管理模块。用户认证解决“你是谁”课程内容解决“看什么”视频播放解决“怎么看”订单交易解决“怎么付费”后台管理解决“怎么运营”。任何一个环节缺失这个平台都不完整。很多人做项目只做到课程展示加个登录就结束了但真正放到生产环境下视频播放的鉴权、订单的幂等性处理、后台权限的细粒度控制才是最能体现工程能力的部分。1.2 技术选型为什么是SpringBootVueMyBatisMySQL这套组合选这套技术栈不是因为它“新”而是因为它“稳”且“通用”。后端的SpringBoot框架解决了传统SSH项目里繁琐的XML配置问题内嵌Tomcat让部署变得极其简单一个jar包扔服务器上就能跑。前端Vue作为渐进式框架既适合快速开发又有成熟的Element UI组件库支撑后台管理界面。数据层用MyBatis而不是MyBatis-Plus或者JPA核心原因是SQL的可控性。在线教育平台里有大量多表关联查询和动态条件查询MyBatis的XML文件能让你精确控制每一条SQL语句排查性能问题的时候特别直观。数据库选MySQL是市场占有率决定的无论是云厂商支持还是社区资料MySQL都是最稳妥的选择。这里插一句很多人问“直接用若依框架不就行了”确实若依这种前后端分离的脚手架能快速生成后台管理功能我在实际工作中也用。但学习阶段我强烈建议你自己从零搭一遍。因为脚手架帮你屏蔽了Spring Security的过滤链、JWT的Token校验、动态路由的加载这些底层细节你虽然能改代码但不知道为什么要这么改。这套系统我不引入任何重量级安全框架只用SpringBoot拦截器加JWT手写一套轻量鉴权目的就是让你把Token的前后端流转机制彻底吃透。2. 数据库设计与核心功能模块拆解2.1 表结构设计从用户到订单的完整数据建模在线教育平台的数据库设计是整个系统的地基。我以主流的“用户-角色-课程-章节-视频-订单-评论”为维度设计数据表下面是我在实际项目中最终落地的核心表结构。用户表sys_user是系统的入口字段包括id主键、username用户名、password密码存储加密后的密文、nickname昵称、avatar头像URL、phone手机号、email邮箱、status账户状态1启用0禁用、create_time创建时间、update_time更新时间。密码字段这里要特别说明绝对不能明文存储项目里我用的是MD5加盐的方式虽然现代安全标准更推荐BCrypt但在教学项目中MD5加盐更容易理解生产环境替换成BCrypt也很简单。角色表sys_role不复杂id、role_name角色名、role_code角色编码比如ADMIN、TEACHER、STUDENT。用户表和角色表通过user_id和role_id建立关联登录后把角色编码写进JWT的claims里后续接口鉴权就靠解析这个角色编码判断权限。课程表edu_course是内容的核心id、title课程标题、cover封面图URL、price价格单位用分为单位存储避免浮点数精度问题、teacher_id讲师ID关联用户表、description课程简介、status上下架状态1上架0下架、buy_count购买人数、create_time、update_time。课程表这里我故意不做分类字段因为分类逻辑跟业务强相关后续扩展的时候加一张分类表用多对多关联更合理。章节表edu_chapter和视频表edu_video是嵌套关系。一门课程包含多个章节一个章节包含多个视频小节。edu_chapter字段id、course_id课程ID、title章节标题、sort排序权重。edu_video字段id、chapter_id章节ID、title小节标题、video_url视频地址这里存的是m3u8播放列表的URL、duration视频时长秒、play_count播放次数、free是否免费试看1免费0收费、sort排序权重。之所以要把视频单独拆一张表因为一个章节下可能有多个视频而且视频需要记录独立的播放次数和免费试看标记这些字段挂在章节上不合适。订单表edu_order承载交易数据id、order_no订单编号业务唯一键用时间戳加随机数生成、user_id用户ID、course_id课程ID、amount订单金额分、status订单状态1待支付2已支付3已取消、create_time、pay_time支付时间。订单表是整个系统中并发要求最高的表下单接口必须考虑幂等性防止用户重复点击生成多个订单。我的处理方案是先查询该用户是否已有该课程的有效订单有则直接返回没有再创建新订单。评论表edu_comment用于课程评价id、user_id、course_id、content内容、parent_id父评论ID支持楼中楼回复、create_time。评论表跟用户表和课程表都是多对一关系查询的时候需要关联用户表拿昵称头像。2.2 关键业务逻辑设计权限控制、订单流程、视频鉴权权限控制这块系统里最核心的是登录拦截和角色校验。我选择用JWTJSON Web Token SpringBoot拦截器的方式实现不引入Spring Security因为对教学项目来说Security的过滤器链太复杂容易把注意力从业务逻辑转移到框架配置上。用户登录成功后后端生成一个包含userId和roleCode的Token返回给前端前端存储在localStorage里。后续每次请求前端在请求头带上Authorization字段后端拦截器统一解析校验。订单流程这里要重点讲一下幂等性问题。用户购买课程时点击“立即购买”如果前端没做防重复提交用户快速点了两下就会发出两个请求。后端下单接口的常规防线是先查订单表里是否已有该用户对该课程的待支付或已支付订单如果有就直接返回已有订单编号。这个查询要加上用户ID和课程ID两个条件因为一个用户可能购买多门课程。另外订单金额必须以后端查询的课程价格为准绝对不能信任前端传来的金额字段否则用户改一下请求参数就能用1分钱买课。视频鉴权是教育平台特有的需求。不是所有视频都对所有人开放免费试看课节可以直接播放付费课节必须校验当前用户是否已购买该课程。我的实现方案是视频播放地址不直接暴露静态路径而是通过一个受保护的接口/api/video/play/{videoId}来获取。这个接口内部先判断视频是否免费再判断用户是否已购买通过后才返回拼接好的m3u8播放地址。这样既实现了权限控制又避免视频地址被爬虫抓走后直接盗链。3. 后端核心实现SpringBootMyBatis的实战细节3.1 项目初始化与公共配置从IDEA创建到统一响应封装用IDEA创建SpringBoot项目的时候很多人会卡在版本选择上。如果你电脑装的是JDK 8Spring Boot版本就选2.7.x如果你装的是JDK 17可以直接用Spring Boot 3.x。这里要特别提醒Spring Boot 3.x相比2.x有本质变化javax包名换成了jakarta很多老教程的代码直接搬过来会编译报错。项目里我为了兼顾主流生产环境选用Spring Boot 2.7.18 JDK 8这套组合稳定、兼容性好、网上遇到的问题基本都有解决方案。创建项目时需要引入的核心依赖就几个spring-boot-starter-webWeb基础、mybatis-spring-boot-starterMyBatis与SpringBoot整合的官方starter、mysql-connector-jMySQL驱动、jjwt-api、jjwt-impl、jjwt-jacksonJWT生成解析、lombok简化实体类代码。不需要额外引入其他东西够用即可。application.yml配置文件里要关注几个点。数据源配置用spring.datasource.urljdbc:mysql://localhost:3306/edu?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai这里一定要加serverTimezone参数否则MySQL 8.x版本连接会报时区错误。MyBatis配置要开启驼峰映射mybatis.configuration.map-underscore-to-camel-casetrue这样数据库字段user_id能自动映射到Java属性的userId省去大量ResultMap的编写。再就是SQL打印配置logging.level.com.example.mapperdebug开发阶段必须能看到执行的SQL语句这是排查问题的基础技能。统一响应结果封装是每个SpringBoot项目都要做的。我定义了一个Result类包含code状态码、message提示信息、data返回数据三个字段。成功返回code200业务异常返回code500未登录返回code401。所有Controller方法统一返回Result对象前端axios响应拦截器里根据code做统一处理这样前端就不用每个接口都写一遍错误逻辑了。统一异常处理用RestControllerAdvice注解捕获全局的业务异常和未知异常避免把堆栈信息直接抛给前端。3.2 登录鉴权与Token处理手写JWT拦截器的完整方案登录接口的逻辑不复杂接收用户名和密码先用用户名查询用户再用同样的加密算法对比密码是否匹配匹配则生成Token返回。密码加密我这边的做法是MD5(md5(password) salt)即对原始密码做一次MD5再加上每个用户唯一的盐值再做一次MD5。盐值我直接存在用户表的一个字段里注册的时候用UUID生成。这个方案虽然不如BCrypt强度高但胜在实现简单、便于学习理解生产环境可平滑替换。JWT工具类是这个项目的核心类。我封装了三个方法generateToken生成Token、parseToken解析Token、isTokenValid校验Token是否有效。生成的Token里放入userId和roleCode两个claim过期时间设置为2小时。密钥写在配置文件中注意生产环境绝对不能硬编码在代码里。Token的解析过程就是把JWT字符串拆开验签SpringBoot拦截器里每个请求都会执行一次。拦截器类AuthInterceptor实现HandlerInterceptor接口重写preHandle方法。逻辑分三步第一步从请求头取Authorization字段没有则直接返回401第二步调用JWT工具类解析Token解析失败说明Token被篡改或过期返回401第三步把解析出的userId和roleCode放到request的attribute里方便后续Controller获取当前用户信息。这里有个关键点拦截器只负责校验Token是否合法具体接口的角色权限判断还要在Controller层处理。我封装了一个RequireRole注解配合自定义AOP切面在方法上标注角色编码切面里判断当前用户角色是否匹配不匹配直接拒绝访问。WebConfig配置类里注册拦截器并通过addPathPatterns(/api/**)指定拦截路径通过excludePathPatterns排除登录、注册、课程列表等公开接口。这一步经常被忽略很多新手把拦截器配好之后发现所有接口都被拦了其实就是路径配置问题。我习惯把所有接口统一放在/api前缀下这样拦截规则的配置非常清晰。3.3 MyBatis实战经验缓存机制、动态SQL与数字比较的坑MyBatis用多了之后会发现真正影响开发效率的不是CRUD本身而是对框架特性的理解程度。先说MyBatis的缓存机制。一级缓存是SqlSession级别的默认开启同一个SqlSession中执行两次相同的查询第二次会直接命中缓存。但要注意在SpringBoot整合MyBatis后每个Mapper方法都会开启一个新的SqlSession方法执行完就关闭所以一级缓存其实没有太大意义除非在同一个事务里执行两次查询。二级缓存是Mapper级别的跨SqlSession有效默认关闭需要手动在Mapper.xml里加cache标签开启。实际开发中我基本不开启二级缓存因为缓存失效策略不好控制一旦数据更新就要手动清缓存容易产生脏数据。这个理解在面试中经常被问到搞清楚一级缓存和二级缓存的区别以及各自的生命周期很重要。动态SQL是MyBatis最实用的功能我用得最多的是where、if、foreach三个标签。多条件查询课程列表的时候where标签能自动处理多余的AND关键字比如if testtitle ! null and title ! AND title LIKE CONCAT(%, #{title}, %)/if这种写法比在Java代码里拼SQL字符串安全得多。批量插入订单的时候用foreach标签collection属性指定参数名item指定遍历元素变量名separator设置每条语句之间的分隔符。批量插入的性能远优于循环调用单条插入原因在于减少了数据库连接和SQL解析的开销。这里必须分享一个MyBatis非常经典的坑单个数字字符的比较。在Mapper.xml里写if teststatus 1运行时会发现条件永远不生效。原因在于MyBatis的OGNL表达式解析中1会被当成字符类型而不是字符串Java中char和String的equals比较结果永远是false。正确写法有两种第一种是if teststatus 1.toString()强制把字符转成字符串再比较第二种是外层用双引号if teststatus 1内层用双引号包住字符串。这个问题极其隐蔽我第一次遇到的时候排查了两个小时最后翻源码才找到原因。顺带说一句碰到这种问题最好的排查办法是把MyBatis的日志级别开到DEBUG模式在控制台看最终生成的SQL语句一眼就能看出条件有没有拼接上。4. 前端核心实现Vueaxios搭建在线教育平台4.1 前端环境准备与项目初始化前端部分我用的是Vue 2 Element UI这套成熟组合虽然Vue 3已经出了很久但Vue 2的生态资料最全遇到问题基本都能搜到解决方案。Node版本建议用14.x或16.x LTS版本版本太高比如Node 18以上在npm install的时候经常遇到node-sass编译失败的问题可以换成sass或dart-sass解决。用vue-cli创建项目vue create edu-front选择Manually select features勾选Router和Vuex其余按默认即可。创建完项目后需要安装的依赖有axiosHTTP请求库、element-uiUI组件库、video.js和videojs-contrib-hls视频播放器支持m3u8格式。安装依赖的时候如果npm速度慢或者报错可以把registry切换到国内镜像源这个操作是常规操作能解决绝大多数安装超时的问题。项目目录结构我习惯这样组织src/api目录放接口请求方法、src/utils目录放request.js请求封装、src/router目录放路由配置、src/views目录放页面组件、src/components目录放通用组件。4.2 axios封装与Token注入前后端分离请求的核心机制前后端分离架构下前端和后端跑在不同的端口上浏览器存在跨域限制所以必须解决两个问题跨域请求和Token传递。跨域问题的解决方案我放在部署章节细讲这里先说Token的传递机制。在src/utils/request.js里创建一个axios实例设置baseURL为/api开发环境走代理生产环境走Nginx转发设置超时时间为10秒。然后添加请求拦截器从localStorage里取出Token如果存在就设置到请求头的Authorization字段。添加响应拦截器如果返回的code是401说明Token过期或未登录清掉本地存储的Token并跳转到登录页如果是其他错误码弹出错误提示信息。这样封装好之后业务代码里请求接口完全不用关注Token问题每个请求自动携带Token响应返回时自动处理公共错误代码非常干净整洁。登录流程在前端的实现是登录页面表单校验通过后调用登录接口拿到返回的Token和用户信息Token存localStorage用户信息存Vuex。然后路由跳转到首页。这里有一个细节不要在登录成功后立刻用router.push跳转先调一下获取用户信息的接口确保用户数据在页面渲染前就已经加载完成否则页面里用到用户昵称的地方会闪现默认值。4.3 路由配置与权限控制前端路由守卫的两种典型场景路由配置这块需要区分“无需登录的公开页面”和“必须登录的受保护页面”。首页、课程列表页、课程详情页部分内容可看属于公开页面个人中心、订单确认页、课程学习页必须登录后才能访问。Vue Router的路由守卫是实现权限控制的关键。在router/index.js里配置全局前置守卫router.beforeEach逻辑是这样每次路由跳转前先从localStorage里取Token如果目标路由的meta.requiresAuth为true且没有Token就重定向到登录页并在query里带上redirect参数记录用户原本想去的页面登录成功后自动跳回。这个方法我特意用在了“从课程详情页点击购买按钮后未登录”的场景用户被引导到登录页面登录成功后再自动返回购买页用户体验非常顺畅。路由参数的使用也值得一提。课程详情页的URL设计为/course/detail/:id在路由配置里用path: /course/detail/:id定义页面里通过this.$route.params.id获取课程ID。列表页跳详情页时用this.$router.push({ path: /course/detail/ id })。这里注意param参数和query参数的区别param是路径的一部分适合传主IDquery是URL后面的?keyvalue适合传搜索条件这类非必须参数。开发中我见过很多新手把两者混用导致刷新页面后参数丢失。4.4 m3u8视频播放课程播放页的核心技术实现在线教育平台的视频播放是最容易出问题的环节。视频源我采用的是m3u8格式这是HLSHTTP Live Streaming协议下的播放列表文件里面存的是一系列.ts视频分片的地址。相比MP4格式m3u8的优势在于支持拖动进度条响应更快分段加载、天然支持清晰度切换、对网络波动容忍度高是目前在线教育行业的主流格式。Vue项目里要播放m3u8最常用的方案是video.js配合videojs-contrib-hls插件。播放页的代码逻辑不复杂进入页面后先调用视频播放地址接口拿到播放地址之后用video.js初始化播放器。关键点在于播放地址接口的返回值处理。因为鉴权逻辑在后端所以播放地址是通过接口动态获取的而且这个地址可能带有临时的鉴权参数比如?tokenxxx有效时间5分钟。播放器拿到这个临时的完整URL后加载视频流。这里要提醒一下如果生产环境是HTTPS协议视频地址也必须是HTTPS开头的因为浏览器对混合内容HTTPS页面加载HTTP资源会直接拦截这个坑在项目上线时经常遇到表现为视频黑屏但控制台里看不到明显报错。videojs-contrib-hls插件在高版本video.js中可能不再被维护我实际开发中遇到过兼容性问题。备用方案是用hls.js这个库它专门处理HLS流的播放对m3u8的兼容性比videojs-contrib-hls更好。如果只考虑Chrome、Firefox这类现代浏览器hls.js是最稳妥的选择。另外视频播放页还需要记录学习进度我实现了一个简单的方案监听播放器的timeupdate事件每15秒向后端上报一次当前播放进度用户下次进入时根据已保存的进度自动定位。5. 前后端联调与生产环境部署5.1 开发环境联调跨域问题的两种解决方式开发阶段前后端分离前端跑在8080端口vue-cli默认后端跑在8080端口浏览器访问前端页面时前端代码里的axios请求指向/api这个/api会被代理转发到后端的8080端口。这个代理配置写在vue.config.js文件里module.exports { devServer: { port: 80, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这个方案的好处是前端代码里不需要写完整的后端地址请求路径都写成/api/xxx这种相对路径。开发环境由webpack-dev-server做代理转发生产环境由Nginx做反向代理转发前端代码完全不用改。另一种解决跨域问题的方式是在后端配置CORS跨域资源共享在SpringBoot中写一个WebMvcConfigurer配置类重写addCorsMappings方法允许所有来源、所有请求头、所有方法。这个方案适合前后端域名不同、不方便做代理的场景。两种方式用哪种取决于部署架构如果前后端最终部署在同一台服务器的同一个域名下后端通过Nginx的URL前缀区分就用代理方案如果前后端部署在不同域名下必须用CORS方案。5.2 生产环境部署从打包到Nginx配置的完整流程项目上线部署是整个流程的最后一步很多人开发环境跑得好好的一上服务器就各种问题核心原因是环境差异。我梳理一遍完整的部署流程照着做基本不会出大问题。第一步后端打包。在项目根目录执行mvn clean package -DskipTests生成的jar包位于target目录下。上传到服务器后执行启动命令nohup java -jar edu-server.jar --spring.profiles.activeprod edu.log 21 。这里用了nohup和让进程在后台运行并把日志输出到edu.log文件。注意生产环境的数据库连接、Redis地址、JWT密钥等配置要写在application-prod.yml里启动时通过--spring.profiles.activeprod指定。如果服务器配置了系统服务比如CentOS的systemd更推荐用systemd管理Java进程可以实现开机自启和异常自动重启。第二步前端构建。在项目目录执行npm run build生成的静态文件在dist目录下。把dist目录上传到服务器的某个目录比如/opt/edu-front。这里要检查vue.config.js里的publicPath配置如果不是部署在域名根路径而是子路径比如https://example.com/edu/需要设置publicPath: /edu/否则打包后静态资源路径会找不到表现为页面空白但控制台一堆404。第三步安装配置Nginx。Nginx是整个部署架构的核心它同时承担了静态资源托管、API反向代理、HTTPS配置三个职责。nginx.conf的关键配置如下server { listen 80; server_name edu.example.com; # 前端静态资源 location / { root /opt/edu-front; index index.html; try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 视频文件代理m3u8和ts分片 location /video/ { proxy_pass http://127.0.0.1:8080/video/; } }这里有一个至关重要的配置try_files $uri $uri/ /index.html;。因为Vue Router如果使用history模式刷新/course/detail/1这个页面时Nginx会去查找这个路径对应的文件查不到就会返回404。加上这行配置后所有未匹配到文件的请求都会重写到index.html由Vue Router内部处理路由。如果不加就会出现“生产环境刷新页面空白”或“刷新页面404”的经典问题。如果你用的Vue Router是hash模式URL里带#则不受这个影响但hash模式对SEO不友好一般还是推荐history模式。第四步初始化数据库。把项目里的edu.sql脚本导入MySQL。导入命令mysql -u root -p edu edu.sql。脚本里除了建表语句我还写了初始化管理员账号的INSERT语句。生产环境的MySQL要做几个基础优化字符集设置为utf8mb4支持emoji表情和特殊字符、时区设置为8:00、关闭MySQL对外网直接开放尽量只允许内网访问或SSH隧道。5.3 MySQL安装与基础配置经验因为热词里很多人搜索MySQL安装与配置这里补充一下我在多个环境下安装MySQL的经验。Windows开发环境推荐装MySQL Installer一键安装并配置Windows服务最省心的版本是5.7和8.0之间选8.0因为5.7官方已经结束维护。Linux服务器上推荐用docker方式docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 mysql:8.0一条命令搞定比yum安装干净得多也好卸载。MySQL Workbench是官方提供的图形化管理工具建表、执行SQL、导出数据都方便但生产环境的权限管理建议用命令行操作更加严谨。另外存储过程在日常开发中不常用MySQL的存储过程写法跟SQL Server和Oracle都不一样如果只是做业务开发了解概念即可不用深挖。这里必须强调一下MySQL安装完成后第一步要做的不是建库建表而是修改root密码并创建专用账号。我见过太多人把root密码设为123456就直接对公网开放这是极其危险的操作。正确做法是创建项目专用账号只授予业务库的增删改查权限root账号仅限本机登录。6. 常见问题与排查技巧实录6.1 新手最常踩的五个坑从版本冲突到打包异常先说SpringBoot版本太高的问题。很多新手用IDEA的Spring Initializr创建项目默认拉到3.x版本然后发现项目里引入了javax.servlet依赖编译报错代码里import javax.servlet.http.HttpServletRequest全部标红。这就是Spring Boot 3.x把javax包迁移到了jakarta包的原因。解决方案要么把JDK升级到17并改import为jakarta开头要么在创建项目时把Spring Boot版本手动选为2.7.x。我的建议是学习阶段用2.7.x等理解了Spring Boot的自动配置原理后再去看3.x的新特性也不迟。再说vue打包后布局异常的问题。开发环境跑得好好的npm run build之后页面全部变形、样式错乱。最常见的原因是publicPath配置错误。默认publicPath是/如果打包后的dist文件是放在子目录访问的资源加载路径就会出错。解决方案在vue.config.js里设置publicPath: ./相对路径或者根据实际部署路径设置。另一个原因是Element UI的按需引入配置出了问题build后样式没打进去检查babel.config.js里是否有按需引入的插件配置。MyBatis单个数字字符比较的问题前面已经详细说了这里不再重复。但我要再强调一遍排查方法把日志级别调到DEBUG查看MyBatis生成的SQL如果SQL里没有这个条件说明条件判断本身就为false问题出在OGNL表达式而不是SQL语句。视频播放不了的排查思路要按顺序来。第一步看浏览器控制台的网络请求video接口是否返回200如果返回200但视频黑屏第二步看视频地址能否直接访问如果直接访问能播放但页面里不行大概率是跨域或混合内容问题如果服务端日志报错找不到文件确认Nginx配置的/video/路径是否正确指向了视频文件目录。这里最关键的是配置正确的MIME类型Nginx默认不识别m3u8和ts需要在nginx.conf的http块里加配置types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; }不加这个配置浏览器会把m3u8文件当成application/octet-stream直接下载而不是播放。6.2 开发调试实用技巧让问题定位效率翻倍MyBatis配置打印SQL是我最常用的调试手段。在application.yml中配置logging.level.com.example.mapperdebug后控制台会打印每条SQL的完整语句和参数值以及查询结果的行数。这个功能在排查动态SQL拼接问题时至关重要你能直观看到if标签里的条件到底有没有生效。MyBatis Log Plugin是IDEA的付费插件评测期免费能自动把日志里的SQL占位符替换成真实参数值去掉一堆看不懂的日志格式复制出来直接在数据库客户端里执行调试效率非常高。不用付费插件的替代方案是使用IDEA自带的数据库工具直接把SQL复制到数据库执行。我个人习惯是配合使用IDE里看日志数据库工具里跑SQL验证结果。前端调试方面Vue官方推荐安装Vue DevTools浏览器插件能直观地查看组件的props、data、computed以及Vuex的状态变化。排查接口问题时打开浏览器开发者工具的Network面板关注请求头里的Authorization字段是否携带有Token。我在联调阶段见过最多的场景就是前端说“我请求了”后端说“我没收到”结果一看请求被跨域拦截了或者请求发到了错误的地址。养成看Network面板的习惯能省下大量无意义的沟通时间。6.3 问题排查速查表问题现象可能原因排查步骤解决方案刷新页面404Vue Router history模式Nginx未配置try_files访问任意子路由刷新看Nginx错误日志Nginx配置try_files $uri $uri/ /index.html后端接口返回401Token过期/未携带/被篡改查看请求头Authorization字段重新登录获取Token检查请求拦截器是否注入Token批量插入报语法错误foreach标签配置不正确或缺少批量SQL配置查看生成的SQL语句检查collection和separator配置JDBC连接加上allowMultiQueriestruem3u8无法播放MIME类型缺失/跨域/混合内容直接访问视频地址看是否播放Nginx配置m3u8和ts的MIME类型统一HTTPS协议前后端联调跨域开发环境代理配置错误查看浏览器控制台CORS报错检查vue.config.js的proxy配置MyBatis条件不生效OGNL表达式中数字字符比较错误开启DEBUG日志查看SQL使用1.toString()或双引号写法7. 扩展方向与我的实操体会这个系统虽然完整覆盖了在线教育平台的业务闭环但真要上生产还有很多可以扩展的地方。如果有余力我建议按这个优先级来扩展第一优先级是引入Redis缓存把课程列表、课程详情这些热点数据缓存起来并改造登录鉴权为Redis存储Token这样用户退出登录和强制下线功能就容易实现了第二优先级是引入消息队列比如ActiveMQ或RabbitMQ下单成功后发送延时消息30分钟未支付自动关单第三优先级是引入OSS对象存储和CDN视频文件不要直接放在应用服务器的磁盘上而是走云存储加CDN分发降低服务器带宽压力。这些点任何一个拿出来做深了都能让这个项目的技术含金量上升一个台阶。我在自己的实践中体会最深的是不要急于求成把最基础的业务链路先跑通再逐步增加复杂度。一开始只需要一个登录接口加一个课程列表接口能跑通了再去做订单、做视频、做后台。每增加一个功能模块都要想清楚它在这个业务闭环中扮演什么角色数据是怎么流转的。这样一步步搭出来的系统你才会对每一行代码、每一个配置了如指掌。面试的时候被问到这个项目你不仅能讲清楚架构还能讲清楚为什么要这样设计遇到过哪些坑、是怎么解决的这种真实感的项目经验远比简历上罗列一堆技术名词有说服力得多。最后再分享一个小技巧这个项目的前后端分离架构和管理后台的布局方案在很多实际公司项目中都是通用的。你把这个在线教育平台的业务代码摸透之后换一个业务场景比如电商、知识付费、预约系统把课程、订单、视频这些表换成商品、订单、库存技术骨架完全不用动业务代码调整逻辑就行。这就是项目经验最值钱的部分——不是记住了哪段代码而是掌握了解决问题的通用能力和技术判断力。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询