如果你今年正在为毕业设计选题发愁,尤其是计算机专业或者软件工程方向的,那我建议你认真看看这篇文章里提到的“Java + 微信小程序”这套体育选课系统方案。
体育选课系统这个题目,本质上是高校教务场景里最典型的“高并发查询 + 低频事务操作”业务模型,拿来练手再好不过。它既不像电商系统那样涉及复杂的支付和库存,也不像后台管理系统那样枯燥乏味,而是踩在了“小程序前端 + Java后端 + 数据库设计 + 权限管理”多个技术点的交叉口上。无论你是想快速完成毕设,还是想在简历上多一个拿得出手的完整项目,这套系统都算得上一个性价比极高的选择。
这篇文章就把我当时做这个题目时踩过的坑、想明白的原理、以及最后落地的完整设计思路全部写出来,给你一条可以照抄的路线。
1. 选题价值分析:为什么体育选课系统是毕设的“安全牌”
很多人选毕设题目很随意,到最后答辩才发现难度不够、创新点不足、工作量虚高。体育选课系统这个题目好就好在,它虽然看起来简单,但该有的功能模块一个不少,而且业务逻辑非常清晰,适合用来展示你的工程能力。
1.1 业务复杂度刚刚好,适合学生独立完成
体育选课系统的核心流程不复杂:学生登录小程序,查看可选的体育课程,提交选课申请,系统判断名额余量,成功后生成选课记录;老师可以维护课程信息,查看选课名单;管理员负责基础数据管理。
这套流程覆盖了典型的“用户端 + 管理端”双层架构,前端小程序负责学生操作,后端管理平台负责教师和管理员操作。业务逻辑简单但不单薄,不会出现毕设做到一半发现自己控制不了复杂度的情况。
拿一个具体的模块来举例。课程名额的并发扣减是这类系统的核心难点之一,也是面试官最爱问的点。一个学生同时打开了五个课程页面,正在犹豫选哪个,等他想好提交请求的那一瞬间,该课程的名额可能已经被其他同学抢光了。你需要在服务端做严格的名额校验和原子性扣减,这涉及的不仅仅是SQL,更是对数据库事务隔离级别的理解。
如果把这些内容全部掌握并写进论文,你的毕设工作量已经非常扎实了。
1.2 跨平台场景天然贴合“小程序 + 后端”主流架构
现在高校的教务类服务,几乎都在从App和网页端往微信小程序迁移。原因很直接:学生不用额外安装App,打开微信搜一下或者扫码就能用,获客成本趋近于零,后台的开发成本也比双端适配低不少。
从技术栈的角度来看,这种题目让你有机会完整地实践一套前后端分离的开发流程。后端用Java Spring Boot提供RESTful API,前端用微信小程序原生框架接收数据并渲染页面,二者通过HTTPS + JSON进行交互。这套模式,和业界的日常工作方式完全一致。
说到热搜词里频繁出现的uniapp微信小程序,我多说一句。如果你是毕设答辩时间比较紧,或者想省掉一部分重复劳动,可以考虑用uniapp来开发小程序端,它的Vue语法写起来确实比原生WXML更顺手。但如果你希望通过这个项目加深对微信小程序底层机制的理解,那我还是建议先用原生框架做一遍,踩过原生框架的坑再来抽象。我当时用的就是原生方式,答辩时老师问我“小程序的生命周期都有哪些”,我可以当场答出onLoad、onShow、onReady、onHide、onUnload各自触发的时机,这个细节让老师印象很深。
2. 系统整体设计:从需求分析到模块划分的完整链路
系统设计没有你想象中那么玄乎,说白了就是把一个笼统的需求,拆成一堆能落地的具体功能点。我当时先从需求分析文档开始写,把学生、教师、管理员三类角色的操作梳理清楚,再画用例图和数据流图,最后才动代码。
2.1 角色权限设计:三类用户的身份边界怎么划分
体育选课系统的用户角色必须分清楚,因为不同角色操作的资源差异很大,权限模型搞混了会带来严重的数据安全隐患。
- 学生:浏览课程、选课、退课、查看个人课表、维护个人资料。
- 教师:管理自己负责的课程信息、查看选课学生名单、设置课程名额和上课时间。
- 管理员:维护教师账号、维护课程类别、发布选课公告、处理异常选课记录。
我从一开始就做了“基于RBAC的简易权限模型”。后端拦截器根据请求头里携带的token去查当前用户的角色,然后判断该角色是否有权限访问对应的接口。比如删除一门课程,这个操作只允许管理员执行,教师只能修改自己创建的课程,学生就根本没有访问这个接口的资格。
权限这块我建议你不要用硬编码判断。虽然硬编码写法最简单,但如果后面要加一个“教务秘书”角色,你会发现所有接口的if判断都要改一遍,代码很脆。我当时用的是Spring Security + JWT的组合方案,配置几个过滤器链,把不同接口的访问规则写清楚,后期的可维护性会高很多。
2.2 模块划分与页面流转逻辑
整个系统我分成三个主要端:
小程序端作为学生的主战场,包含登录页、首页课程列表、课程详情页、选课确认弹窗、个人中心、我的课表这六个核心页面。
后端管理端单独做了一个PC网页,技术栈也用的Vue + Element UI,功能有课程管理、类别管理、学生管理、教师管理、选课统计。
后端Java服务则承担了所有业务的处理,对小程序和PC端提供统一的接口层,用Swagger生成API文档,前端联调的时候直接看在线文档就行,不需要反复问后端要接口说明。
这里有个设计细节值得说,就是课程状态。体育课程有“未开始选课”“选课进行中”“选课已结束”“课程已结课”这四种状态。选课接口必须校验当前时间是否处于该课程的选课时间段内,不是简单的看数据库里状态字段,而是动态比对时间。因为课程状态是可以由时间自动推进的,比如22点选课结束,21:59学生提交选课请求还可以成功,22:00整就会被拒绝。这个校验逻辑处理好了,导师会认为你对业务场景的理解很到位。
2.3 Uni-app与原生微信小程序的选型权衡
热词里频繁出现uniapp,这个必须提一下。uniapp的跨端特性非常香,一套代码可以编译到微信小程序、H5、App等多个平台,对于想要同时拥有小程序端和Web端的毕设来说,它甚至能帮你把教师管理后台也跑起来。
但uniapp也有它的坑。最典型的是在微信小程序环境里,部分浏览器端的API不存在,很多组件的行为和H5端不一样,你仍然需要去翻微信小程序的文档。我的结论是,如果后端接口设计得好,前端用什么框架影响不大,选自己最熟悉的就行。
我个人的方案是前端全部用原生微信小程序,后端管理端用Vue,这样前后端的技术栈区分很清楚。当时有的同学图省事,管理端也想用微信小程序写,结果调试管理端的复杂表格和弹窗时浪费了大量时间,得不偿失。
3. 数据库设计:四张核心表怎么定义才能支撑整个系统
数据库设计是毕设论文里的重头戏,也是答辩老师一眼就能看出水平的地方。体育选课系统的表结构不算复杂,但表的字段设计、索引设计和关系设计必须经得起推敲。
3.1 核心表结构逐张拆解
我设计的核心表一共四张,外加几张辅助表。第一张是用户表,字段包含用户ID、学号工号、姓名、密码、角色类型、院系、班级、手机号、头像URL、创建时间。密码不能存明文,我当时用BCrypt做了加密处理,这个在答辩时也是一个加分项。
第二张是课程表,字段包含课程ID、课程名称、课程类型(篮球/足球/羽毛球/游泳等)、任课教师ID、上课时间、上课地点、总名额、已选人数、学分、选课开始时间、选课结束时间、课程状态、课程简介。
第三张是选课记录表,字段包含选课ID、学生ID、课程ID、选课时间、退课时间、选课状态。状态字段我设计的取值有已选、已退、已结课,这样即使学生退过课,历史记录也不会完全消失,对后续的选课统计有重要作用。
第四张是公告表,字段包含公告ID、标题、内容、发布人ID、发布时间、置顶状态。
这里有一个很多新手容易犯的错误,就是把“已选人数”直接当成一个普通字段,在每次选课成功后就“读出来、加一、写回去”,这种操作方式在高并发选课场景下会出大问题。正确的做法是选课成功的标志是成功插入一条选课记录,而课程表的“已选人数”要么用数据库触发器维护,要么在事务里基于课程ID做行锁更新。
3.2 索引设计和ER关系怎么画才规范
索引设计的核心思路是“查询频率高的字段建索引”。课程表的课程类型和选课开始时间字段、选课记录表的学生ID和课程ID字段,这些是查询最高频的条件,需要建立组合索引。选课记录表的唯一索引也很关键,建在学生ID + 课程ID上,防止学生重复选修同一门课。
ER图在论文里是必备内容。我当时用Power Designer画的,包括学生、课程、教师、公告、选课记录这五个实体的属性以及它们之间的关系。比如学生和课程之间是多对多的选课关系,通过选课记录表来体现;教师和课程是一对多关系,一个教师可以带多门课程。如果你对画图这件事不太熟练,也可以用ProcessOn在线工具,导出的图片清晰度足够放进论文。
4. 小程序端核心功能实现:登录、课程列表、选课交互
小程序端的实现细节非常多,哪些功能用微信生态的能力、哪些功能需要走自己的后端,这里面的边界要清晰。我当时是从零开始,一行一行写的WXML和JS,虽然花费了不少时间,但收获很大。
4.1 微信登录与后端JWT鉴权的完整链路
微信小程序的登录逻辑是很多人的第一道坎。我当时也踩过坑,以为微信登录就是前端直接调用wx.login拿一个code就能完成登录,其实不是。
完整的链路是这样的:前端调用wx.login获取临时凭证code,再把code发给后端;后端拿着这个code,去调用微信接口服务获取用户的openid和session_key;如果获取成功,后端就用openid去用户表里查找该用户,找到说明该学生已经注册过了,没找到就额外创建一个用户;最终后端签发一个JWT令牌返回给前端,前端把它存储到wx.setStorageSync里。
后面每次前端请求后端接口时,都在请求头里带上这个JWT令牌,后端通过拦截器解析令牌,获取当前登录用户的身份信息。需要注意的是,JWT令牌有一个有效期,我当时配置的是7天,过期之后前端必须重新走一遍登录流程。这个方案既保证了安全性,也不会频繁打扰用户。
4.2 课程列表的分页加载与下拉刷新
课程列表页是学生打开系统后的主页面,数据量虽然不算特别大,但良好的体验感很重要。我这里做了两个优化:第一个是分页加载,每页固定10条数据,实现“上拉加载更多”的效果;第二个是下拉刷新,重新拉取第一页数据。
小程序端使用onReachBottom监听触底事件,使用enablePullDownRefresh开启下拉刷新配置。逻辑实现上,每次请求接口时传pageNum和pageSize两个参数,后端返回一个“分页对象”,包含当前页码的数据列表、总条数和总页数。前端拿到数据后追加到已有数组的尾部,通过wx.nextTick保证渲染完成后再停止加载动画。
在列表页还有一个筛选功能,学生可以根据课程类型(篮球、足球、羽毛球)和上课时间段来过滤课程。这个筛选功能在前端做筛选是需要一次性把当前页所有数据载入的,我当时没有偷这个懒,而是把筛选条件提交给后端,通过查询参数做条件过滤,这个方案在数据量大之后依然能保持稳定。
4.3 选课和退课交互中的状态把控
选课的交互流程比表面看起来要复杂。学生点击某门课程的“选课”按钮,前端不能直接就提交,而是要弹出一个确认对话框,展示课程名称、上课时间、学分和课程简介,提醒学生确认无误后再选。
确认之后前端发起选课请求,此时有几个常见的返回结果:选课成功、名额已满、选课时间未到、选课时间已结束、已经选过这门课、和其他课程上课时间冲突。这些状态码在后端接口里都做了统一封装,前端拿到返回结果后分别进行提示。
退课的流程也同样需要确认,而且退课成功之后,前端要立刻更新课程列表里剩余的名额数字。这里注意一个小细节:退课时不能只在前端把界面上的数字减一,而是要在退课接口返回成功后,再重新请求一下该课程的详情数据,让界面显示后端真实的数量。我当时在退课功能上线后测试时发现,如果退课后不刷新,界面显示名额是实时的,但再次请求后发现数量可能已经变了,因为同时有其他同学在选课——所以“以服务器为准,前端只负责展示”这条开发原则非常关键。
5. 后端Java核心逻辑:并发选课与接口设计
后端是整个系统的发动机,所有关键的规则都在这层做。很多同学的毕设代码里面,后端就是薄薄一层,所有逻辑都堆在Controller里,这种写法在答辩时非常容易被挑毛病。一个合格的Spring Boot工程,应该经过Controller层、Service层、Mapper层这样的清晰分层。
5.1 基于Spring Boot的分层架构实践
我当时的后端工程结构是这样划分的:
controller包:接收前端请求,进行参数校验。service包:承载核心业务逻辑,比如选课、退课、课程发布。mapper包:使用MyBatis-Plus操作数据库。entity包:数据库表对应的实体类。dto包:接收前端参数的传输对象。vo包:返回给前端的数据对象。
分层的好处是职责清晰,比如选课接口,从Controller进入后,CourseService.selectCourse方法内部要执行很多步骤:校验课程是否存在,校验选课时间是否开启,校验是否重复选课,校验名额是否已满,校验上课时间是否冲突,最后插入选课记录并更新名额。这一连串的逻辑如果全塞在Controller里面,整个方法会非常臃肿,也不方便写单元测试。
5.2 并发选课场景下的数据一致性保护
学生集中抢课的时候,多个请求同时到达后端,如果不对数据做并发保护,就一定会出现“超选”现象,这是整个系统最容易翻车的地方。我当时查阅了不少资料,最后整理了三种可行的方案。
第一种方案是数据库层面的悲观锁,使用SELECT ... FOR UPDATE锁住课程记录,强制串行化对同一门课程的操作。优点是实现简单,缺点是并发性能差,同一门课程每秒能处理的选课请求有限。
第二种方案是乐观锁,在课程表中添加版本号字段,更新名额时校验版本号是否被其他事务修改,如果版本不一致则重新读取数据再操作。这个方案性能好,但在人多的时候容易出现更新失败,需要做重试处理。
第三种方案是直接UPDATE扣减,利用UPDATE语句的原子性:“UPDATE course SET selected_count = selected_count + 1 WHERE id = #{courseId} AND selected_count < total_count”,并检查受影响行数。如果受影响行数为0,说明名额已经满了或者数据有误,再执行回滚。
我最终在正式代码里用的是“先INSERT选课记录,再UPDATE扣减名额,底层通过数据库唯一索引兜底重复选课”这一套组合方案,三个措施加在一起,既保证了用户操作的不重复性,又保证了数据不会超发。
5.3 接口统一返回体与异常处理机制
前后端分离的开发模式下,接口的返回格式如果五花八门,前端联调会非常痛苦。我当时规定所有接口都必须返回一个统一的JSON结构:状态码、提示信息和数据体。
比如选课成功返回:{ "code": 200, "message": "选课成功", "data": null };名额已满返回:{ "code": 500, "message": "该课程名额已满", "data": null }。
这个统一返回体配合Spring Boot的@RestControllerAdvice全局异常处理器,可以把业务异常、参数校验异常和系统异常分别做处理。我在业务代码里只需要throw new BizException("该课程名额已满"),异常处理器会自动捕获并封装成标准格式返回给前端,不用在每一个接口里都写try-catch,代码干净了不少。
6. 管理端功能:教师排课与管理员的日常维护
管理端虽然看起来没有学生端那么有视觉冲击力,但在整个系统里占据了一席之地。没有管理端,课程数据从哪里来?教师账号谁来维护?所以这部分功能要做扎实。
教师端最核心的功能是课程管理。教师登录后能看到自己名下的课程列表,可以新增课程,也可以编辑已有课程的信息。新增课程时需要注意一个业务规则,同一时间段该教师不能被安排两门不同的课程,这个校验我在后端Service层做了,防止前端漏传参数导致脏数据。
管理员端的功能则更多一些。管理员可以创建教师账号、重置教师密码、查看所有课程以及选课人数、生成选课统计报表、发布站点公告。这里面有一个小功能容易被忽略,就是“学生选课记录的导出”。我当时用了EasyExcel工具库,把选课列表导出成Excel文件,虽然写起来只是几行代码,但答辩演示的时候效果很好,老师会觉得你考虑到了实际使用中的真实需求。
7. 常见问题与排查技巧实录
毕业设计的过程本质上是踩坑和填坑的过程,我把自己当初最常遇到的几个问题整理出来,给正在做的你当参考。
问题一:开发环境中请求后端接口,小程序一直提示“request:fail”。这个问题绝大多数情况下是域名白名单和HTTPS证书的问题。开发调试时,在微信开发者工具里勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”就能解决。但要发布上线的话,必须配置备案域名并部署HTTPS证书。
问题二:用户登录后,过一段时间再操作一直提示“登录已过期”。原因是JWT令牌过期了。我当时的处理是,前端在请求拦截器里判断HTTP状态码,如果是401就跳转重新登录,同时自动调用wx.login重新获取新令牌,保持用户无感刷新。
问题三:选课并发测试时发现有名额超发。后来检查发现,是我在更新“已选人数”时先SELECT再UPDATE,中间存在时间窗口。改成前面提到的原子UPDATE后,问题才真正解决。这个坑也让我明白了为什么说“检查再更新”是最容易产生并发问题的写法。
问题四:真机预览时,图片加载不出来。原因是微信小程序在真机上不能访问本地IP的图片资源,必须使用HTTPS的图片地址。我当时把所有图片都传到了服务器上,换成了线上地址才解决。
我印象最深的一个排查过程,是在模拟大量用户同时选课时,系统频繁报“数据库连接池不够用”的错误。那是因为我默认的HikariCP连接池最大连接数设置太小,而每个选课请求因为事务还没提交,占用的连接一直被持有,导致后续请求只能排队等待。后来我把最大连接数调大,同时优化了事务的范围,让事务尽量早地提交,问题才彻底缓解。这类问题在毕设答辩时很有故事性,因为说明你真的做了并发测试,原因分析也能讲得头头是道。
8. 项目扩展与答辩加分技巧
如果你想让这个毕设的完成度更高一点,或者希望答辩时让老师眼前一亮,建议可以考虑从这几个角度做二次扩展。
8.1 技术维度的升级方向
缓存层面,可以把课程列表数据放进Redis,设置60秒过期时间,减少数据库的查询压力。选课成功后,异步把选课记录同步到数据库,同时把缓存中的已选人数做原子自增,进一步提升系统的并发处理能力。
消息推送层面,学生选课成功或者退课审核通过后,通过微信小程序的订阅消息功能给学生发送一条通知。这个功能需要在小程序后台申请模板消息权限,并引导用户授权订阅,恰好能够体现你对微信生态的了解程度。
数据可视化层面,管理端可以加入一个简单的图表页面,用ECharts展示不同体育课程的选课人数占比、各院系学生的选课偏好等统计信息。
8.2 论文和答辩的侧重点
写论文的时候,架构图、流程图和ER图这些肯定不能少,但真正的加分项在于你对核心问题的反思。我当时在论文里用了一整节来写“高并发选课场景下的数据一致性方案选型”,从业务背景、存在问题、方案对比例表、最终效果这四个方面做了完整分析。论文里这个部分让老师在答辩时花了很多时间提问,但我每一个细节都能对上,最后反而成了整个项目的亮点。
答辩展示的时候,建议准备两条演示路径。一条是正常路径,完整展示学生从登录到选课成功的业务流程;另一条是异常路径,比如选课名额已满会提示什么、选课时间未开始会提示什么。主动演示异常情况会让老师觉得你对边界情况有考量,而不是光做了个快乐路径就草草了事。
8.3 最后再分享一点个人建议
体育选课系统这个题目虽然不奢华,但它是一个真正的“麻雀虽小,五脏俱全”的项目。我在做完这个项目之后,对Spring Boot的后端分层、微信小程序的登录机制、数据库事务和并发控制都有了成体系的理解,后续找实习面试时,很多Java相关的题目我都能从这段经历里找到对应的案例来讲。
建议你整个项目周期尽量留出七八周的时间,自己独立编码、独立调试、独立写文档。代码可以借鉴别人的思路,但最后一定要完全吃透。你从图书馆、从博客、从开源项目里找到的任何参考代码,都一定要一条一条去理解它的逻辑,再根据自己的需求去改。只有真正亲手敲过的代码,在答辩时才能自信地讲出来,否则老师随便问一个变量为什么这么写,你就可能卡壳了。