1. 项目概述
这个基于SpringBoot+Vue的学生选课系统是一个典型的校园信息化应用,我去年为本地一所职业院校开发过类似项目。这类系统看似简单,但实际开发中会遇到不少技术难点,比如高并发选课时的系统稳定性、前后端数据交互的实时性、以及复杂的业务逻辑处理。
系统采用现在主流的前后端分离架构,后端用SpringBoot提供RESTful API,前端用Vue.js构建用户界面。数据库方面通常选择MySQL,对于选课这种读多写少的场景特别合适。下面我会结合自己踩过的坑,详细解析这个系统的技术实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型分析
2.1 为什么选择SpringBoot
SpringBoot的自动配置特性让后端开发变得极其高效。在选课系统中,我们需要快速集成以下组件:
- Spring Security用于权限控制
- MyBatis-Plus操作数据库
- Redis缓存选课结果
- RabbitMQ处理选课队列
我特别推荐用MyBatis-Plus而不是JPA,因为在处理复杂的选课关系查询时,手写SQL更灵活。记得在application.yml中做好分页配置:
yaml复制mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
2.2 Vue前端架构设计
前端采用Vue CLI搭建项目骨架,有几个关键配置需要注意:
- 使用vue-router的history模式时,记得配置Nginx转发规则
- Axios拦截器中要处理401跳转登录页
- 选课表格推荐用el-table配合自定义分页
javascript复制// axios全局配置
axios.interceptors.response.use(response => {
return response.data
}, error => {
if (error.response.status === 401) {
router.push('/login')
}
return Promise.reject(error)
})
3. 核心功能实现
3.1 选课业务逻辑
选课的核心难点在于解决并发冲突,我采用Redis分布式锁+数据库乐观锁的方案:
java复制public boolean selectCourse(Long courseId, Long studentId) {
String lockKey = "lock:course:" + courseId;
try {
// 获取分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
Course course = courseMapper.selectById(courseId);
if (course.getSelected() < course.getCapacity()) {
// 使用乐观锁更新
int updated = courseMapper.updateSelected(courseId,
course.getVersion());
if (updated > 0) {
// 记录选课关系
studentCourseMapper.insert(new StudentCourse(studentId, courseId));
return true;
}
}
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
3.2 数据库设计要点
选课系统主要涉及6张核心表:
| 表名 | 字段 | 说明 |
|---|---|---|
| student | id, name, grade, major | 学生信息 |
| teacher | id, name, title | 教师信息 |
| course | id, name, credit, capacity, selected | 课程信息 |
| classroom | id, building, room_no, capacity | 教室信息 |
| schedule | id, course_id, teacher_id, classroom_id, week, day, section | 排课信息 |
| student_course | id, student_id, course_id, score | 选课记录 |
特别注意:course表的selected字段需要加索引,但不要用唯一索引,否则会严重影响并发性能。
4. 典型问题解决方案
4.1 选课高峰期系统崩溃
我们上线第一天就遇到了这个问题,解决方案是:
- 前端增加排队机制,使用WebSocket通知排队进度
- 后端采用限流策略,Guava RateLimiter控制QPS
- 课程列表接口增加Redis缓存
java复制@GetMapping("/courses")
@Cacheable(value = "courses", key = "#root.methodName")
public List<CourseVO> getAvailableCourses() {
return courseService.listAvailableCourses();
}
4.2 数据一致性问题
当网络抖动导致选课结果不一致时,我们实现了补偿机制:
- 记录操作日志到MongoDB
- 定时任务检查选课记录与课程已选人数的差异
- 自动修复或通知管理员手动处理
5. 部署优化建议
5.1 后端部署
使用Docker Compose部署能极大简化环境配置:
dockerfile复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: ["java", "-jar", "/app.jar"]
depends_on:
- redis
- mysql
redis:
image: redis:6
ports:
- "6379:6379"
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
5.2 前端优化
通过以下配置显著提升加载速度:
- 开启Gzip压缩
- 使用CDN加载Vue等静态资源
- 配置合理的缓存策略
javascript复制// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('compression-webpack-plugin').use(
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: /\.(js|css)$/,
threshold: 10240
})
)
}
}
6. 扩展功能建议
在实际使用中,院校通常会提出更多需求:
- 课程评价系统
- 教师调课申请
- 自动冲突检测
- 微信小程序接入
- 选课数据可视化分析
实现冲突检测的SQL示例:
sql复制SELECT * FROM schedule
WHERE classroom_id = #{classroomId}
AND week = #{week}
AND day = #{day}
AND (
(start_section <= #{endSection} AND end_section >= #{startSection})
)
这个系统最关键的还是处理好选课时的并发问题,建议在压力测试阶段使用JMeter模拟至少500并发请求,确保系统稳定。我们项目最终实现了2000QPS的选课处理能力,核心就是Redis分布式锁和数据库乐观锁的合理运用。
