毕业设计实战:基于Spring Boot的学生选课系统全栈开发

69 阅读20分钟

一、项目背景:高校选课管理数字化的迫切需求

在高校规模化发展背景下,传统学生选课模式面临流程繁琐、效率低下、数据混乱三大核心痛点。据教育信息化报告显示,2023年仍有超40%的高校采用人工登记、Excel统计的选课方式,导致选课高峰期出现“抢课拥堵”“信息登记错误”“成绩统计滞后”等问题,不仅增加教务人员工作负担,也影响学生选课体验与学习规划。

为破解这一困境,基于Spring Boot的学生选课系统应运而生。系统以“选课流程线上化、信息管理规范化、成绩统计自动化”为核心目标,采用B/S架构构建一体化选课服务平台,整合管理员统筹管理、教师审核选课与成绩录入、学生自主选课与成绩查询等核心功能,建立“管理员-教师-学生”三层协同应用模式,推动高校选课管理从“线下人工化”向“线上系统化、数据化、高效化”转型。

二、技术架构:学生选课系统的全栈技术选型

项目围绕“稳定性优先、易维护、高适配”三大原则,选用成熟且贴合高校选课场景的技术栈,确保系统在选课高峰期多用户并发操作、大量课程与成绩数据存储时的可靠性:

技术模块具体工具/技术核心作用
后端框架Spring Boot 2.x快速构建高效API接口,处理课程管理、选课审核、成绩统计等核心业务逻辑
前端技术JSP + Layui构建简洁直观的管理与选课界面,适配管理员、教师、学生三类角色操作场景
数据库MySQL 8.0安全存储用户信息、课程数据、选课记录、成绩信息等核心数据
架构模式B/S(浏览器/服务器)无需客户端安装,支持教务人员在办公设备、师生在个人设备随时访问
文件存储本地文件系统存储教师/学生头像、课程封面图片等静态资源
开发工具Eclipse + Navicat高效完成代码开发与数据库可视化管理,便捷维护选课数据

三、项目全流程:6步完成学生选课系统开发

3.1 第一步:需求分析——明确系统核心价值

针对传统选课管理的“效率低、体验差”痛点,系统聚焦“选课流程简化、信息管理集中、数据同步实时”,明确三类角色的核心需求:

3.1.1 功能性需求

  1. 三角色权限体系

    • 管理员:个人中心(密码修改、信息维护)、用户管理(新增/编辑/删除教师/学生账号)、专业管理(配置专业信息)、课程管理(上架/编辑/下架课程)、选课管理(查看全校选课记录)、成绩管理(查看全校学生成绩);
    • 教师:个人中心(密码修改)、课程查询(查看本人授课课程)、选课审核(审核学生选课申请)、成绩管理(录入/编辑学生课程成绩、填写评语);
    • 学生:个人中心(资料维护、密码修改)、课程查询(按专业/课程分类筛选课程)、自主选课(提交选课申请)、选课查询(查看本人选课记录与审核状态)、成绩查询(查看已修课程成绩与评语)。
  2. 核心业务功能

    • 课程全生命周期管理:从课程信息录入、专业关联到学生选课的完整流程;
    • 选课审核闭环:学生提交选课申请→教师审核(通过/拒绝)→学生接收审核结果;
    • 成绩管理闭环:教师录入学生成绩→系统自动判断“是否合格”→学生查询成绩与评语;
    • 多维度查询:支持管理员按角色查询用户、教师按课程查询学生、学生按条件筛选课程;
    • 数据校验:对选课冲突(如同一时间段选两门课)、成绩范围(0-100分)进行自动校验,避免错误数据。

3.1.2 非功能性需求

  • 系统性能:支持100+并发用户操作(选课高峰期),课程查询响应时间<2秒,选课申请提交响应时间<1秒;
  • 数据安全:用户密码加密存储(MD5),学生成绩等敏感信息仅授权角色可见(如学生仅能查看本人成绩);
  • 用户体验:界面符合高校师生操作习惯,核心功能(如选课、成绩查询)操作步骤≤2步;
  • 兼容性:支持Chrome、Edge、Firefox等主流浏览器,适配办公电脑与学生笔记本常见分辨率(1366×768及以上)。

