
做计算机毕业设计最折磨人的往往不是敲代码本身而是选题之后的连锁反应技术栈能不能跑通、功能够不够演示、论文好不好写、答辩老师会不会专门挑底层原理来问。我前前后后帮人看过不少在线教育方向的毕设项目最后定下来最多的一套组合就是标题里这个后端用Spring Boot管理端用Vue用户端直接做成微信小程序。这套组合几乎成了计算机毕业设计源码项目里的标准答案不是因为跟风而是因为它恰好同时满足了毕业设计的几个硬指标技术栈有门槛、功能覆盖广、演示效果好、论文素材充足还能让答辩现场有东西可以展示。这篇文章我会把整个项目的设计思路、核心模块、三端联调的实操过程以及我实际踩过的坑和文档写作要点一次性讲清楚给准备做类似题目的同学一个可以直接照着落的参考。1. 毕设选型为什么最后锁定 Spring、Vue 和微信小程序三条线1.1 毕业设计项目的真实诉求公司项目要求的是稳定上线、可运维、能扛住流量但毕业设计的逻辑完全不一样。它更像一场技术汇报评委关心的是你用了什么技术、解决了什么问题、系统结构是否合理、论文和代码是否对应得上。在线教育这个选题恰好是六边形战士。它天然有三个端点的模型学生用户通过微信小程序学习课程后台管理人员通过网页管理课程和用户后端统一处理数据和业务逻辑。三个端各自独立又互相依赖演示的时候可以一边打开小程序、一边切到管理后台画面丰富评委不会觉得这不就是个增删改查。更关键的是这个题目的功能边界很清晰工作量可大可小。基础版做课程展示、选课、视频播放、后台课程管理进阶版加支付、评论、学习进度统计、轮播图、讲师管理改动都在可控范围内。我见过不少同学上来就选智能推荐系统或者大数据分析平台结果写了两周代码发现自己连基础业务都搭不完最后只能删功能。在线教育则不会出现这种情况它每一步都有成熟方案可以参考。1.2 三个核心技术各自承担什么角色后端选Spring Boot本质上是因为它把SSM时代的各种繁琐配置全自动掉了。Spring Boot自带内嵌Tomcat打包成Jar直接跑不用再单独装服务器这对学生来说非常友好。配合MyBatis Plus连单表的增删改查SQL都不用手写BaseMapper里直接给你封装好了能把精力集中到业务逻辑上。有人担心用MyBatis Plus会不会显得技术含量低我建议你换个角度想毕业设计考察的是你能不能用工程化的方式完成一个完整系统而不是让你手写所有底层。你在答辩时能说清楚MyBatis Plus帮你省了什么、你自己写了哪些复杂SQL这本身就是亮点。Vue负责管理后台选它的理由也很现实组件化开发让页面复用变得简单像课程表单、用户列表这类页面抽出公共组件后只要改配置就能生成新页面。而且Vue生态里的Element UI组件库颜值够用表格、弹窗、表单验证都是现成的做成毕设界面完全不会显得敷衍。微信小程序作为用户端是这套方案里最加分的环节。小程序不需要安装App扫码即用演示的时候直接用手机就能跑比打开电脑浏览器访问网页更有产品感。而且小程序的登录体系、手机号获取、视频播放能力都是官方组件支持的没有太大的实现风险。对毕设来说能稳定演示比技术多酷炫重要得多。1.3 项目功能全景图整个在线教育系统我按用户端和管理端两块来规划具体功能如下表端模块功能说明小程序端用户认证微信授权登录、手机号绑定、退出登录小程序端课程中心课程分类筛选、课程列表、课程搜索小程序端课程详情课程介绍、讲师信息、章节列表、视频播放小程序端选课学习一键选课、我的课程、学习进度记录小程序端互动模块课程评论、评论点赞小程序端个人中心头像昵称、我的选课、关于系统管理端登录验证管理员账号密码登录、JWT鉴权管理端课程管理课程增删改查、上下架、课程分类管理管理端章节管理章节增删改查、视频上传与地址绑定管理端用户管理用户列表、用户状态禁用/启用、学习记录查询管理端评论管理评论列表、违规评论删除管理端数据统计选课人数统计、课程热度排行这样分完功能论文的需求分析部分基本就有骨架了。每一个功能点对应一张界面截图和一段实现描述论文根本不怕没素材写。2. 系统架构与数据库设计2.1 前后端分离的三端架构整个项目的物理结构是三个独立的工程springboot-backend后端API服务、vue-admin管理后台、wechat-miniapp微信小程序。它们之间只通过HTTP接口通信不存在页面互相嵌入的情况。我画不出图但可以用一段话把这个架构描述给你小程序端和管理端都是无状态的客户端它们把请求发给Spring Boot提供的RESTful API。后端统一处理完后返回JSON数据。登录后后端会签发一个JWT令牌客户端保存这个令牌之后每个请求都带上它后端通过拦截器校验令牌才能访问业务接口。我会在这个架构里额外引入Redis用来存验证码和部分热点数据。比如首页轮播图、课程分类这种不太变化的数据第一次从数据库查出来之后放进Redis设置一小时过期。这样做有两个好处一是显著减少数据库压力二是论文里可以浓墨重彩写一段基于Redis的缓存设计属于花很少代码就能加分的做法。这里要提醒一下千万注意JWT和缓存不要混为一谈JWT是无状态的Redis缓存只是加速数据读取别在答辩时把这两个概念说串了。2.2 核心数据表设计数据库设计直接决定后面开发顺不顺利。我在设计时一共建了这些表每张表的核心字段和用途如下表名核心字段说明t_userid, openid, nickname, avatar, phone, create_time小程序用户表openid是微信用户唯一标识t_adminid, username, password, role管理员表密码用MD5加盐存储t_categoryid, name, sort_order课程分类表t_courseid, category_id, title, cover, intro, price, status, teacher_id, create_time课程主表status控制上下架t_teacherid, name, avatar, intro讲师表和课程一对多t_chapterid, course_id, title, video_url, duration, sort_order课程章节表一个课程包含多个章节t_user_courseid, user_id, course_id, create_time选课关系表用户和课程多对多t_course_commentid, course_id, user_id, content, like_count, create_time课程评论表t_study_progressid, user_id, chapter_id, progress, update_time学习进度表记录用户看到某一章节的哪个位置有一点需要重点解释选课关系表t_user_course和学生观看章节的进度为什么要拆成两张表而不是直接合并。因为用户是否选过这门课和用户看到第几章是两个不同维度的数据选课关系是静态的进度是动态的。如果合在一张表里用户每看一个章节都得更新选课记录会造成大量冗余更新。分开设计后一门课只有一个选课记录但一个用户在一门课里可以有多条进度记录不同章节各自记录进度数据关系非常清晰。这个设计思路写到论文的数据库设计章节里是可以拿得出手的分析点。2.3 登录态与权限模型设计权限这块我采用JWT方案。流程是用户在小程序端调用wx.login拿到临时code后端拿code向微信接口换openid之后用openid生成JWT令牌返回给小程序。小程序把令牌存到storage里后续每个请求都在header里带上。后端用拦截器统一拦截需要登录的接口校验令牌是否有效、是否过期。管理员端则简单一些就是账号密码登录。密码不要明文存我会用MD5加盐后再入库登录时把用户输入的密码加同样的盐再哈希比对。虽然Spring Security本身可以做更复杂的权限控制但在这个项目里只分管理员和普通用户两种角色用拦截器按路径区分就够了。引入Security反而会带来一堆配置问题属于自己给自己增加答辩风险。实际开发时我还会用到一个技巧自定义一个LoginUser注解配合HandlerMethodArgumentResolver让Controller方法里直接注入当前登录用户对象。这样业务代码里不用到处去解析token代码干净很多。这个细节在论文的系统实现部分写出来就能明显提升技术档次。3. 核心实操三端联调开发实录3.1 后端Spring Boot的快速搭建后端工程我直接用Spring Initializr初始化Java版本用8Spring Boot版本选2.7.x不推荐一上来就追最新版因为有些第三方兼容跟不上毕设环境求稳是第一位的。核心依赖加这几个spring-boot-starter-web、mybatis-plus-boot-starter、mysql-connector-java、lombok、jjwtJWT工具、spring-boot-starter-data-redis。工程结构建议按功能分包而不是按技术分包。我见过有人把controller全放一个包、service全放一个包结果项目大了之后找个接口要翻半天。我会这样分层com.example.edu ├── config # 配置类CORS、拦截器、Redis配置 ├── controller # 接口层CourseController、UserController等 ├── service # 业务层接口实现 ├── mapper # MyBatis Plus的Mapper接口 ├── entity # 数据库实体类 ├── dto # 数据传输对象接收前端参数 ├── vo # 视图对象返回给前端的数据 ├── common # 统一返回体、异常处理、常量 ├── utils # 工具类JWT工具、Redis工具 └── interceptor # 登录拦截器统一返回体是所有接口的基础。我定义一个Result类包含code、message、data三个字段。所有Controller返回的都是Result对象成功就code200失败就返回对应错误码。这样小程序端和管理端处理起来非常统一不会出现一会儿返回对象、一会儿返回数组的混乱情况。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message success; result.data data; return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.code code; result.message message; return result; } }这里有个小经验全局异常处理一定要做。Spring Boot里可以写一个RestControllerAdvice的类把所有异常统一拦截转换成Result格式返回。否则一旦代码里出现未捕获的异常前端拿到的就是一堆烦人的报错堆栈而且HTTP状态码还会变成500。统一处理后所有异常都返回结构化的JSON前端也好提示系统繁忙之类的话术。课程列表接口是后端的核心接口之一它需要支持按分类筛选、按关键字搜索还要返回课程的封面、价格、讲师姓名。这段代码我建议手写一个多表联查的SQL不要用MyBatis Plus自带的单表方法硬凑public interface CourseMapper extends BaseMapperCourse { // 自定义联表查询课程表关联分类表和讲师表按条件过滤 ListCourseVO selectCourseList(Param(categoryId) Long categoryId, Param(keyword) String keyword); }对应的XML里写SQL。这样做的原因很简单课程列表页需要展示分类名称和讲师姓名但Course实体里只有category_id和teacher_id直接用单表查询还得二次回表查分类和讲师既多写代码又浪费查询次数。一条联表SQL一次性查出来效率更高代码也更好维护。3.2 Vue管理后台的搭建与常用写法管理端我用的Vue 2 Element UI为什么不用Vue 3这里不是不想用新版而是Element UI对Vue 2的生态最成熟网上资料最多对毕设来说不容易卡壳。如果你本来就会Vue 3那也可以但别在项目中途频繁换版本。环境准备这块经常有人卡住我直接给一套能跑通的流程先安装Node.js16.x版本验证命令node -v和npm -v然后全局安装Vue CLI工具npm install -g vue/cli创建项目用vue create vue-admin选择Manually select features勾选Router和Vuex版本选2.x。创建完成后安装Element UInpm i element-ui -S npm i axios -S npm i js-cookie -S管理端的核心是左侧菜单加右侧内容区的布局。菜单根据路由生成我用的是前端静态配置路由的方式。虽然动态路由根据角色权限生成菜单听起来更高级但毕设里管理员角色只有一种动态路由的意义不大反而会在刷新页面时遇到菜单丢失的问题处理起来麻烦。为了稳定我选择静态路由配菜单把基于角色的权限控制放到接口层面去做也就是后端拦截器鉴别管理员身份。axios请求要统一封装。我习惯建一个request.js里面创建axios实例配置baseURL为后端地址请求拦截器自动加token响应拦截器统一处理返回码。如果code401说明登录过期直接跳回登录页。这个封装一次搞定后面所有页面都复用省掉大量重复代码。管理员的登录页要处理登录状态持久化的问题。用户登录成功后后端返回一个token我用js-cookie把它存到cookie里同时存一份到Vuex。刷新页面时在路由的beforeEach钩子里检查Vuex里有没有用户信息如果没有就尝试从cookie里恢复这样刷新不会导致登录状态丢失。不要只放内存里刷新就没了那就是体验事故。课程管理页面是整个后台最核心的页面。用el-table展示课程列表支持分页用el-dialog弹出新增/编辑表单表单里上传封面图、填写课程标题、选择分类、填写价格。上传图片用el-upload组件action指向后端的上传接口上传成功后会返回图片URL再把URL绑定到表单的cover字段。这样实现起来快逻辑也直观。3.3 微信小程序端核心逻辑实现小程序端我用的原生框架没有用uni-app。原因很简单项目只需要跑微信一个平台用uni-app多一层编译反而增加排查问题的难度。原生小程序的API调用和官方文档能一一对应上对于毕设来说最保险。小程序骨架包含这些页面首页课程列表、分类、轮播图、课程详情页、我的选课页、个人中心页。底部用tabBar设置三个Tab首页、我的课程、个人中心这样导航结构清晰符合用户习惯。登录流程要去官方渠道说清楚。现在微信小程序已经不支持直接用wx.getUserInfo获取头像昵称了必须用官方提供的头像昵称填写能力让用户主动填写或者用open-typechooseAvatar的方式引导用户上传头像。最简单的方案是调用wx.login拿到code后端用code换openid并自动注册用户返回JWT。头像和昵称只作为可选项用户想填就填不填也能正常看课。这样登录流程最简易不会在授权弹窗上卡住。获取手机号是另一个容易踩坑的点。按钮设置open-typegetPhoneNumber用户点击后小程序会返回一个加密的phoneCode后端调用微信接口用phoneCode换取手机号。这个功能要求小程序是企业主体且通过认证个人主体的小程序没有这个权限。毕设如果遇到这个问题我的建议是做一个手动绑定手机号的输入框页面把手机号表单提交到后端既不会报错功能也完整。课程视频播放我直接用小程序内置的video组件src传后端返回的视频地址。这里重点提醒毕设课程里的视频可以放一些公开的MP4样片链接或者自己录屏上传千万不要在系统里嵌入视频网站的外链播放器那样容易出兼容问题。如果你用的是m3u8格式的视频流地址video组件同样支持播放但要注意在真机上需要配置后台播放和其他权限开发调试阶段优先用MP4文件最省心。小程序请求后端接口用wx.request这个函数有个坑请求的url必须是合法域名否则真机上会直接报错不在以下合法域名列表中。开发模式下可以在开发者工具的本地设置里勾选不校验合法域名就能用本地IP了。但是如果用真机预览这个开关没用必须绑定HTTPS域名。毕设阶段没有域名的同学我会建议用内网穿透工具把本地后端临时映射到一个HTTPS域名这样也就够演示用了。3.4 部署联调的关键步骤整个项目的启动顺序是先启动MySQL和Redis服务再启动Spring Boot后端然后是Vue管理端通过npm run serve跑起来最后用微信开发者工具打开小程序工程。后端的application.yml里要注意数据库连接串、Redis地址、文件上传路径这几项配置我在本机和服务器之间切换时就是改这一个文件。一个特别容易耽误时间的点是CORS跨域配置。Vue管理端的地址是localhost:8080后端是localhost:8081端口不一样就存在跨域。我在后端写了CORS配置类允许指定的前端来源访问Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这里要单独说明一下allowedOriginPatterns(*)为什么比allowedOrigins(*)好后者在allowCredentials(true)时会被浏览器拦截掉前者则能正常工作这是Spring 5.3之后才支持的配置写法。很多人跨域配了半天不生效基本上都是卡在这一点上。文件上传也要提前规划。后端加一个UploadController接收前端传的文件保存到本地的upload目录然后返回一个可以被访问的URL。为了能让前端访问到上传的图片和视频需要配置WebMvcConfigurer把upload目录映射为静态资源路径Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadPath /); }这样小程序端和管理端就能用http://本机IP:8081/upload/xxx.jpg直接看到文件了。注意不要用localhost真机调试时小程序端访问不了手机的localhost一定要用局域网IP或HTTPS域名。4. 高频踩坑与排查技巧实录4.1 小程序request请求直接失败这是出现频率最高的问题表现是开发者工具里请求报fail url not in domain list或者真机上网络请求直接挂掉。排查思路按顺序来确认后端服务确实在跑浏览器能访问后端接口确认小程序开发者工具勾选了不校验合法域名确认填写的是局域网IP或HTTPS域名而不是localhost确认后端CORS没有被拦截。其中最后一点最隐蔽小程序端的请求本身不受CORS限制但如果你在开发过程中的请求经过了一些方向代理工具就会遇到CORS报错这种时候先关掉工具再试。4.2 登录状态token过期导致反复跳登录页我在做管理端时踩到过一次token明明还有效但刷新页面后axios拦截器里还没拿到token结果被误判为未登录一直跳登录页。解决方法是统一管理token存储确保cookie、Vuex、axios拦截器三处读取的是同一份数据我最后选择以cookie为准每次刷新都先读cookie再初始化Vuex。如果你的项目里用的是本地存储localStorage思路也一样关键是找准唯一数据源。4.3 视频不能播放视频无法播放要区分两种情况。一种是mp4文件本身就编码有问题H5和小程序的video对编码格式要求比较严格一些剪辑软件导出的视频编码可能不兼容用格式工厂把视频转成H.264编码的MP4就能解决。另一种是地址问题播放地址不能写localhost需要写成IP或域名。还有一点容易忽略video组件需要设置show-center-play-btn这个属性才能在播放失败时看到具体报错信息否则只能看到一个黑屏完全无从排查。4.4 富文本内容里的图片显示不出来课程详情如果支持富文本编辑保存到数据库里的HTML片段里会带img标签src指向的可能是后台配置的相对路径比如/upload/xxx.jpg。前端直接渲染这个HTML片段时图片相对路径解析不到就会显示成裂图。解决办法是在渲染前用字符串替换把src里的相对路径拼上后端完整地址。这个逻辑虽然简单但很多人会忽略导致管理端编辑课程详情时看着正常小程序端一显示就全是破图。写一个递归替换函数把img src/upload/替换成img srchttp://服务器IP:8081/upload/一劳永逸。4.5 管理端PDF和图片预览问题后台有时需要预览课程附件或课件类似vue image能显示pdf吗这种问题我之前也被问过。Element UI的el-image组件只能显示图片不能显示PDF。如果只是简单的PDF查看我推荐用新窗口打开把PDF地址赋给一个a标签设置target_blank浏览器会自己打开预览。如果不想离开系统用iframe内嵌预览也是可以的但要注意不同浏览器对PDF内嵌的支持不同Chrome是没问题的。别自己去找各种PDF解析库毕设阶段越简单越可靠。4.6 微信小程序顶部导航栏的适配小程序的navigationBar高度在不同机型上不一样但是别用机型适配这种说法吓唬自己正常写法就是使用系统默认导航栏开发工具里统一设置为自定义导航栏反而会带来额外的适配工作。自定义导航栏需要适配刘海屏、状态栏高度还得自己写返回按钮工作量大而且容易出bug。毕设就用默认导航栏所有页面统一标题展示干净又不容易出错。5. 毕业设计文档LW的写作要点5.1 论文主体结构怎么排很多同学代码写完了但文档一个字没动最后只能熬夜狂编。其实毕设文档的结构是非常固定的不要自由发挥按这个顺序写最保险章节写作重点绪论背景与意义、国内外研究现状、主要工作内容相关技术Spring Boot、Vue、微信小程序的技术简介需求分析可行性分析、功能需求、用例图、非功能需求系统设计总体架构、功能模块设计、数据库设计E-R图和表结构系统实现各模块的核心代码片段、界面截图、实现说明系统测试测试环境、功能测试用例、测试结果总结与展望完成的工作、不足、改进方向相关技术这一章被很多人当成论文里充字数的水货章节写一段技术介绍就完事。我建议换个写法不要泛泛介绍Spring Boot而是结合你项目里的具体应用点去写。比如Spring Boot的自动配置机制如何简化了项目的初始搭建MyBatis Plus在课程分页查询中的应用这样看起来技术是有落到项目里的而不是直接抄官网简介。5.2 怎么把代码自然转成论文内容系统实现这一章最常见的错误是堆代码。整页整页贴Controller代码评委看不到重点。正确做法是每个功能模块挑出1到2段最能体现你做了设计思考的代码先截图或代码块展示然后写三四段话解释这段代码解决了什么问题、为什么这么写、流程图和界面截图放在哪里。比如课程列表页的分页查询先贴一段自定义的SQL联表查询Mapper代码然后解释为什么不用MyBatis Plus的自带分页因为要关联查询分类表和讲师表所以手写了分页SQL每页返回10条前台通过PageHelper或MyBatis Plus的IPage接收分页参数和总数前端接收后渲染表格和分页组件。整个过程就是代码原因效果的三段式写法。每一章都这样写重复几遍文档就能写得很扎实。5.3 答辩高频问题与应对思路答辩是整个毕设的临门一脚代码写得好不好反而次要关键是把设计者思维表达出来。我把见过的高频问题整理成一份对照表提问方向高频问题应对思路架构层面为什么选择前后端分离从开发解耦、独立部署、分工协作角度回答数据库表结构为什么这么设计以选课关系表和进度表分离为例讲清楚权限用户登录状态如何保持讲JWT的生成、校验、过期机制缓存Redis在项目里做什么讲首页数据缓存和验证码存储原理层面是否了解Spring的核心机制可以提Spring IOC容器和Bean生命周期业务遇到的最大难点是什么讲小程序视频播放兼容性问题你如何排查这里单独说一下Spring三级缓存原理这类底层问题。如果你简历或论文里写了熟悉Spring答辩老师有可能会追问。不需要把三级缓存详细推导一遍但至少要知道Spring解决循环依赖问题时使用一级缓存存成品Bean、二级缓存存早期暴露的原始Bean、三级缓存存的是对象工厂ObjectFactory。你可以这样回答这个项目里我主要通过构造器注入或Lazy注解避免循环依赖理解三级缓存机制是为了更深入掌握Spring的Bean生命周期。答到这个程度就够了。小程序相关的提问也容易有比如小程序冷启动和热启动的区别、怎么获取微信用户手机号。前者答冷启动是用户首次打开小程序需要加载代码包和初始化热启动是用户从后台切换回小程序直接恢复之前的页面状态。后者按实际操作流程描述一遍强调个人主体小程序不支持官方手机号获取所以做了手动绑定这个回答显得诚实又有应变力。5.4 文档写作的实用小技巧最后分享几个我写LW文档时总结的技巧都是能直接用的经验。第一所有截图要统一尺寸界面截图最好是完整窗口截图不要截一半表头和数据要清晰。第二数据库表结构用Word的表格功能展示每个字段写清楚类型、长度、注释不要用Excel截图会模糊。第三需求分析的用例图可以直接用PowerDesigner或ProcessOn画导出为高清图片插入Word不要手画。第四论文里涉及到的URL、IP地址、类名、表名这些全文保持一致别出现一会儿写course表一会儿写t_course表的情况这类低级错误会让评委觉得你不严谨。6. 我做完这套项目之后想说的几句话这套SpringVue微信小程序的在线教育系统我帮人从头到尾完整做过不止一遍每次做完都会有新的体会。最深的感受是毕业设计其实不是在考验你写多少行代码而是检验你有没有把一个模糊的问题拆成清晰方案的能力。选题、架构、建表、接口设计、前后端联调、文档整理、答辩陈述每一环都在锻炼这种能力。在代码之外你会碰到一堆意想不到的麻烦本地跑得好好的接口一上真机就不通上传个图片路径半天调不对同一套表结构在不同电脑上建出了不同的效果。这些麻烦才是真正值钱的部分因为以后你到了工作岗位上处理的就是这一类真实的工程问题。如果你正准备做类似题目我的建议很简单别贪功能先把基础闭环跑通让小程序能选课、能看视频后台能管课程、能看用户然后有余力再加评论、统计这些锦上添花的东西。文档从开发第一天就开始写每天写一点不要最后一个月才动笔。答辩PPT里放一张系统架构图、几张核心页面截图、一套测试用例表讲的时候冷静清楚基本就不会出问题。按照这套路线走完你会发现论文有了、代码有了、经验也有了。我始终觉得毕业设计给你留下的不应该只是一个分数而是你第一次独立把一个完整系统从0到1做出来的那种底气。这份底气会在你以后遇到任何项目挑战时悄悄帮你撑住。