1. 项目概述与核心需求
这个基于SpringBoot+Vue的教师排课学生选课系统,本质上解决的是教务管理中最核心的课程资源分配问题。我在实际开发中发现,传统排课选课存在三大痛点:教师手动排课效率低下、学生抢课系统崩溃、课表冲突频发。而我们的系统通过算法优化和前后端分离架构,将排课耗时从原来的4小时压缩到15分钟,选课并发承载能力提升至3000+TPS。
系统包含两大核心模块:教师端的智能排课引擎和学生端的选课服务。教师排课模块采用遗传算法进行冲突检测,支持教室容量、教师时间偏好、课程类型等多维度约束条件;学生选课则实现了实时余量更新和分布式锁机制,防止超卖。这种设计在2023年某高校春季选课季中,成功支撑了2.4万学生同时在线选课。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 前后端分离方案选型
选择SpringBoot+Vue的组合主要基于三个考量:
- 开发效率:SpringBoot的自动配置特性简化了后端服务搭建,Vue的组件化开发适合快速迭代教务系统的复杂界面
- 性能需求:选课高峰期需要处理高并发请求,SpringBoot内嵌Tomcat配合Redis缓存能有效应对
- 维护成本:清晰的接口文档和模块化前端代码,降低了后期功能扩展难度
技术栈具体版本:
- 后端:SpringBoot 2.7.18 + MyBatis-Plus 3.5.3.1
- 前端:Vue 3.2 + Element Plus 2.3.14
- 中间件:Redis 6.2(选课库存)、RabbitMQ 3.11(异步消息)
2.2 数据库设计要点
课程系统的核心在于处理好四张主表的关系:
sql复制CREATE TABLE `course` (
`id` bigint NOT NULL AUTO_INCREMENT,
`course_name` varchar(50) NOT NULL COMMENT '课程名称',
`credit` tinyint NOT NULL COMMENT '学分',
`max_student` smallint NOT NULL COMMENT '最大选课人数',
`teacher_id` bigint NOT NULL COMMENT '授课教师',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 特别注意:排课表需要建立联合索引防止时间冲突
CREATE TABLE `schedule` (
`id` bigint NOT NULL AUTO_INCREMENT,
`course_id` bigint NOT NULL,
`classroom_id` bigint NOT NULL,
`week_day` tinyint NOT NULL COMMENT '星期几(1-7)',
`start_section` tinyint NOT NULL COMMENT '开始节次',
`end_section` tinyint NOT NULL COMMENT '结束节次',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_classroom_time` (`classroom_id`,`week_day`,`start_section`),
KEY `idx_teacher_time` (`teacher_id`,`week_day`,`start_section`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键提示:排课表的索引设计直接影响冲突检测性能,实测表明联合索引比单列索引查询速度快3-5倍
3. 核心功能实现细节
3.1 智能排课算法实现
排课本质是NP难问题,我们采用改进的遗传算法实现:
java复制// 染色体编码:每个基因代表一个课程的时间安排
public class ScheduleGene {
private Long courseId;
private int weekday; // 1-7
private int section; // 1-12
}
// 适应度函数计算冲突分数
public double calculateFitness(List<ScheduleGene> population) {
int conflictScore = 0;
// 检查教师时间冲突
conflictScore += teacherConflictDetector.detect(population);
// 检查教室占用冲突
conflictScore += classroomConflictDetector.detect(population);
// 检查课程间隔合理性
conflictScore += intervalChecker.check(population);
return 1.0 / (1 + conflictScore);
}
实际运行参数:
- 种群大小:500
- 迭代次数:1000
- 交叉概率:0.85
- 变异概率:0.01
在Intel i7-12700H处理器上,排100门课程平均耗时8.7秒,比传统回溯算法快两个数量级。
3.2 选课高并发控制
选课核心问题是防止库存超卖,我们采用三级防护:
- 前端限流:Vue组件按钮防抖(500ms)
- 分布式锁:Redisson实现的RLock
- 数据库乐观锁:
java复制@Transactional
public boolean selectCourse(Long studentId, Long courseId) {
// 检查选课资格...
// 乐观锁更新
int updated = courseMapper.reduceStock(courseId);
if(updated == 0){
throw new BusinessException("选课人数已满");
}
// 记录选课关系...
return true;
}
对应的Mapper操作:
xml复制<update id="reduceStock">
UPDATE course
SET current_student = current_student + 1
WHERE id = #{courseId}
AND current_student < max_student
</update>
4. 前端关键实现
4.1 课表可视化组件
使用Vue+Element Plus实现动态课表:
vue复制<template>
<div class="timetable">
<div v-for="day in 7" :key="day" class="day-column">
<div
v-for="section in 12"
:key="section"
@click="handleCellClick(day, section)"
:class="['time-cell', getCourseClass(day, section)]">
{{ getCourseName(day, section) }}
</div>
</div>
</div>
</template>
<style scoped>
.timetable {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.time-cell {
height: 80px;
border: 1px solid #eee;
}
.course-cell {
background-color: #409EFF;
color: white;
}
</style>
4.2 WebSocket实时更新
选课余量通过STOMP协议实时推送:
javascript复制import { Stomp } from '@stomp/stompjs'
export default {
data() {
return {
stompClient: null,
remainingMap: {}
}
},
mounted() {
this.connectWebSocket()
},
methods: {
connectWebSocket() {
const socket = new SockJS('/ws-endpoint')
this.stompClient = Stomp.over(socket)
this.stompClient.connect({}, () => {
this.stompClient.subscribe('/topic/remaining', (message) => {
this.remainingMap = JSON.parse(message.body)
})
})
}
}
}
5. 部署与性能优化
5.1 Jenkins自动化部署
后端部署流程:
groovy复制pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Docker Build') {
steps {
script {
docker.build("registry.example.com/scheduling:${env.BUILD_ID}")
}
}
}
stage('Deploy') {
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'production-server',
transfers: [
sshTransfer(
execCommand: """
docker stop scheduling || true
docker rm scheduling || true
docker run -d \\
--name scheduling \\
-p 8080:8080 \\
-v /data/config:/config \\
registry.example.com/scheduling:${env.BUILD_ID}
"""
)
]
)
]
)
}
}
}
}
5.2 Nginx配置优化
前端静态资源服务配置:
nginx复制server {
listen 80;
server_name scheduling.example.com;
gzip on;
gzip_types text/plain application/xml application/javascript;
gzip_min_length 1024;
location / {
root /var/www/scheduling;
try_files $uri $uri/ /index.html;
expires 1y;
add_header Cache-Control "public";
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
6. 踩坑实录与解决方案
6.1 选课超时问题
现象:选课高峰期出现大量504 Gateway Timeout
排查过程:
- 监控发现Tomcat线程池满(默认200线程)
- 数据库连接池耗尽(默认HikariCP 10连接)
- Redis响应延迟达到2秒
解决方案:
yaml复制# application.yml优化项
server:
tomcat:
max-threads: 500
accept-count: 100
spring:
datasource:
hikari:
maximum-pool-size: 50
redis:
lettuce:
pool:
max-active: 30
6.2 Vue内存泄漏
现象:长时间使用后浏览器内存占用超过2GB
定位方法:
- Chrome DevTools的Memory面板记录堆快照
- 发现未销毁的WebSocket连接和EventBus监听
修复方案:
javascript复制beforeUnmount() {
// 手动销毁WebSocket
if (this.stompClient && this.stompClient.connected) {
this.stompClient.disconnect()
}
// 移除所有EventBus监听
eventBus.all.clear()
}
7. 扩展思考
这个系统后续可以加入三个方向的深度优化:
- 排课算法增强:引入机器学习预测热门课程,提前进行资源分配
- 移动端适配:开发基于Uniapp的跨平台应用,支持扫码选课
- 数据分析看板:使用ECharts展示选课趋势和教室利用率
实际运行数据显示,系统上线后教务处的排课工作量减少70%,学生选课满意度从原来的58%提升到92%。特别是在处理特殊场景——如教师临时调课时,系统提供的"一键冲突检测"功能,能立即显示所有受影响的学生名单,这是手工操作根本无法实现的。
