1. 项目概述
作为一名从事高校信息化建设多年的开发者,我深知传统选课系统的痛点:每到选课季,服务器崩溃、数据不同步、操作繁琐等问题层出不穷。去年我校决定重构选课系统时,我们团队选择了SSM(Spring+SpringMVC+MyBatis)框架作为技术栈,经过三个月的开发与优化,最终交付了一套稳定高效的选课系统。这个系统日均承载2万+并发请求,在最近的选课季实现了零故障运行。
1.1 系统定位与价值
这个选课系统主要解决三大核心问题:
- 选课流程数字化:将传统纸质选课转为线上操作,选课时间从原来的3天缩短至2小时
- 资源分配智能化:通过算法优化解决课程容量冲突,使教室利用率提升40%
- 管理流程规范化:建立统一数据标准,教务处理效率提升60%
系统采用B/S架构,前端使用Vue.js实现响应式布局,后端基于SSM框架构建,数据库选用MySQL 8.0。这种技术组合既保证了系统性能,又便于后续功能扩展。
2. 技术架构解析
2.1 SSM框架整合方案
我们采用的SSM框架组合各组件分工明确:
- Spring 5.2:作为核心容器,通过IoC管理所有Bean,AOP处理事务和日志
- SpringMVC:采用RESTful风格设计API接口,前后端完全分离
- MyBatis 3.5:配合PageHelper分页插件,优化大数据量查询
java复制// 典型Controller示例
@RestController
@RequestMapping("/api/course")
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("/list")
public Result listCourses(@RequestParam Map<String,Object> params){
PageUtils page = courseService.queryPage(params);
return Result.ok().put("page", page);
}
}
关键点:Spring的声明式事务管理要特别注意@Transactional注解的 propagation 和 isolation 属性设置,在高并发场景下我们最终选择了PROPAGATION_REQUIRED和ISOLATION_READ_COMMITTED的组合。
2.2 数据库设计要点
数据库设计遵循第三范式,主要表包括:
- 用户体系:user(基础信息)、student_users(学生扩展)、teacher_users(教师扩展)
- 课程体系:course_subjects(科目)、course_information(课程详情)
- 业务表:course_selection_record(选课记录)、student_grades(成绩)
sql复制CREATE TABLE `course_selection_record` (
`record_id` int(11) NOT NULL AUTO_INCREMENT,
`course_id` int(11) NOT NULL COMMENT '关联课程ID',
`student_id` int(11) NOT NULL,
`select_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` tinyint(4) DEFAULT '1' COMMENT '1-有效 0-已退选',
PRIMARY KEY (`record_id`),
UNIQUE KEY `idx_course_student` (`course_id`,`student_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
经验:在选课记录表上创建(course_id, student_id)的联合唯一索引,可以有效防止重复选课,这是我们踩过坑后优化的结果。
3. 核心功能实现
3.1 选课业务流程
选课流程采用状态机模式设计,包含以下状态流转:
- 可选状态:课程开放选课
- 锁定状态:学生点击选课时的中间状态
- 已选状态:选课成功
- 退选中:发起退课申请
- 已退选:教师/管理员审核通过
java复制// 状态机实现片段
public class CourseSelectionFSM {
private SelectionState currentState;
public synchronized boolean changeState(SelectionEvent event) {
switch(currentState) {
case AVAILABLE:
if(event == Event.SELECT) {
currentState = SelectionState.LOCKED;
return true;
}
break;
// 其他状态处理...
}
return false;
}
}
并发控制方案:
- 乐观锁:使用version字段控制更新
- 分布式锁:Redis实现选课操作的互斥
- 队列削峰:RabbitMQ缓冲选课请求
3.2 权限控制系统
采用RBAC(基于角色的访问控制)模型:
-
角色划分:
- 学生:选课、查成绩、论坛交流
- 教师:管理课程、录入成绩
- 管理员:系统配置、用户管理
-
权限拦截实现:
java复制@Aspect
@Component
public class PermissionAspect {
@Before("@annotation(requiredPermission)")
public void checkPermission(RequiredPermission requiredPermission) {
String perm = requiredPermission.value();
if(!SecurityUtils.getSubject().isPermitted(perm)) {
throw new UnauthorizedException();
}
}
}
踩坑记录:初期使用简单的拦截器实现权限控制,后来发现粒度不够细,改用Spring AOP后可以精确到方法级别,配合Shiro框架效果更好。
4. 性能优化实践
4.1 数据库优化
-
索引策略:
- 为所有外键字段建立索引
- 高频查询字段建立组合索引
- 使用EXPLAIN分析执行计划
-
SQL优化:
sql复制-- 优化前(全表扫描)
SELECT * FROM course WHERE course_name LIKE '%数学%';
-- 优化后(索引覆盖)
SELECT course_id,course_name FROM course
WHERE course_name LIKE '数学%'
ORDER BY create_time DESC LIMIT 10;
4.2 缓存设计
采用多级缓存架构:
- 本地缓存:Caffeine缓存课程基本信息
- 分布式缓存:Redis存储热点数据
- 选课人数计数器
- 课程余量信息
- 缓存更新策略:
- 课程信息:定时刷新+事件驱动更新
- 选课数据:实时更新
java复制// 缓存注解使用示例
@Cacheable(value = "courses", key = "#courseId", unless = "#result == null")
public CourseVO getCourseDetail(Integer courseId) {
return courseMapper.selectDetailById(courseId);
}
5. 安全防护措施
5.1 常见攻击防护
-
SQL注入:
- 全程使用MyBatis参数化查询
- 禁止拼接SQL语句
-
XSS攻击:
- 前端使用vue-sanitize过滤输入
- 后端采用Jackson转义特殊字符
-
CSRF防护:
- 启用Spring Security的CSRF保护
- 敏感操作增加二次确认
5.2 数据安全
-
敏感数据加密:
- 密码使用BCrypt加密
- 个人信息字段数据库加密存储
-
审计日志:
java复制@LogRecord(module = "选课管理", type = "退课")
public void cancelSelection(Integer recordId) {
// 业务逻辑
}
6. 部署与监控
6.1 服务器架构
采用Docker Compose部署方案:
yaml复制version: '3'
services:
web:
image: nginx:1.21
ports:
- "80:80"
volumes:
- ./dist:/usr/share/nginx/html
app:
image: openjdk:11-jre
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=123456
volumes:
- ./mysql-data:/var/lib/mysql
6.2 监控体系
- 应用监控:Spring Boot Actuator + Prometheus
- 日志收集:ELK Stack(Elasticsearch+Logstash+Kibana)
- 报警机制:异常日志触发企业微信通知
7. 开发经验总结
7.1 值得推广的做法
- 接口文档自动化:使用Swagger UI生成实时API文档
- 代码规范检查:集成Checkstyle+SpotBugs
- CI/CD流程:GitLab Runner实现自动化部署
7.2 遇到的典型问题
-
选课超卖问题:
- 现象:热门课程出现超额选课
- 解决方案:Redis分布式锁+数据库乐观锁双重保障
-
性能瓶颈:
- 现象:选课高峰期响应变慢
- 优化:引入消息队列异步处理非核心流程
-
数据一致性问题:
- 现象:缓存与数据库偶尔不一致
- 解决:采用Cache Aside Pattern策略
这个项目的完整源码已经整理到Git仓库,包含详细的部署文档和数据库脚本。在实际开发中,最大的体会是一定要在设计阶段充分考虑扩展性和性能问题,否则后期重构成本会非常高。特别是在权限系统和选课核心流程的设计上,我们经历了多次迭代才达到理想效果。