1. 项目背景与痛点分析
每到新学期开学季,高校教务处门口总能看到这样的场景:几十名学生排着长队,手里攥着皱巴巴的纸质申请表,焦急地等待盖章。这就是传统重修课程管理流程的真实写照——学生需要先到学院办公室申请盖章,再去财务处缴纳重修费用,最后返回教学办抢剩余的重修名额。任何一个环节出现问题,整个流程就得重头再来。
更令人头疼的是,重修相关的数据分散在多个部门:名额信息在教学办、缴费记录在财务处、成绩录入在任课教师手中。这种碎片化管理导致:
- 学生需要反复跑腿确认各环节状态
- 教师手工核对表格效率低下且易出错
- 管理员无法实时掌握整体重修情况
- 历史数据追溯困难,统计报表制作耗时
2. 系统整体设计
2.1 技术选型与架构设计
基于上述痛点,我们采用SpringBoot+MySQL技术栈构建B/S架构的"重修通"管理系统,主要考量如下:
后端技术选型:
- SpringBoot 2.7.x:简化配置,快速构建RESTful API
- Spring Security:实现基于角色的访问控制
- MyBatis-Plus:增强型ORM框架,提高数据库操作效率
- Redis:缓存热点数据(如剩余名额)
- Hutool:工具库处理加密、日期等常见操作
前端技术选型:
- Thymeleaf:服务端模板引擎,实现动态页面渲染
- Bootstrap 5:响应式布局适配各种终端设备
- ECharts:可视化数据统计报表
- Layer:轻量级弹层组件
数据库设计:
- MySQL 8.0:关系型数据库存储结构化数据
- 采用InnoDB引擎支持事务处理
- 建立适当的索引优化查询性能
- 定期备份机制保障数据安全
2.2 系统功能模块
系统采用经典的三层架构设计,核心功能模块包括:
2.2.1 用户管理模块
- 多角色登录(学生/教师/管理员)
- 密码加密存储(BCrypt算法)
- 个人信息维护
- 权限分级控制
2.2.2 课程管理模块
- 重修课程发布与维护
- 课时/学分/考核方式设置
- 教师课程关联
- 名额实时更新
2.2.3 选课流程模块
- 在线选课申请
- 教师审核机制
- 状态实时推送
- 历史记录追溯
2.2.4 成绩管理模块
- 成绩录入与修改
- 成绩统计分析
- 导出打印功能
- 补考标记处理
2.2.5 通知公告模块
- 重要通知发布
- 紧急公告置顶
- 已读状态标记
- 历史公告归档
3. 核心功能实现细节
3.1 选课并发控制
重修课程常出现"秒杀"现象,需要解决高并发下的数据一致性问题。我们采用多级控制策略:
// 乐观锁控制名额扣减 @Transactional public boolean selectCourse(Long courseId, Long studentId) { // 1. 校验选课资格 if(!checkQualification(studentId)) { throw new BusinessException("不符合选课条件"); } // 2. 乐观锁更新 int updated = courseMapper.updateRemain( courseId, "remain_num = remain_num - 1", "remain_num > 0" ); if(updated == 0) { return false; // 名额不足 } // 3. 记录选课关系 CourseSelection selection = new CourseSelection(); selection.setCourseId(courseId); selection.setStudentId(studentId); selection.setStatus(0); // 待审核 selectionMapper.insert(selection); return true; }关键技术点:
- 前置条件校验(是否已选/冲突检测等)
- 数据库乐观锁保证原子性
- 事务管理确保数据一致性
- Redis缓存减轻数据库压力
3.2 审核流程设计
采用状态机模式管理选课生命周期:
[待审核] → [已通过] → [成绩待录入] ↘ [已拒绝]状态变更时自动触发相关事件:
- 通过:扣减名额、生成课表
- 拒绝:释放名额、通知学生
- 超时:自动取消未处理申请
// 状态变更处理示例 public void processApplication(Long selectionId, boolean approved, String comment) { CourseSelection selection = selectionMapper.selectById(selectionId); if(selection.getStatus() != 0) { throw new BusinessException("申请已处理"); } if(approved) { selection.setStatus(1); // 已通过 courseService.decreaseRemain(selection.getCourseId()); messageService.notifyStudent(selection.getStudentId(), "选课已通过"); } else { selection.setStatus(2); // 已拒绝 courseService.increaseRemain(selection.getCourseId()); messageService.notifyStudent(selection.getStudentId(), "选课被拒绝:" + comment); } selection.setProcessTime(LocalDateTime.now()); selectionMapper.updateById(selection); }3.3 实时数据同步
前端通过WebSocket实现关键数据的实时更新:
// 建立WebSocket连接 const socket = new WebSocket(`wss://${location.host}/ws`); // 监听名额变化 socket.addEventListener('message', (event) => { const data = JSON.parse(event.data); if(data.type === 'remain_update') { updateRemainDisplay(data.courseId, data.remain); } }); // 选课成功后触发更新 function selectCourse(courseId) { fetch(`/api/select/${courseId}`, {method: 'POST'}) .then(res => { if(res.ok) { showSuccess('选课申请已提交'); } }); }4. 数据库详细设计
4.1 核心表结构
用户基础表(sys_user)
CREATE TABLE `sys_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '登录账号', `password` varchar(100) NOT NULL COMMENT '加密密码', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `role_type` tinyint NOT NULL COMMENT '角色(1学生 2教师 3管理员)', `college` varchar(100) DEFAULT NULL COMMENT '学院', `major` varchar(100) DEFAULT NULL COMMENT '专业', `status` tinyint DEFAULT '1' COMMENT '状态(0禁用 1正常)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;重修课程表(course)
CREATE TABLE `course` ( `id` bigint NOT NULL AUTO_INCREMENT, `course_name` varchar(100) NOT NULL COMMENT '课程名称', `teacher_id` bigint NOT NULL COMMENT '授课教师', `credit` decimal(3,1) DEFAULT NULL COMMENT '学分', `total_hours` int DEFAULT NULL COMMENT '总课时', `exam_type` tinyint DEFAULT NULL COMMENT '考核方式', `max_num` int NOT NULL COMMENT '最大人数', `remain_num` int NOT NULL COMMENT '剩余名额', `schedule` text COMMENT '上课时间安排', `classroom` varchar(50) DEFAULT NULL COMMENT '教室', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_teacher` (`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;选课记录表(course_selection)
CREATE TABLE `course_selection` ( `id` bigint NOT NULL AUTO_INCREMENT, `course_id` bigint NOT NULL, `student_id` bigint NOT NULL, `status` tinyint NOT NULL DEFAULT '0' COMMENT '0待审核 1已通过 2已拒绝', `process_comment` varchar(255) DEFAULT NULL COMMENT '处理意见', `process_time` datetime DEFAULT NULL COMMENT '处理时间', `score` decimal(5,2) DEFAULT NULL COMMENT '成绩', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_course_student` (`course_id`,`student_id`), KEY `idx_student` (`student_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;4.2 性能优化策略
- 读写分离:查���操作使用从库,减轻主库压力
- 缓存策略:
- 课程余量使用Redis缓存
- 课程详情采用多级缓存
- 索引优化:
- 高频查询字段建立组合索引
- 避免过度索引影响写入性能
- SQL优化:
- 避免SELECT *
- 合理使用JOIN
- 大数据量查询分页处理
5. 系统部署方案
5.1 环境要求
开发环境:
- JDK 1.8+
- Maven 3.6+
- MySQL 5.7+/MariaDB 10.3+
- Redis 5.0+
生产环境建议:
- 2核4G云服务器(最低配置)
- CentOS 7.6+/Ubuntu 18.04+
- Nginx 1.18+ 作为反向代理
- 配置HTTPS保障传输安全
5.2 部署步骤
- 数据库初始化:
mysql -u root -p < init.sql- 应用打包:
mvn clean package -DskipTests- 启动服务:
java -jar target/repeat-course-1.0.0.jar \ --spring.profiles.active=prod \ --server.port=8080- Nginx配置示例:
server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 静态资源缓存 location ~* \.(js|css|png|jpg)$ { expires 30d; access_log off; } }6. 项目开发经验分享
6.1 踩坑实录
并发选课问题: 初期采用先查询再更新的方式,在高并发场景下出现超卖现象。解决方案:
- 改为乐观锁直接更新
- 添加Redis分布式锁作为第二道防线
- 前端增加防重复提交控制
事务失效场景: 发现@Transactional在某些情况下不生效,排查发现:
- 自调用问题(AOP代理失效)
- 异常类型未配置(默认只回滚RuntimeException)
- 数据库引擎不支持(MyISAM不支持事务)
缓存一致性问题: 课程信息更新后缓存未及时失效,导致数据不一致。改进方案:
- 采用Cache Aside Pattern
- 设置合理的过期时间
- 重要操作强制刷新缓存
6.2 性能优化技巧
接口响应优化:
- 启用Gzip压缩
- 合并前端请求
- 使用@Cacheable注解缓存热点数据
数据库访问优化:
- 批量操作代替循环单条处理
- 合理使用@Transactional减少事务范围
- 避免N+1查询问题
前端加载优化:
- 组件懒加载
- 图片压缩处理
- CDN加速静态资源
6.3 安全防护措施
输入验证:
- 服务端校验所有参数
- 使用Hibernate Validator
- 防范XSS/SQL注入
权限控制:
- 基于注解的权限检查
- 数据权限过滤
- 敏感操作日志记录
会话管理:
- JWT无状态认证
- 合理的token过期时间
- 防止CSRF攻击
7. 项目扩展方向
移动端适配:
- 开发微信小程序版本
- 响应式布局优化
- 推送通知集成
智能推荐:
- 基于历史数据的选课推荐
- 冲突检测提醒
- 个性化课表生成
数据分析:
- 重修率统计分析
- 教学效果评估
- 预测模型构建
系统集成:
- 与教务系统对接
- 统一身份认证
- 财务系统联动
在实际开发过程中,建议采用迭代式开发模式,先实现核心功能再逐步扩展。对于高校实际部署,需要充分考虑与现有系统的兼容性和数据迁移方案。