news 2026/7/3 13:28:33

SpringBoot+Vue3学生选课系统开发实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue3学生选课系统开发实战

1. 项目概述

这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0技术栈的学生选课系统,是一个典型的Java Web全栈项目。我在实际开发中发现,这类系统虽然业务逻辑不复杂,但完整实现前后端分离架构、处理好数据一致性、保证系统性能等方面都有不少值得注意的技术细节。

系统主要包含学生端和管理端两个模块:学生可以进行课程查询、选课退课、课表查看等操作;管理员则负责课程管理、学生管理、教师管理等后台功能。采用前后端分离架构,后端提供RESTful API,前端通过axios调用接口,整体符合现代Web应用开发规范。

2. 技术栈选型解析

2.1 后端技术组合

SpringBoot2作为基础框架,我选择2.7.x稳定版本而非最新的3.x系列,主要考虑因素是:

  1. 企业生产环境对JDK17的接受度还不够高
  2. 2.x版本有更丰富的社区支持
  3. 与MyBatis-Plus等组件的兼容性更成熟

MyBatis-Plus 3.5.x版本提供了强大的单表CRUD操作支持,通过Lambda表达式可以写出更优雅的查询条件:

// 查询计算机学院开设的选修课 LambdaQueryWrapper<Course> query = new LambdaQueryWrapper<>(); query.eq(Course::getDepartment, "计算机学院") .eq(Course::getType, "选修") .orderByAsc(Course::getCourseId); List<Course> courses = courseMapper.selectList(query);

2.2 前端技术方案

Vue3的组合式API相比Options API更适合复杂交互场景。在选课页面开发时,我特别利用了这些特性:

  1. 使用ref和reactive管理组件状态
  2. 用computed属性实现选课学分限制的实时计算
  3. 通过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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

特别注意:

  1. 建立联合唯一索引防止重复选课
  2. 添加课程ID索引优化查询性能
  3. 使用utf8mb4字符集支持完整Unicode

3. 核心功能实现细节

3.1 选课业务逻辑

选课不是简单的insert操作,需要处理多种约束条件:

  1. 课程剩余名额检查
  2. 学生已选学分统计
  3. 时间冲突检测
  4. 先修课程要求验证

我采用事务+乐观锁的方案保证数据一致性:

@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 高并发场景处理

选课系统经常面临开学季的高并发压力,我通过以下措施提升系统性能:

  1. 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; }
  1. 使用分布式锁防止超卖
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); } }
  1. 数据库连接池优化配置
spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000

4. 前后端交互设计

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 MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { return registry -> registry.config().commonTags( "application", "course-selection-system" ); } }

日志收集方案:

  1. 使用Logback输出JSON格式日志
  2. Filebeat收集日志发送到ELK
  3. 关键业务操作记录审计日志
<!-- logback-spring.xml --> <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender>

6. 常见问题与优化建议

6.1 性能优化经验

  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;
  1. N+1查询问题解决:
# mybatis-plus配置 mybatis-plus: global-config: db-config: select-strategy: not_empty

6.2 事务处理陷阱

  1. 事务方法自调用失效问题:
// 错误示例 public void processSelection(Long studentId, List<Long> courseIds) { courseIds.forEach(courseId -> { this.selectCourse(studentId, courseId); // 事务不会生效 }); } // 正确做法 @Transactional public void batchSelect(Long studentId, List<Long> courseIds) { for(Long courseId : courseIds) { selectCourseInternal(studentId, courseId); } }
  1. 事务超时设置:
@Transactional(timeout = 30) public void complexOperation() { // 长时间运行的操作 }

6.3 前端性能提升

  1. 课程列表虚拟滚动:
<template> <RecycleScroller class="scroller" :items="courses" :item-size="72" key-field="id" v-slot="{ item }" > <CourseItem :course="item" /> </RecycleScroller> </template>
  1. API请求防抖:
import { debounce } from 'lodash-es'; const searchCourses = debounce(async (keyword) => { const res = await api.searchCourses(keyword); courses.value = res.data; }, 500);

7. 项目文档要点

完善的文档应该包含:

  1. 接口文档(Swagger或YAPI)
@Operation(summary = "选课接口") @PostMapping("/selection") public Result selectCourse( @Parameter(description = "学生ID") @RequestParam Long studentId, @Parameter(description = "课程ID") @RequestParam Long courseId) { // 实现逻辑 }
  1. 数据库设计文档(包含ER图)

  2. 部署手册(Docker Compose示例)

version: '3' services: app: image: course-selection:1.0 ports: - "8080:8080" environment: - SPRING_PROFILES_ACTIVE=prod depends_on: - redis - mysql mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: course_selection
  1. 压力测试报告(JMeter测试计划)

  2. 代码规范检查清单(SonarQube配置)

在实际开发中,我发现这些技术决策和实现细节对系统的稳定性、性能和可维护性都有显著影响。特别是选课业务中的并发控制和事务管理,需要特别注意各种边界条件的处理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 13:24:07

3分钟掌握WorkshopDL:无需Steam账号的创意工坊模组下载完整指南

3分钟掌握WorkshopDL&#xff1a;无需Steam账号的创意工坊模组下载完整指南 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否曾在Epic Games Store或GOG平台购买了心爱的游…

作者头像 李华
网站建设 2026/7/3 13:22:53

更多Bash Shell命令实战——从进程管理到数据归档

前言 本文是《更多的bash shell命令》课程的实践总结&#xff0c;通过在Xshell中连接虚拟机&#xff0c;亲手操作了Linux中进程管理、磁盘管理、数据排序、搜索和归档等常用命令。本文将按照PPT的顺序&#xff0c;逐一演示每个命令的用法&#xff0c;并附上操作截图和常见参数说…

作者头像 李华
网站建设 2026/7/3 13:21:40

ComfyUI到Python代码转换:解锁AI工作流自动化的关键技术

ComfyUI到Python代码转换&#xff1a;解锁AI工作流自动化的关键技术 【免费下载链接】ComfyUI-to-Python-Extension A powerful tool that translates ComfyUI workflows into executable Python code. 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-to-Python-Exten…

作者头像 李华
网站建设 2026/7/3 13:19:07

KMR221与PIC18F27K42的嵌入式电压管理系统设计

1. KMR221与PIC18F27K42的硬件协同架构解析在嵌入式电压管理系统中&#xff0c;KMR221作为一款高精度电压监测芯片&#xff0c;与PIC18F27K42微控制器的组合堪称黄金搭档。这套方案的核心优势在于KMR221的0.5%电压检测精度与PIC18F27K42的纳安级功耗特性完美结合。实际部署时&a…

作者头像 李华
网站建设 2026/7/3 13:18:37

漫画和小说都在NAS里,却只能回家看?用Kavita打造随身数字书屋

前言 电子书、漫画和小说收藏得越来越多以后&#xff0c;最先出现的问题往往不是没有内容可看&#xff0c;而是想看的时候找不到。 有些文件放在电脑硬盘里&#xff0c;有些存进了NAS&#xff0c;还有一些散落在不同文件夹中。换到手机或平板阅读时&#xff0c;又要重新寻找文…

作者头像 李华
网站建设 2026/7/3 13:18:23

嵌入式系统高精度电压管理方案设计与实现

1. 项目背景与核心需求在嵌入式系统开发中&#xff0c;精确的电压管理一直是个关键挑战。电源电压的微小波动可能导致MCU工作异常、传感器读数失真甚至系统崩溃。传统方案要么成本高昂&#xff0c;要么响应速度不足&#xff0c;难以满足现代嵌入式设备对稳定性和实时性的双重需…

作者头像 李华