Spring Boot + Vue3 学生选课管理系统:三端权限、事务选课与排课冲突检测实战
Java 全栈项目实战 Spring Boot Vue3 Element Plus MySQL JWT 前后端分离 选课系统
摘要
本文介绍一套可完整本地运行的 B/S 学生选课 / 教务管理系统。后端基于 Spring Boot 2.7、Spring Data JPA、JWT;前端使用 Vue 3、Element Plus、Vite;数据库为 MySQL 8.0(库名 xuanke_db,13 张核心表)。
系统面向 管理员、教师、学生 三类用户,业务链覆盖:基础数据维护 → 课程发布 → 排课 → 选退课 → 课表 → 成绩录入发布 → Excel 导出。选课与排课均含 多维度时间冲突检测,选课流程在 数据库事务 下完成容量、时间窗、重复选课等校验。
运行环境:JDK 17、Maven 3.6+、Node.js 16+、MySQL 8.0+;前端 http://localhost:5173,后端 http://localhost:8080/api。

目录
- 一、项目概述
- 二、项目亮点
- 三、登录与权限
- 四、管理员端功能(详解)
- 五、教师端功能(详解)
- 六、学生端功能(详解)
- 七、核心业务规则说明
- 八、数据库设计概要
- 九、本地运行与演示
- 十、总结
一、项目概述
1.1 项目定位
本系统模拟高校教务中的选课环节,解决三类用户的典型需求:
| 管理员 | 维护学期与师生档案,发布课程、安排上课时间教室,监控选课与冲突 |
| 教师 | 按学期查看授课班级、名单,录入/发布成绩,导出 Excel |
| 学生 | 在选课时间内选课/退课,查看课表与已发布成绩,避免课表时间冲突 |
采用 前后端分离:Vue 单页应用 + RESTful API + MySQL 持久化;业务规则集中在后端 Service,前端负责展示与交互。
1.2 业务流程
【准备】管理员:学期 → 专业/班级 → 学生/教师账号
【发布】课程管理:新增课程(自动创建教学班:学期+教师+容量+状态)
【排课】教学班管理:配置时间地点(跨班冲突自动拦截)
【选课】学生:选课中心选/退课(容量+时间窗+课表冲突校验)
【教学】教师:名单 + 成绩草稿/发布 + Excel 导出
【查询】学生:课表、已发布成绩
1.3 技术栈
| 后端 | Spring Boot | 2.7.18 |
| ORM | Spring Data JPA | 实体 + Repository |
| 数据库 | MySQL | 8.0+,utf8mb4 |
| 认证 | JWT | Token 有效期 24 小时 |
| 导出 | Apache POI | .xlsx |
| 前端 | Vue 3 + Vite | 3.3 + 5.0 |
| UI | Element Plus | 2.4 |
| 路由/状态 | Vue Router + Pinia | 三端菜单与登录态 |
| HTTP | Axios | 开发代理 /api → 8080 |
1.4 系统架构
#mermaid-svg-p7sayACS0JtEUPhj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p7sayACS0JtEUPhj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p7sayACS0JtEUPhj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p7sayACS0JtEUPhj .error-icon{fill:#552222;}#mermaid-svg-p7sayACS0JtEUPhj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p7sayACS0JtEUPhj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p7sayACS0JtEUPhj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p7sayACS0JtEUPhj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p7sayACS0JtEUPhj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p7sayACS0JtEUPhj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p7sayACS0JtEUPhj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p7sayACS0JtEUPhj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p7sayACS0JtEUPhj .marker.cross{stroke:#333333;}#mermaid-svg-p7sayACS0JtEUPhj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p7sayACS0JtEUPhj p{margin:0;}#mermaid-svg-p7sayACS0JtEUPhj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-p7sayACS0JtEUPhj .cluster-label text{fill:#333;}#mermaid-svg-p7sayACS0JtEUPhj .cluster-label span{color:#333;}#mermaid-svg-p7sayACS0JtEUPhj .cluster-label span p{background-color:transparent;}#mermaid-svg-p7sayACS0JtEUPhj .label text,#mermaid-svg-p7sayACS0JtEUPhj span{fill:#333;color:#333;}#mermaid-svg-p7sayACS0JtEUPhj .node rect,#mermaid-svg-p7sayACS0JtEUPhj .node circle,#mermaid-svg-p7sayACS0JtEUPhj .node ellipse,#mermaid-svg-p7sayACS0JtEUPhj .node polygon,#mermaid-svg-p7sayACS0JtEUPhj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p7sayACS0JtEUPhj .rough-node .label text,#mermaid-svg-p7sayACS0JtEUPhj .node .label text,#mermaid-svg-p7sayACS0JtEUPhj .image-shape .label,#mermaid-svg-p7sayACS0JtEUPhj .icon-shape .label{text-anchor:middle;}#mermaid-svg-p7sayACS0JtEUPhj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-p7sayACS0JtEUPhj .rough-node .label,#mermaid-svg-p7sayACS0JtEUPhj .node .label,#mermaid-svg-p7sayACS0JtEUPhj .image-shape .label,#mermaid-svg-p7sayACS0JtEUPhj .icon-shape .label{text-align:center;}#mermaid-svg-p7sayACS0JtEUPhj .node.clickable{cursor:pointer;}#mermaid-svg-p7sayACS0JtEUPhj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-p7sayACS0JtEUPhj .arrowheadPath{fill:#333333;}#mermaid-svg-p7sayACS0JtEUPhj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p7sayACS0JtEUPhj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p7sayACS0JtEUPhj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p7sayACS0JtEUPhj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-p7sayACS0JtEUPhj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,



