
简介一份完整的微信小程序学生选课系统毕业设计源码包基于 Java、小程序与 MySQL 构建面向计算机专业毕业生和课程设计学习者。系统围绕学生、教师、管理员三类用户设计包含首页、个人中心、学生管理、教师管理、课程类型管理、课程信息管理、选课信息管理、取消选课管理等全套功能模块业务覆盖从课程发起到选课取消的完整流程。压缩包内共 1245 个文件以 Java 后端源码、小程序前端页面、Vue 管理界面、数据库 SQL 脚本及图片文档等类型为主压缩后约 24.79 MB项目采用 Maven 管理依赖部署环境为 JDK1.8、MySQL5.7 和 Tomcat7便于本地搭建运行。资源附带完整工程源码和数据库文件目录模块划分清晰能帮助学习者理解 MVC 分层、Servlet 处理流程、前后端数据交互与数据库表设计等关键知识点。目前已有 63 人学习浏览适合作为毕业设计起步模板和课程设计参考项目可按自身需求在此基础上扩展二次开发。1. 先从这份学生选课系统源码说起Java 后端、小程序端和 MySQL 怎么协作每年到这个季节都会有人拿一份「微信小程序学生选课系统源码java小程序mysqlLW.zip」来问我。解压之后通常是这样几样东西一个 Java 后端工程、一个微信小程序前端目录、一个 .sql 数据库脚本加上 LW 目录里的说明文档。它要解决的问题很朴素学生用微信小程序浏览课程、选课、退课、查看自己的课表管理员或教师通过后端维护课程和选课结果。适合正在忙毕设、课设或者想把「小程序 Java MySQL」整条链路完整跑通一遍的人。多数人拿到这份源码的第一反应是去 Java 工程里找 main 方法。但让新手真正翻车的往往不是 Java 代码而是三个端各自为政小程序不知道后端地址、后端连不上 MySQL、数据库表结构跟接口对不上。这篇按我自己的落地顺序拆开讲先从整体架构和核心业务流入手再一层层落到建表 SQL、接口联调和排查记录。2. 先画跑通的路线学生选课系统整体架构、目录与核心业务流拿到源码别急着双击项目。先把「谁调用谁、数据存在哪、请求怎么走」这条路线画出来后面所有操作都是在往这条线上填内容。这个系统的数据流可以压缩成一句话微信小程序通过 HTTP 请求访问 Java 后端接口Java 后端通过 JDBC 操作 MySQL再把结果返回给小程序渲染。2.1 为什么偏偏是 Java 微信小程序 MySQL选型逻辑与替代方案这套组合不是作者随手挑的而是毕设最稳妥的“黄金三角”。我一般选型时会先看三件事一是资料够不够厚二是答辩时能不能讲清楚三是本地电脑能不能跑起来。这三个条件一框答案基本就定了。层这个标题为什么选它常见替代方案替代的代价后端Java十有八九是 Spring Boot 或 SSM 结构入职面试也会问讲起来有分量Python Flask/Django、PHP别人想复现时环境差异大课程大纲也不容易对上小程序端微信小程序原生微信开发者工具免费手机上有微信就能演示uni-app、H5多一层框架打包和原生组件适配要额外折腾数据库MySQL免费、教程多、和 Java 配合最成熟SQLite、SQL ServerSQLite 不适合演示并发SQL Server 装起来重如果你是第一次接触这类源码判断后端是不是 Spring Boot 很简单看工程根目录有没有pom.xml。有就是 Maven 管理的 Spring Boot 或 SSM 工程没有可能是直接import到 IDE 的普通 Java Web 工程要自己补依赖。这套选课系统里Java 后端负责提供接口、做业务校验、写事务MySQL 负责把学生、课程、选课记录存住小程序端只做展示和请求不碰数据库。2.2 解压源码包之后三层目录各放什么一个典型的毕设源码包解压后目录结构大致长这样student-select-system/ ├── backend/ # Java 后端工程 │ ├── pom.xml # Maven 依赖导入 IDEA/Eclipse 用 │ ├── src/main/java # Java 源码controller/service/mapper │ └── src/main/resources # application.yml、mapper xml、初始化脚本 ├── miniprogram/ # 微信小程序端 │ ├── pages/ # 页面目录course、my、login 等 │ ├── app.js # 小程序入口逻辑 │ ├── app.json # 页面注册与全局配置 │ └── project.config.json # 微信开发者工具导入入口 ├── sql/ │ └── student_course.sql # 建库建表脚本导入 MySQL 用 └── LW/ # 设计文档/论文材料 └── 说明文档.pdf / word这段目录结构里有几个入口要点。project.config.json是微信开发者工具识别项目的钥匙打开工具时选“导入项目”指向miniprogram目录优先认这个文件pom.xml是后端工程的身份证IDEA 里Open选到backend根目录等 Maven 把依赖拉下来sql/student_course.sql是整套系统的地基必须先建库再导数据否则后端一启动就报Unknown database。很多源码包的 LW 目录里不只是论文还会写清楚数据库账号密码、后端端口、小程序 AppID 从哪申请。我拿到包第一件事就是翻这个目录比在代码里猜省事得多。2.3 把选课主流程完整串一遍选课是整套系统的核心链路完整走一遍大概是这个顺序学生打开小程序进入登录页点击授权小程序调用wx.login拿到临时 code。小程序把 code 发给 Java 后端后端拿 code 跟微信服务器换 openid生成登录态并返回给学生端。学生进入课程列表页小程序请求后端“查询可选课程”接口后端从 MySQL 课程表按学期、状态查询返回。学生点某一门课的“选课”按钮小程序把courseId和学生标识传到后端。Java 后端先校验课程是否存在、当前选课人数是否小于容量再校验学生有没有重复选、选课时间是否冲突。校验通过后端开事务往选课记录表插一条记录同时把课程表的selected_count加一。小程序收到成功后刷新列表课程卡片上的“已选人数”跟着变化。退课流程正好反过来小程序发起退课后端删除或标记选课记录同时把课程的selected_count减一。管理端则多出几个维护动作添加课程、修改课程容量、查看每条选课记录。这套动作里最容易出问题的不是逻辑复杂度而是第 5 步和第 6 步在多人同时选课时怎么保证不超卖、不重复这就是后面要重点讲的数据库事务和索引问题。3. 把数据地基立住选课系统 MySQL 表设计、建表 SQL 与并发控制数据库是这类系统的命门。很多源码在代码里写了密密麻麻的业务逻辑但表结构漏了唯一约束导致重复选课、超员选课这些低级问题在演示现场翻车。这一章直接从建表讲起表结构对了后端代码能少写一半判断。3.1 四张核心表的字段设计学生、课程、教师、选课记录不看具体源码包的命名差异学生选课系统的核心关系就落在四张表上表名关键字段作用studentid、student_no、name、password、openid、major、class存学生基本信息openid 用来对接微信登录courseid、course_no、course_name、teacher_id、credit、max_student、selected_count、week_day、section、location存课程基本信息、容量、已选人数、上课时间地点teacherid、teacher_no、name、department存教师信息和课程是一对多关系student_courseid、student_id、course_id、select_time、status选课关系表也叫中间表连接学生和课程课程表里的teacher_id关联教师表这是典型的一对多一个老师可以开多门课。student_course是典型的关联表解决“一门课被多个学生选、一个学生选多门课”的多对多问题。额外提一下status字段很多毕设只允许退课删除记录我更建议用状态标记0表示已选1表示已退这样管理端还能统计历史选课行为。学生表里的openid不是必填的但它决定了微信登录能不能落地。如果小程序端只靠一个固定账号密码登录那叫模拟登录把openid存下来才能识别“当前这个微信使用者是谁”。做毕设时可以先允许手动输入学号登录openid留空但表结构里有这个字段答辩时才好说明设计意图。3.2 建表 SQL 直接抄InnoDB、utf8mb4 与联合唯一索引下面是一份可以直接抄的建库建表 SQL覆盖学生、课程、教师、选课记录四张核心表-- 建库字符集选 utf8mb4中文和表情符号都能存 CREATE DATABASE IF NOT EXISTS student_course_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_course_db; -- 学生表 CREATE TABLE student ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, password VARCHAR(100) NOT NULL COMMENT 登录密码, openid VARCHAR(64) DEFAULT NULL COMMENT 微信openid, major VARCHAR(50) DEFAULT NULL COMMENT 专业, class_name VARCHAR(50) DEFAULT NULL COMMENT 班级, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表; -- 教师表 CREATE TABLE teacher ( id BIGINT AUTO_INCREMENT PRIMARY KEY, teacher_no VARCHAR(20) NOT NULL UNIQUE COMMENT 工号, name VARCHAR(50) NOT NULL COMMENT 姓名, department VARCHAR(50) DEFAULT NULL COMMENT 院系 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教师表; -- 课程表 CREATE TABLE course ( id BIGINT AUTO_INCREMENT PRIMARY KEY, course_no VARCHAR(20) NOT NULL UNIQUE COMMENT 课程编号, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, teacher_id BIGINT NOT NULL COMMENT 授课教师ID, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT 学分, max_student INT NOT NULL DEFAULT 50 COMMENT 容量上限, selected_count INT NOT NULL DEFAULT 0 COMMENT 当前已选人数, week_day TINYINT NOT NULL COMMENT 星期几上课 1-7, section VARCHAR(20) NOT NULL COMMENT 节次如 3-4节, location VARCHAR(100) DEFAULT NULL COMMENT 上课地点, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_teacher (teacher_id), KEY idx_course_no (course_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; -- 选课记录表核心中间表 CREATE TABLE student_course ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_id BIGINT NOT NULL COMMENT 学生ID, course_id BIGINT NOT NULL COMMENT 课程ID, select_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 选课时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0已选 1已退, UNIQUE KEY uk_student_course (student_id, course_id, status), KEY idx_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选课记录表;这份 SQL 里我特别重视三处细节。第一所有表都用 InnoDB 引擎只有它支持事务和行级锁后面防超卖靠的就是这两个能力。第二字符集统一 utf8mb4不要用老项目的 utf8因为微信昵称里可能带表情utf8 存表情会报Incorrect string value。第三选课记录表的联合唯一索引uk_student_course它在数据库层面挡住了“同一个人同一门课选两次”比在 Java 代码里if判断可靠得多。顺便说一句物理外键很多教材会让你在student_course上建FOREIGN KEY关联 student 和 course。我的习惯是不建物理外键只保留逻辑关联和普通索引。原因是毕设里经常出现“想删一门课结果被外键约束挡住报Cannot delete or update a parent row”实际项目里也普遍用应用层约束代替物理外键。3.3 选课人数校验为什么不安全原子更新与事务这是整套源码里最值得在答辩时展开讲的技术点。先说一个错误写法后端先查SELECT selected_count FROM course WHERE id ?在 Java 里判断selected_count max_student再执行插入和 update。这个写法单独测试没问题一旦有人同时选最后一门课的最后一个名额两个请求都查到了selected_count 49然后都执行 update最终结果可能是 51 人选进入 50 人容量的课。这就是并发下的“超卖”也是这类源码最容易翻车的地方。正确的做法是把人数校验放进数据库的原子更新里让 MySQL 的锁帮我们挡一圈-- 核心条件里带 selected_count max_student UPDATE course SET selected_count selected_count 1 WHERE id ? AND selected_count max_student;这条 SQL 的妙处在于行锁和WHERE条件共同保证了并发安全。同一时刻只有一个事务能更新这行课程数据后到的更新要么锁等待要么发现selected_count已经等于max_student影响行数为 0。后端拿到影响行数 0 就知道“选满了”直接给小程序返回“课程已满员选课失败”。配合这条 SQLJava 后端里的选课方法要套一层事务。Spring Boot 里最直接的写法是加Transactional注解Transactional public boolean selectCourse(Long studentId, Long courseId) { // 1. 先原子更新课程人数只有容量未满才更新成功 int rows courseMapper.increaseSelectedCount(courseId); if (rows 0) { throw new BizException(课程已满员或不存在); } // 2. 插入选课记录唯一索引会拦住重复选课 try { studentCourseMapper.insert(studentId, courseId, 0); } catch (DuplicateKeyException e) { // 走到这里说明联合唯一索引生效重复选课要回滚人数 throw new BizException(不能重复选课); } return true; }这段代码里有个细节容易被忽略先 update 课程人数再 insert 选课记录顺序是有讲究的。如果先 insert 选课记录、再 update 人数一旦 update 影响行数为 0 抛异常事务回滚会把刚插的选课记录一起回滚逻辑上也没错但先 update 可以把“满员”判断放在更靠前的位置减少空转。courseMapper.increaseSelectedCount(courseId)对应的 SQL 就是前面那条原子 UPDATE。Transactional保证 update 和 insert 要么都成功、要么都失败。最后一句里 catch 的DuplicateKeyException专门应对那种“两个人同时开事务唯一索引帮我们兜底”的情况。这两道防线叠在一起才能在演示高并发选课时不翻车。4. 把 Java 后端和小程序端接起来接口设计、wx.request 封装与联调参数数据库立住之后下一步就是把 Java 后端跑起来、让小程序能请求到数据。很多源码跑不通死在这三个地方后端接口路径写死、小程序请求地址配错、MySQL 连接参数不对。这一章把接口、请求封装和配置文件一次讲透。4.1 后端接口清单与选课接口实现一个能支撑演示的选课系统后端接口最少要有下面这些接口请求方式参数说明/api/course/listGETpage、pageSize、keyword分页查询可选课程带已选人数和容量/api/course/detailGETcourseId课程详情含教师、时间地点/api/course/selectPOSTcourseId学生选课返回成功或满员失败/api/course/cancelPOSTcourseId学生退课/api/student/coursesGET无查询“我的课表”/api/loginPOSTcode用 wx.login 的 code 换 openid返回登录态接口设计遵循一个原则小程序端只传业务数据不传 SQL、不传表名。比如选课前端只需要传一个courseId学生身份从登录态里取不靠前端传 userId这是很多毕设里会出现的接口安全隐患。看一下 Spring Boot 里选课接口的典型实现RestController RequestMapping(/api/course) public class CourseController { Autowired private CourseService courseService; PostMapping(/select) public Result selectCourse(RequestBody SelectRequest request) { // request.courseId 来自小程序studentId 从登录态 token 里解析 Long studentId StudentContext.getCurrentStudentId(); try { courseService.selectCourse(studentId, request.getCourseId()); return Result.ok(选课成功); } catch (BizException e) { return Result.error(e.getMessage()); } } }这段代码暴露了两个关键设计用RestController让接口直接返回 JSON省掉每处都拼ResponseBody的重复StudentContext.getCurrentStudentId()是关键它从请求头携带的 token 里解析当前学生避免了“传一个 userId 就能帮别人选课”的越权漏洞。RequestBody接收小程序 POST 上来的 JSON 体字段名要和前端传的courseId对应上。选课方法本身第 3 章已经给了事务写法Controller 只做参数接收和结果包装。这里有个参数约定要提醒实体里的max_student、selected_count、create_time这种下划线命名在 Spring Boot 里要开启map-underscore-to-camel-case否则查询结果里courseName永远为 null这个问题我放到 4.3 配置里说。4.2 小程序端封装 wx.request统一 baseURL、Token 与加载态小程序端如果每个页面都直接写wx.request后端的 baseURL 改一处就要改十个文件。常见做法是先封装一个request.js统一处理请求地址、超时时间和错误提示。// utils/request.js const BASE_URL http://127.0.0.1:8080; // 开发阶段指向本机后端 function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, timeout: 10000, header: { content-type: application/json, token: wx.getStorageSync(token) || }, success(res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络请求失败, icon: none }); reject(err); } }); }); } module.exports { request, BASE_URL };这里最关键的两个参数是BASE_URL和header.token。开发阶段后端跑在本机127.0.0.1:8080就能访问到了真机预览127.0.0.1指的是手机自己要改成电脑的局域网 IP比如http://192.168.1.101:8080或者直接上一个已备案的 HTTPS 域名。header里带 token是登录态标准做法。后端从 token 解析学生身份而不是每次都让前端传学号。另外我在success回调里统一判断了res.data.code 200这是约定后端接口返回结构统一为{ code, message, data }页面里只需要关心业务数据错误提示由封装层统一弹出。页面里调用封装的请求选课按钮的防重复点击也要在这里处理// pages/course/course.js Page({ data: { courseList: [], loading: false }, onLoad() { this.loadCourses(1); }, loadCourses(page) { this.setData({ loading: true }); request(/api/course/list?page page pageSize10) .then(list { this.setData({ courseList: page 1 ? list : this.data.courseList.concat(list) }); }) .finally(() this.setData({ loading: false })); }, onSelectCourse(e) { const courseId e.currentTarget.dataset.id; if (this.data.selecting) return; // 防止连点 this.setData({ selecting: true }); request(/api/course/select, POST, { courseId }) .then(() { wx.showToast({ title: 选课成功, icon: success }); this.loadCourses(1); }) .finally(() this.setData({ selecting: false })); } });这里顺手处理了两个小程序特色问题。第一是页面列表加载更多loadCourses(page)里用concat把下一页数据追加到现有列表配合onReachBottom触发加载下一页就能实现常见的“上拉加载更多”这也是课程多的时候演示流畅度的关键。第二是selecting标志位把选课按钮在请求期间锁住避免用户连点两次触发两个请求这个和 5.2 里讲的并发问题直接相关。4.3 让三端对上话端口、连接串、MyBatis 配置后端工程能不能连上 MySQL几乎都写在application.yml里。最常见的启动失败不是代码问题而是配置不对。下面是选课系统后端典型的配置直接给注释版server: port: 8080 # 后端端口小程序 BASE_URL 要对应 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/student_course_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true逐项说参数。server.port8080决定后端监听的端口小程序BASE_URL里的端口必须跟它一致改一处就要同步改另一处。url里的useSSLfalse是必加的不加会看到一个长长的 SSL 警告日志严重时直接报连接错误这个坑我放在 5.3 细说。serverTimezoneAsia/Shanghai解决CST和 UTC 时区错乱导致的日期差 8 小时问题查询选课时间对不上就是它的锅。driver-class-name这里要特别注意如果你本地装的是 MySQL 8.0驱动类名要写com.mysql.cj.jdbc.Driver连接数据库的用户名密码要和本机 MySQL 一致。mapper-locations告诉 MyBatis 去哪找 XML 映射文件如果你的源码是纯注解 SQL 不用 XML这行可以不配。最后一行map-underscore-to-camel-case: true千万不能省否则数据库里的course_name映射不到 Java 实体的courseName字段接口返回的数据全是 null。这三层配置对上了开发工具里的流程就是先启动 MySQL再启动 Spring Boot最后微信开发者工具里打开小程序。顺序反了才会出现“后端明明起来了小程序还是请求失败”的灵异现象。5. 微信小程序选课系统联调避坑5 个高发问题排查记录三端联调是最耗时间的阶段也是经验和问题最集中的地方。下面五条是从实际跑这类毕设源码时高频出现的坑里挑出来的每条按“现象、原因、解决”写。5.1 小程序一请求就报错不在以下合法域名列表中现象点击选课按钮后小程序wx.request的fail回调被触发控制台报错信息带一句“不在以下 request 合法域名列表中”。原因微信开发者工具默认校验小程序的 request 域名要求所有请求地址必须是配置过合法域名的 HTTPS 地址。毕设开发阶段后端跑在127.0.0.1:8080既不是 HTTPS也不是合法域名校验自然不通过。解决开发阶段绕开校验在微信开发者工具右上角点“详情”进“本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。注意这个勾选只对当前项目当前工具生效真机调试时仍然建议把后端挂到一台有公网 HTTPS 域名的服务器上如果没有也可以临时用局域网 IP 配合真机调试的“不校验合法域名”选项。还是不行就检查后端进程有没有真的起来netstat -ano | grep 8080看一眼端口是否在监听。5.2 选课瞬间多个请求丢在并发缝隙里现象演示时手速过快连点同一门课的“选课”按钮后端只收到一次请求或者课程列表在上拉加载时页面出现重复数据、加载卡顿。原因微信小程序的wx.request并发请求数上限是 10 个超出部分会直接进入fail回调。连点选课会触发多个相同请求同时还有列表分页请求抢占连接前面的请求被丢弃。解决前端加防重复提交标志请求期间把按钮置灰或加loading这就是 4.2 里selecting标志的作用同时后端在接口里做幂等处理用唯一索引兜底重复请求到了数据库也会被拦住。列表加载更多要控制并发每次只允许一个分页请求在跑loading状态下不触发下一次加载。5.3 MySQL 连接报 SSL 错误和驱动版本不匹配现象后端启动时控制台刷出大段红色日志关键词是SSL、Public Key Retrieval is not allowed或者ClassNotFoundException: com.mysql.jdbc.Driver。原因新版 MySQL 8.x 默认开启了 SSL 校验老式 JDBC 连接串没加useSSLfalse就会告警甚至失败另一类常见原因是驱动版本和数据库版本不匹配比如用老旧的mysql-connector-java 5.1.x去连 MySQL 8.x类名和方法都对不上。解决连接串统一加三个参数useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai避免使用 5.x 驱动改成com.mysql.cj.jdbc.DriverMaven 依赖也同步换成对应驱动坐标。改完配置一定重启后端这种错误只在连接池初始化时暴露一次运行中再改不生效。5.4 同一门课被同一个人选了两次现象数据库student_course里出现两条相同student_id和course_id的记录我的课表里出现重复课程。原因代码层面只做了查询再插入没建联合唯一索引。两个请求同时进来两次查询都没发现已存在记录于是各插一条。如果建表时没执行第 3.2 节的uk_student_course这个坑一定会踩。解决执行ALTER TABLE student_course ADD UNIQUE KEY uk_student_course (student_id, course_id, status);把唯一索引补上然后清掉已有重复数据再测试。Java 端也要处理插入时的DuplicateKeyException转成“不能重复选课”的友好提示而不是让 500 错误直接打到小程序端。5.5 接口通了但系统不认识你是谁现象请求接口返回 200但后端拿到不到当前学生身份或者换一个微信号登录看到的还是上一个学生的课表。原因登录链路没打通。小程序端只调了wx.login拿到临时 code没有往后端换 openid后端也没有把登录态持久化每次请求都不知道“你是谁”。解决把登录流程补完整小程序把 code 发到后端/api/login后端调微信接口换 openid查 student 表把 studentId 和登录态存到服务端返回一个 token 给小程序小程序把 token 存到wx.setStorageSync后续每个请求的 header 都带token后端从 token 解析学生身份。这样既解决了身份识别也顺带挡住了“传一个 userId 就能帮别人选课”越权操作。6. 验收自测清单让这套选课源码在答辩现场稳稳跑起来代码调通之后最后一步是验收。很多毕设源码在仓库里能跑换个电脑就崩十有八九是没有把环境顺序和数据状态固定下来。我自用的验收流程是这样一张清单你也可以照着走一遍。验收项操作路径预期结果常见失败MySQL 初始化用 root 账号导入 student_course.sql四张表出现在库中无报错建库时用错字符集或没选库后端启动IDEA 里运行 Spring Boot 主类控制台出现 Tomcat started无异常堆栈端口被占用、连接串密码错小程序登录开发者工具打开 miniprogram点登录能拿到 token学生信息显示BASE_URL 配错、登录接口没通学生选课课程列表点选一门容量未满的课提示选课成功已选人数 1重复点击、后端事务没加满员拦截把课程 max_student 设为 1再选第二个学生被提示课程已满没有原子更新人数超卖退课还原进入我的课表点退课选课记录状态变更人数 -1退课不退人数数据失衡演示前我强烈建议做一次冷启动演练按“MySQL → 后端 → 微信开发者工具”的顺序重启全部服务然后跑一遍“登录 → 选课 → 满员 → 退课”完整流程。有一次我在答辩前夜发现换了电脑后 MySQL 的 root 密码没改后端连接串还指向旧密码就是靠这张清单定位到的。给答辩加分的还有两个细节一是故意把max_student设成 1开两个学生的账号先后选同一门课现场展示“满员拦截”配合解释第 3 章的原子更新二是准备好一条重置数据 SQL演示完把选课人数清回初始状态防止下一位老师演示时看到脏数据。我有随手在 LW 文档里记录修改点和启动口令的习惯每次答辩前直接照着做这个习惯省过不少事希望你也能用上希望帮到你。本文还有配套的精品资源点击获取