SpringBoot+Vue3学生选课系统开发实战
1. 项目概述这个基于SpringBoot2Vue3MyBatis-PlusMySQL8.0技术栈的学生选课系统是一个典型的Java Web全栈项目。我在实际开发中发现这类系统虽然业务逻辑不复杂但完整实现前后端分离架构、处理好数据一致性、保证系统性能等方面都有不少值得注意的技术细节。系统主要包含学生端和管理端两个模块学生可以进行课程查询、选课退课、课表查看等操作管理员则负责课程管理、学生管理、教师管理等后台功能。采用前后端分离架构后端提供RESTful API前端通过axios调用接口整体符合现代Web应用开发规范。2. 技术栈选型解析2.1 后端技术组合SpringBoot2作为基础框架我选择2.7.x稳定版本而非最新的3.x系列主要考虑因素是企业生产环境对JDK17的接受度还不够高2.x版本有更丰富的社区支持与MyBatis-Plus等组件的兼容性更成熟MyBatis-Plus 3.5.x版本提供了强大的单表CRUD操作支持通过Lambda表达式可以写出更优雅的查询条件// 查询计算机学院开设的选修课 LambdaQueryWrapperCourse query new LambdaQueryWrapper(); query.eq(Course::getDepartment, 计算机学院) .eq(Course::getType, 选修) .orderByAsc(Course::getCourseId); ListCourse courses courseMapper.selectList(query);2.2 前端技术方案Vue3的组合式API相比Options API更适合复杂交互场景。在选课页面开发时我特别利用了这些特性使用ref和reactive管理组件状态用computed属性实现选课学分限制的实时计算通过watchEffect监听选课列表变化// 选课逻辑示例 const selectedCourses ref([]) const totalCredits computed(() { return selectedCourses.value.reduce((sum, course) sum course.credits, 0) }) watchEffect(() { if(totalCredits.value 30) { showCreditLimitWarning() } })2.3 数据库设计要点MySQL8.0提供了窗口函数、CTE等高级特性但在选课系统中最关键的还是合理的表结构设计CREATE TABLE student_course ( id bigint NOT NULL AUTO_INCREMENT, student_id varchar(20) NOT NULL COMMENT 学号, course_id varchar(20) NOT NULL COMMENT 课程编号, select_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, status tinyint NOT NULL DEFAULT 1 COMMENT 1-有效 0-退课, PRIMARY KEY (id), UNIQUE KEY uk_student_course (student_id,course_id), KEY idx_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;特别注意建立联合唯一索引防止重复选课添加课程ID索引优化查询性能使用utf8mb4字符集支持完整Unicode3. 核心功能实现细节3.1 选课业务逻辑选课不是简单的insert操作需要处理多种约束条件课程剩余名额检查学生已选学分统计时间冲突检测先修课程要求验证我采用事务乐观锁的方案保证数据一致性Transactional public Result selectCourse(Long studentId, Long courseId) { // 1. 检查课程可选性 Course course courseMapper.selectByIdWithLock(courseId); if(course.getSelected() course.getCapacity()) { return Result.fail(课程已满); } // 2. 检查学生已选学分 Integer selectedCredits scMapper.sumSelectedCredits(studentId); if(selectedCredits course.getCredits() MAX_CREDITS) { return Result.fail(超过学分限制); } // 3. 创建选课记录 StudentCourse sc new StudentCourse(); sc.setStudentId(studentId); sc.setCourseId(courseId); scMapper.insert(sc); // 4. 更新课程已选人数 course.setSelected(course.getSelected() 1); courseMapper.updateById(course); return Result.success(); }3.2 高并发场景处理选课系统经常面临开学季的高并发压力我通过以下措施提升系统性能Redis缓存课程余量信息// 课程余量缓存示例 public Integer getCourseRemain(Long courseId) { String key course:remain: courseId; Integer remain redisTemplate.opsForValue().get(key); if(remain null) { remain courseMapper.selectRemain(courseId); redisTemplate.opsForValue().set(key, remain, 5, TimeUnit.MINUTES); } return remain; }使用分布式锁防止超卖public boolean selectCourseWithLock(Long studentId, Long courseId) { String lockKey lock:course: courseId; try { // 尝试获取分布式锁 Boolean locked redisTemplate.opsForValue().setIfAbsent( lockKey, 1, 10, TimeUnit.SECONDS); if(Boolean.TRUE.equals(locked)) { return doSelectCourse(studentId, courseId); } return false; } finally { redisTemplate.delete(lockKey); } }数据库连接池优化配置spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 18000004. 前后端交互设计4.1 API接口规范采用RESTful风格设计API部分关键接口示例功能方法路径参数查询可选课程GET/api/courses/availablepage,size,department学生选课POST/api/selection{studentId,courseId}退课DELETE/api/selection/{id}-查询课表GET/api/schedule/{studentId}-4.2 跨域与安全配置SpringSecurity配置示例Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(/api/admin/**).hasRole(ADMIN) .antMatchers(/api/**).authenticated() .anyRequest().permitAll() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }Axios请求拦截器示例service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer ${token} } return config }, error { return Promise.reject(error) })5. 部署与监控方案5.1 多环境配置使用SpringBoot的profile特性管理不同环境配置# application-dev.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/course_selection_dev username: dev_user password: dev123 # application-prod.yml server: port: 80 spring: datasource: url: jdbc:mysql://prod-db:3306/course_selection username: ${DB_USER} password: ${DB_PASSWORD}5.2 监控与日志集成Prometheus监控Configuration public class MetricsConfig { Bean MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, course-selection-system ); } }日志收集方案使用Logback输出JSON格式日志Filebeat收集日志发送到ELK关键业务操作记录审计日志!-- logback-spring.xml -- appender nameJSON classch.qos.logback.core.ConsoleAppender encoder classnet.logstash.logback.encoder.LogstashEncoder/ /appender6. 常见问题与优化建议6.1 性能优化经验课程列表分页查询优化-- 避免使用count(*)查询总数 SELECT SQL_CALC_FOUND_ROWS * FROM course WHERE status 1 ORDER BY create_time DESC LIMIT 0, 20; SELECT FOUND_ROWS() AS total;N1查询问题解决# mybatis-plus配置 mybatis-plus: global-config: db-config: select-strategy: not_empty6.2 事务处理陷阱事务方法自调用失效问题// 错误示例 public void processSelection(Long studentId, ListLong courseIds) { courseIds.forEach(courseId - { this.selectCourse(studentId, courseId); // 事务不会生效 }); } // 正确做法 Transactional public void batchSelect(Long studentId, ListLong courseIds) { for(Long courseId : courseIds) { selectCourseInternal(studentId, courseId); } }事务超时设置Transactional(timeout 30) public void complexOperation() { // 长时间运行的操作 }6.3 前端性能提升课程列表虚拟滚动template RecycleScroller classscroller :itemscourses :item-size72 key-fieldid v-slot{ item } CourseItem :courseitem / /RecycleScroller /templateAPI请求防抖import { debounce } from lodash-es; const searchCourses debounce(async (keyword) { const res await api.searchCourses(keyword); courses.value res.data; }, 500);7. 项目文档要点完善的文档应该包含接口文档Swagger或YAPIOperation(summary 选课接口) PostMapping(/selection) public Result selectCourse( Parameter(description 学生ID) RequestParam Long studentId, Parameter(description 课程ID) RequestParam Long courseId) { // 实现逻辑 }数据库设计文档包含ER图部署手册Docker Compose示例version: 3 services: app: image: course-selection:1.0 ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod depends_on: - redis - mysql mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: course_selection压力测试报告JMeter测试计划代码规范检查清单SonarQube配置在实际开发中我发现这些技术决策和实现细节对系统的稳定性、性能和可维护性都有显著影响。特别是选课业务中的并发控制和事务管理需要特别注意各种边界条件的处理。