3.2 第二步:系统设计——构建整体架构

系统采用分层设计思想,确保各模块职责清晰、可维护性强,同时满足高校选课“高并发、高可靠”的要求:

3.2.1 系统总体架构

  1. 前端架构

    • 基于JSP实现页面动态渲染,结合Layui提供的表单、表格、弹窗等组件,快速搭建管理与选课界面;
    • 采用Ajax实现异步数据交互(如学生提交选课申请、教师审核选课),避免页面刷新,提升操作流畅度;
    • 按角色划分权限视图:管理员登录后展示完整管理菜单,教师仅显示“课程查询、选课审核、成绩管理”功能,学生仅可见“课程查询、选课、成绩查询”模块。
  2. 后端架构

    • 基于Spring Boot实现分层架构:Controller(接口层,处理HTTP请求)、Service(业务逻辑层,实现选课审核、成绩计算等核心功能)、Mapper(数据访问层,操作数据库);
    • 统一异常处理机制:捕获业务异常(如“课程已选满”“选课时间已截止”“成绩超出范围”)并返回友好提示;
    • 权限拦截器:验证用户登录状态与角色权限,防止越权访问(如学生无法进入教师成绩录入页面)。
  3. 数据持久层

    • 采用MyBatis实现数据库操作,通过XML配置SQL语句,降低代码耦合度;
    • 配置HikariCP数据库连接池,优化数据库访问性能,确保选课高峰期多用户同时操作时的稳定性。

3.2.2 核心数据库设计

系统设计7张核心数据表,覆盖用户、专业、课程、选课、成绩等全业务场景,关键表结构如下:

表名核心字段作用
管理员表(admin)id、用户名、密码(加密)、角色、创建时间存储管理员账号信息,控制系统管理权限
教师表(jiaoshi)id、教师账号、密码(加密)、教师姓名、年龄、职称、性别、手机、照片、创建时间存储教师基本信息,支持教师登录与操作
学生表(xuesheng)id、学号、密码(加密)、姓名、年龄、专业、性别、手机、照片、创建时间存储学生基本信息,支持学生登录与选课
专业表(zhuanye)id、专业名称、创建时间管理专业分类体系(如“计算机科学与技术”“汉语言文学”)
课程表(kecheng)id、课程名称、课程分类、开课时间、结束时间、课程学分、课程内容、封面、教师账号、教师姓名、点击次数、创建时间存储课程核心信息,供学生查询与选课
学生选课表(xueshengxuanke)id、课程名称、是否选择、选择时间、教师账号、教师姓名、学号、姓名、是否审核、审核回复、创建时间记录学生选课申请与审核结果
学生成绩表(xueshengchengji)id、课程名称、学号、姓名、成绩、是否合格、评语、教师账号、教师姓名、创建时间存储学生课程成绩,支持教师录入与学生查询

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现系统核心业务逻辑,重点突破“课程管理”“选课审核”“成绩管理”三大核心场景,确保功能符合高校选课实际需求:

3.3.1 课程管理功能实现(管理员核心操作)

@RestController
@RequestMapping("/api/course")
public class CourseController {
    
    @Autowired
    private CourseService courseService;
    
    /**
     * 管理员新增课程
     */
    @PostMapping("/add")
    public ResponseEntity<?> addCourse(@RequestBody CourseDTO courseDTO,
                                     @RequestHeader("adminId") Long adminId) {
        try {
            // 参数校验:课程名称、开课时间、结束时间、课程学分为必填项
            if (StringUtils.isEmpty(courseDTO.getKechengmingcheng()) || 
                courseDTO.getKaikeshijian() == null || 
                courseDTO.getJieshushijian() == null || 
                courseDTO.getKechengxuefen() == null) {
                return ResponseEntity.badRequest().body("课程名称、开课时间、结束时间、课程学分不能为空");
            }
            
            // 校验开课时间与结束时间逻辑(结束时间需晚于开课时间)
            if (courseDTO.getJieshushijian().before(courseDTO.getKaikeshijian())) {
                return ResponseEntity.badRequest().body("结束时间不能早于开课时间");
            }
            
            // 新增课程
            Kecheng course = courseService.addCourse(courseDTO);
            return ResponseEntity.ok("课程新增成功,课程ID:" + course.getId());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("课程新增失败:" + e.getMessage());
        }
    }
    
    /**
     * 学生/教师查询课程(支持多条件筛选)
     */
    @GetMapping("/list")
    public ResponseEntity<?> getCourseList(
            @RequestParam(required = false) String kechengmingcheng,
            @RequestParam(required = false) String kechengfenlei,
            @RequestParam(required = false) String jiaoshizhanghao, // 教师查询本人课程时传入
            @RequestParam(required = false) String zhuanye, // 学生按专业筛选时传入
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        try {
            // 构建查询条件
            CourseQuery query = new CourseQuery();
            query.setKechengmingcheng(kechengmingcheng);
            query.setKechengfenlei(kechengfenlei);
            query.setJiaoshizhanghao(jiaoshizhanghao);
            query.setZhuanye(zhuanye);
            query.setPage(page);
            query.setSize(size);
            
            // 分页查询课程
            PageResult<CourseVO> result = courseService.getCourseList(query);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("课程查询失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class CourseServiceImpl implements CourseService {
    
    @Autowired
    private KechengMapper courseMapper;
    
    @Override
    public Kecheng addCourse(CourseDTO dto) {
        // 1. 校验课程名称是否重复(同一教师同一学期不允许重复课程)
        KechengExample example = new KechengExample();
        example.createCriteria()
               .andKechengmingchengEqualTo(dto.getKechengmingcheng())
               .andJiaoshizhanghaoEqualTo(dto.getJiaoshizhanghao())
               .andKaikeshijianBetween(
                       getSemesterStart(dto.getKaikeshijian()), 
                       getSemesterEnd(dto.getKaikeshijian()));
        if (courseMapper.countByExample(example) > 0) {
            throw new RuntimeException("该教师本学期已创建同名课程,请勿重复添加");
        }
        
        // 2. 构建课程实体
        Kecheng course = new Kecheng();
        course.setKechengmingcheng(dto.getKechengmingcheng());
        course.setKechengfenlei(dto.getKechengfenlei());
        course.setKaikeshijian(dto.getKaikeshijian());
        course.setJieshushijian(dto.getJieshushijian());
        course.setKechengxuefen(dto.getKechengxuefen());
        course.setKechengneirong(dto.getKechengneirong());
        course.setFengmian(dto.getFengmian()); // 课程封面图片路径
        course.setJiaoshizhanghao(dto.getJiaoshizhanghao());
        course.setJiaoshixingming(dto.getJiaoshixingming());
        course.setClicknum(0); // 初始点击次数为0
        course.setAddtime(new Date());
        
        // 3. 保存课程信息
        courseMapper.insert(course);
        return course;
    }
    
    // 辅助方法:根据开课时间获取学期起始时间(示例:秋季学期9月1日-次年1月15日,春季学期2月20日-7月10日)
    private Date getSemesterStart(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int month = cal.get(Calendar.MONTH) + 1;
        if (month >= 9 || month <= 1) {
            // 秋季学期
            cal.set(Calendar.MONTH, 8); // 9月
            cal.set(Calendar.DAY_OF_MONTH, 1);
        } else {
            // 春季学期
            cal.set(Calendar.MONTH, 1); // 2月
            cal.set(Calendar.DAY_OF_MONTH, 20);
        }
        return cal.getTime();
    }
    
    // 辅助方法:获取学期结束时间
    private Date getSemesterEnd(Date date) {
        // 逻辑类似getSemesterStart,此处省略
        return null;
    }
    
    @Override
    public PageResult<CourseVO> getCourseList(CourseQuery query) {
        // 分页配置
        PageHelper.startPage(query.getPage(), query.getSize());
        
        // 构建查询条件
        KechengExample example = new KechengExample();
        KechengExample.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotEmpty(query.getKechengmingcheng())) {
            criteria.andKechengmingchengLike("%" + query.getKechengmingcheng() + "%");
        }
        if (StringUtils.isNotEmpty(query.getKechengfenlei())) {
            criteria.andKechengfenleiEqualTo(query.getKechengfenlei());
        }
        if (StringUtils.isNotEmpty(query.getJiaoshizhanghao())) {
            criteria.andJiaoshizhanghaoEqualTo(query.getJiaoshizhanghao());
        }
        // 按开课时间倒序,最新课程优先展示
        example.setOrderByClause("kaikeshijian DESC");
        
        // 执行查询
        List<Kecheng> courseList = courseMapper.selectByExample(example);
        PageInfo<Kecheng> pageInfo = new PageInfo<>(courseList);
        
        // 转换为VO(隐藏敏感字段,如教师账号仅教师可见)
        List<CourseVO> voList = courseList.stream()
            .map(course -> {
                CourseVO vo = new CourseVO();
                BeanUtils.copyProperties(course, vo);
                return vo;
            }).collect(Collectors.toList());
        
        return new PageResult<>(voList, pageInfo.getTotal());
    }
}

3.3.2 选课审核功能实现(教师核心操作)

@RestController
@RequestMapping("/api/selection")
public class SelectionController {
    
    @Autowired
    private SelectionService selectionService;
    
    /**
     * 学生提交选课申请
     */
    @PostMapping("/apply")
    public ResponseEntity<?> applySelection(@RequestBody SelectionDTO selectionDTO,
                                          @RequestHeader("studentId") Long studentId) {
        try {
            // 参数校验:课程ID、学号为必填项
            if (selectionDTO.getCourseId() == null || StringUtils.isEmpty(selectionDTO.getXuehao())) {
                return ResponseEntity.badRequest().body("课程ID、学号不能为空");
            }
            
            // 校验选课时间(需在课程开课时间前)
            boolean isInTime = selectionService.checkSelectionTime(selectionDTO.getCourseId());
            if (!isInTime) {
                return ResponseEntity.badRequest().body("选课时间已截止,无法提交申请");
            }
            
            // 校验选课冲突(同一时间段是否已选其他课程)
            boolean hasConflict = selectionService.checkSelectionConflict(selectionDTO.getCourseId(), selectionDTO.getXuehao());
            if (hasConflict) {
                return ResponseEntity.badRequest().body("该课程与已选课程时间冲突,请重新选择");
            }
            
            // 提交选课申请
            Xueshengxuanke selection = selectionService.applySelection(selectionDTO, studentId);
            return ResponseEntity.ok("选课申请提交成功,申请ID:" + selection.getId());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("选课申请提交失败:" + e.getMessage());
        }
    }
    
    /**
     * 教师审核选课申请
     */
    @PostMapping("/audit")
    public ResponseEntity<?> auditSelection(@RequestBody SelectionAuditDTO auditDTO,
                                          @RequestHeader("teacherId") Long teacherId) {
        try {
            // 参数校验:申请ID、审核结果、审核回复为必填项
            if (auditDTO.getId() == null || auditDTO.getSfsh() == null || 
                StringUtils.isEmpty(auditDTO.getShhf())) {
                return ResponseEntity.badRequest().body("申请ID、审核结果、审核回复不能为空");
            }
            
            // 审核选课申请
            Xueshengxuanke selection = selectionService.auditSelection(auditDTO);
            return ResponseEntity.ok("选课申请审核完成,审核结果:" + auditDTO.getSfsh() + ",回复:" + selection.getShhf());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("选课申请审核失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class SelectionServiceImpl implements SelectionService {
    
    @Autowired
    private XueshengxuankeMapper selectionMapper;
    @Autowired
    private KechengMapper courseMapper;
    
    @Override
    public boolean checkSelectionTime(Long courseId) {
        // 查询课程信息
        Kecheng course = courseMapper.selectByPrimaryKey(courseId);
        if (course == null) {
            throw new RuntimeException("该课程不存在");
        }
        // 选课截止时间设为开课时间前7天(可配置)
        Calendar cal = Calendar.getInstance();
        cal.setTime(course.getKaikeshijian());
        cal.add(Calendar.DAY_OF_MONTH, -7);
        return new Date().before(cal.getTime());
    }
    
    @Override
    public boolean checkSelectionConflict(Long courseId, String xuehao) {
        // 1. 查询当前课程时间
        Kecheng currentCourse = courseMapper.selectByPrimaryKey(courseId);
        Date currentStart = currentCourse.getKaikeshijian();
        Date currentEnd = currentCourse.getJieshushijian();
        
        // 2. 查询学生已选课程(审核通过或待审核)
        XueshengxuankeExample example = new XueshengxuankeExample();
        example.createCriteria()
               .andXuehaoEqualTo(xuehao)
               .andShifouxuanzeEqualTo("是")
               .andSfshIn(Arrays.asList("通过", "待审核"));
        List<Xueshengxuanke> selectedCourses = selectionMapper.selectByExample(example);
        
        // 3. 校验时间冲突(当前课程时间与已选课程时间重叠)
        for (Xueshengxuanke selected : selectedCourses) {
            Kecheng selectedCourse = courseMapper.selectByPrimaryKey(selected.getCourseId());
            Date selectedStart = selectedCourse.getKaikeshijian();
            Date selectedEnd = selectedCourse.getJieshushijian();
            // 时间重叠逻辑:当前开始 < 已选结束 且 当前结束 > 已选开始
            if (currentStart.before(selectedEnd) && currentEnd.after(selectedStart)) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public Xueshengxuanke applySelection(SelectionDTO dto, Long studentId) {
        // 构建选课记录实体
        Xueshengxuanke selection = new Xueshengxuanke();
        selection.setKechengmingcheng(dto.getKechengmingcheng());
        selection.setShifouxuanze("是");
        selection.setXuanzeshijian(new Date());
        selection.setJiaoshizhanghao(dto.getJiaoshizhanghao());
        selection.setJiaoshixingming(dto.getJiaoshixingming());
        selection.setXuehao(dto.getXuehao());
        selection.setXingming(dto.getXingming());
        selection.setSfsh("待审核"); // 初始状态为待审核
        selection.setShhf("");
        selection.setAddtime(new Date());
        
        // 保存选课记录
        selectionMapper.insert(selection);
        return selection;
    }
    
    @Override
    public Xueshengxuanke auditSelection(SelectionAuditDTO dto) {
        // 查询选课记录
        Xueshengxuanke selection = selectionMapper.selectByPrimaryKey(dto.getId());
        if (selection == null) {
            throw new RuntimeException("该选课申请不存在");
        }
        
        // 更新审核状态与回复
        selection.setSfsh(dto.getSfsh());
        selection.setShhf(dto.getShhf());
        selectionMapper.updateByPrimaryKeySelective(selection);
        
        return selection;
    }
}

3.3.3 成绩管理功能实现(教师核心操作)

@RestController
@RequestMapping("/api/grade")
public class GradeController {
    
    @Autowired
    private GradeService gradeService;
    
    /**
     * 教师录入学生成绩
     */
    @PostMapping("/add")
    public ResponseEntity<?> addGrade(@RequestBody GradeDTO gradeDTO,
                                    @RequestHeader("teacherId") Long teacherId) {
        try {
            // 参数校验:课程名称、学号、成绩为必填项
            if (StringUtils.isEmpty(gradeDTO.getKechengmingcheng()) || 
                StringUtils.isEmpty(gradeDTO.getXuehao()) || 
                gradeDTO.getChengji() == null) {
                return ResponseEntity.badRequest().body("课程名称、学号、成绩不能为空");
            }
            
            // 校验成绩范围(0-100分)
            if (gradeDTO.getChengji() < 0 || gradeDTO.getChengji() > 100) {
                return ResponseEntity.badRequest().body("成绩需在0-100分范围内");
            }
            
            // 录入成绩(自动判断是否合格:60分及以上为合格)
            Xueshengchengji grade = gradeService.addGrade(gradeDTO);
            return ResponseEntity.ok("成绩录入成功,成绩ID:" + grade.getId() + ",是否合格:" + grade.getShifouhege());
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("成绩录入失败:" + e.getMessage());
        }
    }
    
    /**
     * 学生查询本人成绩
     */
    @GetMapping("/student/list")
    public ResponseEntity<?> getStudentGradeList(
            @RequestParam String xuehao,
            @RequestParam(required = false) String kechengmingcheng,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        try {
            PageResult<GradeVO> result = gradeService.getStudentGradeList(xuehao, kechengmingcheng, page, size);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("成绩查询失败:" + e.getMessage());
        }
    }
}

@Service
@Transactional
public class GradeServiceImpl implements GradeService {
    
    @Autowired
    private XueshengchengjiMapper gradeMapper;
    
    @Override
    public Xueshengchengji addGrade(GradeDTO dto) {
        // 1. 校验是否已录入该学生该课程成绩
        XueshengchengjiExample example = new XueshengchengjiExample();
        example.createCriteria()
               .andKechengmingchengEqualTo(dto.getKechengmingcheng())
               .andXuehaoEqualTo(dto.getXuehao());
        if (gradeMapper.countByExample(example) > 0) {
            throw new RuntimeException("该学生该课程成绩已录入,请勿重复提交");
        }
        
        // 2. 构建成绩实体(自动判断是否合格)
        Xueshengchengji grade = new Xueshengchengji();
        grade.setKechengmingcheng(dto.getKechengmingcheng());
        grade.setXuehao(dto.getXuehao());
        grade.setXingming(dto.getXingming());
        grade.setChengji(dto.getChengji());
        grade.setShifouhege(dto.getChengji() >= 60 ? "是" : "否");
        grade.setPingyu(dto.getPingyu());
        grade.setJiaoshizhanghao(dto.getJiaoshizhanghao());
        grade.setJiaoshixingming(dto.getJiaoshixingming());
        grade.setAddtime(new Date());
        
        // 3. 保存成绩信息
        gradeMapper.insert(grade);
        return grade;
    }
    
    @Override
    public PageResult<GradeVO> getStudentGradeList(String xuehao, String kechengmingcheng, int page, int size) {
        // 分页配置
        PageHelper.startPage(page, size);
        
        // 构建查询条件
        XueshengchengjiExample example = new XueshengchengjiExample();
        XueshengchengjiExample.Criteria criteria = example.createCriteria();
        criteria.andXuehaoEqualTo(xuehao);
        if (StringUtils.isNotEmpty(kechengmingcheng)) {
            criteria.andKechengmingchengLike("%" + kechengmingcheng + "%");
        }
        // 按课程名称正序排列
        example.setOrderByClause("kechengmingcheng ASC");
        
        // 执行查询
        List<Xueshengchengji> gradeList = gradeMapper.selectByExample(example);
        PageInfo<Xueshengchengji> pageInfo = new PageInfo<>(gradeList);
        
        // 转换为VO
        List<GradeVO> voList = gradeList.stream()
            .map(grade -> {
                GradeVO vo = new GradeVO();
                BeanUtils.copyProperties(grade, vo);
                return vo;
            }).collect(Collectors.toList());
        
        return new PageResult<>(voList, pageInfo.getTotal());
    }
}

3.4 第四步:前端界面实现——选课与管理一体化平台

基于JSP + Layui构建前端界面,贴合高校选课“简洁、高效、清晰”需求,按角色划分核心界面:

3.4.1 核心界面设计

  1. 登录界面:支持管理员/教师/学生三角色登录,输入账号密码后验证权限并跳转至对应首页,含“忘记密码”功能(学生/教师可重置,管理员需联系超级管理员);
  2. 管理员后台
    • 首页:展示系统核心数据(教师总数、学生总数、课程总数、待审核选课申请数);
    • 教师管理:表格展示教师列表,支持新增(填写姓名、职称、手机等信息)、编辑、删除;
    • 学生管理:表格展示学生列表,支持按专业筛选,编辑学生专业、联系方式等信息;
    • 专业管理:表单式新增专业,表格展示专业列表,支持编辑、删除;
    • 课程管理:表格展示课程信息,支持按教师/专业筛选,点击“编辑”修改课程学分、开课时间等;
  3. 教师后台
    • 首页:展示本人授课课程数、待审核选课申请数、待录入成绩课程数;
    • 课程查询:表格展示本人授课课程,支持查看课程详情(学生选课名单);
    • 选课审核:表格展示学生选课申请,支持“通过/拒绝”审核并填写回复;
    • 成绩管理:表格展示需录入成绩的学生名单,支持批量录入成绩、填写评语;
  4. 学生前台
    • 首页:顶部导航(首页、课程查询、我的选课、我的成绩),中部展示热门课程推荐;
    • 课程查询:左侧筛选栏(按专业/课程分类),右侧展示课程卡片(含封面、名称、学分、教师),点击“选课”提交申请;
    • 我的选课:表格展示本人选课记录,显示审核状态与回复,支持取消未审核的选课;
    • 我的成绩:表格展示已修课程成绩,含分数、是否合格、教师评语,支持按课程名称筛选。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3.4.2 设计亮点

  • 角色权限清晰:不同角色界面菜单严格区分,避免功能混淆(如学生看不到“教师管理”模块);
  • 选课效率优先:学生选课支持“多条件组合筛选”(如“计算机专业+必修课程”),结果实时刷新;
  • 成绩可视化:学生成绩页面用颜色标注合格状态(绿色“是”、红色“否”),直观展示学习情况;
  • 操作引导友好:关键操作(如成绩录入、选课审核)提供弹窗提示,降低误操作概率。

3.5 第五步:系统测试——确保选课系统稳定性

通过多维度测试验证系统功能完整性、性能稳定性和安全性,符合高校选课“高并发、高可靠”的要求:

3.5.1 功能测试

测试场景测试用例预期结果实际结果
学生提交选课申请选择“计算机网络”课程,提交申请申请成功,教师后台可见待审核记录申请成功,状态同步正常
教师审核选课申请选择学生选课申请,点击“通过”并填写“欢迎选课”审核状态更新为“通过”,学生前台可见回复审核成功,状态与回复同步
教师录入成绩为学生录入“计算机网络”成绩85分成绩录入成功,系统自动标记“合格”录入成功,合格状态判断准确
学生查询成绩学生登录后查询“计算机网络”成绩显示成绩85分、合格、教师评语查询成功,数据展示完整

3.5.2 性能测试

  • 并发测试:模拟50名学生同时选课、20名教师同时审核选课,系统响应时间<1.5秒,无数据丢失;
  • 数据加载测试:加载200条课程信息、500条学生成绩数据,表格分页流畅,筛选响应时间<1秒;
  • 稳定性测试:连续72小时运行系统,模拟选课高峰期操作(大量选课申请提交与审核),无崩溃或数据异常。

3.5.3 安全性测试

测试项测试方法预期结果实际结果
密码加密查看数据库xuesheng表mima字段密码以加密形式存储(如MD5)符合预期,加密存储
越权访问学生角色直接访问管理员“课程删除”接口跳转至登录页,提示“无权限”符合预期,拦截成功
数据校验教师录入成绩105分,提交提示“成绩需在0-100分范围内”符合预期,校验成功
选课冲突学生选择与已选课程时间重叠的课程提示“时间冲突,请重新选择”符合预期,冲突检测准确

3.6 第六步:问题排查与优化——提升选课系统体验

开发过程中遇到的核心问题及解决方案,确保系统符合高校选课实际需求:

  1. 问题:选课高峰期大量学生同时提交申请,导致数据库压力过大,响应变慢
    解决方案:优化数据库索引(为课程表“kaikeshijian”、选课表“xuehao”字段建立索引),增加Redis缓存热门课程信息,减少数据库查询次数;
  2. 问题:学生选课申请提交后,未及时收到审核结果通知
    解决方案:增加“消息通知”功能,教师审核后系统自动向学生推送弹窗通知,同时记录通知时间;
  3. 问题:管理员查询大量学生数据时,表格加载缓慢
    解决方案:实现表格“懒加载”功能,仅加载当前页数据,同时支持“导出Excel”批量查看,提升数据处理效率;
  4. 问题:教师录入成绩时,需手动输入每个学生信息,操作繁琐
    解决方案:开发“成绩批量导入”功能,支持教师下载Excel模板填写成绩后批量上传,自动匹配学生与课程信息,减少手动操作。

四、毕业设计复盘:学生选课系统开发实践总结

4.1 开发过程中的技术挑战

  1. 多角色权限控制:管理员、教师、学生三类角色的功能边界划分,需确保教师仅能操作本人课程、学生仅能查看本人数据;
  2. 选课冲突检测:需精准判断学生所选课程的时间重叠关系,避免逻辑漏洞导致选课冲突;
  3. 高并发处理:选课高峰期大量用户同时操作,需平衡系统响应速度与数据一致性;
  4. 数据关联设计:课程、选课、成绩的多表关联查询(如按课程查询学生成绩),需确保数据准确性与查询效率。

4.2 给后续开发者的建议

  1. 技术选型:优先选择成熟稳定的技术栈(如Spring Boot + MySQL),高校系统对稳定性的要求高于新技术尝鲜;
  2. 数据库设计:提前梳理业务数据关系(如学生-课程-成绩的关联),设计合理的表结构与索引,避免后期重构;
  3. 功能迭代:采用“核心功能优先”策略,先实现选课、成绩录入等核心功能,再迭代消息通知、数据统计等附加功能;
  4. 用户视角优化:从师生使用习惯出发,简化操作流程(如学生首页增加“选课时间倒计时”提醒),降低使用门槛;
  5. 文档完善:编写详细的接口文档与操作手册,方便高校教务人员后期维护(如新增课程、批量导入学生的操作步骤)。

五、项目资源与发展展望

5.1 项目核心资源

本项目提供完整的开发与部署资料,方便后续学习和二次开发,满足毕业设计与高校选课系统实际应用需求:

  • 后端源码:完整的Spring Boot项目,包含所有业务逻辑代码(Controller、Service、Mapper),注释详细;
  • 前端源码:JSP页面文件、Layui配置文件、JS脚本,可直接运行;
  • 数据库脚本:MySQL建表语句和示例数据(含测试管理员账号、教师账号、学生账号);
  • 部署指南:详细的环境配置(JDK 1.8、Tomcat 8.5、MySQL 8.0)和项目部署步骤;
  • 使用手册:管理员、教师、学生的操作指南,含界面截图和步骤描述,适配高校教务人员培训需求。

5.2 系统扩展方向

  1. 智能化升级:集成AI推荐功能,根据学生专业、已选课程推荐匹配度高的课程(如计算机专业学生推荐“人工智能”课程);
  2. 移动端适配:开发微信小程序,支持学生在手机上选课、查询成绩,教师实时接收选课审核提醒;
  3. 选课预警功能:当课程剩余名额较少(如少于5个)时,向关注该课程的学生推送“名额紧张”提醒;
  4. 数据统计分析:增加可视化报表(如各专业选课率、课程通过率、教师成绩分布),为高校教学管理提供数据支持;
  5. 在线答疑模块:新增课程答疑功能,学生可在课程页面提问,教师在线回复,增强师生互动。

如果本文对您的Spring Boot学习、学生选课类毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多企业级教育类项目实战案例!