SpringBoot+Vue高校在线考试系统 附带详细运行指导视频

文章目录

一、项目演示

项目演示地址: 视频地址

二、项目介绍

项目描述:这是一个基于SpringBoot+Vue框架 开发的高校在线考试系统。首先,这是一个前后端分离的项目,代码简洁规范,注释说明详细,易于理解和学习。其次,这项目功能丰富,具有一个高校在线考试系统该有的所有功能。

项目功能:此项目分为三个 角色:学生教师管理员

学生 有登录、查看我的课程信息、查看我的考试信息、考试答题、查看我的成绩信息、查看公告信息、修改个人信息等功能。

教师 有登录、查看数据统计信息、查看学期信息、查看我的课程信息、管理我的题库信息、管理我的课程考试信息、管理我的课程成绩信息、查看公告信息和修改个人信息等功能。

管理员有登录、查看数据统计信息、管理所有学生信息、管理所有教师信息、管理所有管理员信息、管理所有教学组织信息、管理所有学期信息、管理所有课程信息、管理所有题库信息、管理所有课程考试信息、管理所有课程成绩信息和管理所有公告信息等功能。

应用技术:SpringBoot + Vue3.0 + MySQL + MyBatis + Redis + ElementUI-Plus + Vite

运行环境:IntelliJ IDEA2019.3.5 + MySQL5.7 + Redis5.0.5 + JDK1.8 + Maven3.6.3 + Node14.16.1 + Visual Studio Code(以上工具在项目压缩包中都自带)

三、运行截图

四、主要代码

1.考试交卷代码

java 复制代码
    /**
     * 提交考试
     *
     * @param examDTO
     * @return
     */
    @Override
    public ResponseDTO<Boolean> submitExam(ExamDTO examDTO) {
        if (CommonUtil.isEmpty(examDTO.getId())) {
            return ResponseDTO.errorByMsg(CodeMsg.DATA_ERROR);
        }

        // 获取考试信息
        Exam exam = examMapper.selectByPrimaryKey(examDTO.getId());
        if (exam == null) {
            return ResponseDTO.errorByMsg(CodeMsg.EXAM_NOT_EXIST);
        }

        // 获取当前登录用户(学生)
        String userId = examDTO.getUserId();
        if (CommonUtil.isEmpty(userId)) {
            return ResponseDTO.errorByMsg(CodeMsg.DATA_ERROR);
        }

        User user = userMapper.selectByPrimaryKey(userId);
        if (user == null) {
            return ResponseDTO.errorByMsg(CodeMsg.USER_NOT_EXIST);
        }

        // 获取考试题目
        QuestionExample questionExample = new QuestionExample();
        questionExample.createCriteria().andQuestionBankIdEqualTo(exam.getQuestionBankId());
        List<Question> questionList = questionMapper.selectByExample(questionExample);

        // 获取学生答案
        Map<String, String> answers = examDTO.getAnswers();
        if (answers == null) {
            answers = java.util.Collections.emptyMap();
        }

        // 计算成绩
        int totalScore = 0;
        for (Question question : questionList) {
            String userAnswer = answers.get(question.getId());
            String correctAnswer = question.getAnswer();

            if (!CommonUtil.isEmpty(userAnswer) && !CommonUtil.isEmpty(correctAnswer)) {
                // 多选题需要比较每个选项是否都正确
                if (question.getType() == 2) {
                    // 多选题:将答案按逗号分隔并排序后比较
                    String[] userAnswersArray = userAnswer.split(",");
                    String[] correctAnswersArray = correctAnswer.split(",");
                    java.util.Arrays.sort(userAnswersArray);
                    java.util.Arrays.sort(correctAnswersArray);
                    if (java.util.Arrays.equals(userAnswersArray, correctAnswersArray)) {
                        totalScore += question.getScore();
                    }
                } else {
                    // 单选题、判断题:直接比较答案
                    if (userAnswer.equals(correctAnswer)) {
                        totalScore += question.getScore();
                    }
                }
            }
        }

        // 计算用时(分钟)
        Integer timeUsed = examDTO.getTimeUsed();
        if (timeUsed == null) {
            timeUsed = 0;
        }

        // 计算获得学分(60分以上获得学分)
        int earnedScore = totalScore >= 60 ? 3 : 0;

        // 创建成绩记录
        Score score = new Score();
        score.setId(UuidUtil.getShortUuid());
        score.setUserId(userId);
        score.setCourseId(exam.getCourseId());
        score.setExamId(exam.getId());
        score.setScore(totalScore);
        score.setTimeUsed(timeUsed);
        score.setRank(0); // 排名稍后统一计算
        score.setEarnedScore(earnedScore);

        // 保存成绩
        int result = scoreMapper.insert(score);
        if (result <= 0) {
            return ResponseDTO.errorByMsg(CodeMsg.SCORE_SAVE_ERROR);
        }

        // 提交考试后,重新计算该考试所有学生的排名
        scoreService.updateRankByExamId(exam.getId());

        return ResponseDTO.successByMsg(true, "提交成功!您的成绩是:" + totalScore + "分");
    }

2.保存题库信息代码

java 复制代码
	/**
     * 保存题库信息
     *
     * @param questionBankDTO
     * @return
     */
    @Override
    public ResponseDTO<Boolean> saveQuestionBank(QuestionBankDTO questionBankDTO) {
        CodeMsg validate = ValidateEntityUtil.validate(questionBankDTO);
        if (!validate.getCode().equals(CodeMsg.SUCCESS.getCode())) {
            return ResponseDTO.errorByMsg(validate);
        }
        QuestionBank questionBank = CopyUtil.copy(questionBankDTO, QuestionBank.class);
        if (CommonUtil.isEmpty(questionBank.getId())) {
            questionBank.setId(UuidUtil.getShortUuid());
            if (questionBankMapper.insertSelective(questionBank) == 0) {
                return ResponseDTO.errorByMsg(CodeMsg.QUESTION_BANK_ADD_ERROR);
            }
        } else {
            if (questionBankMapper.updateByPrimaryKeySelective(questionBank) == 0) {
                return ResponseDTO.errorByMsg(CodeMsg.QUESTION_BANK_EDIT_ERROR);
            }
        }
        return ResponseDTO.successByMsg(true, "保存成功!");
    }

3.用户登录代码

java 复制代码
    /**
     * 用户登录操作
     *
     * @param userDTO
     * @return
     */
    @Override
    public ResponseDTO<UserDTO> login(UserDTO userDTO) {
        // 进行是否为空判断
        if (CommonUtil.isEmpty(userDTO.getNo())) {
            return ResponseDTO.errorByMsg(CodeMsg.NO_EMPTY);
        }
        if (CommonUtil.isEmpty(userDTO.getPassword())) {
            return ResponseDTO.errorByMsg(CodeMsg.PASSWORD_EMPTY);
        }
        // 对比学号/学工号和密码是否正确
        UserExample userExample = new UserExample();
        userExample.createCriteria().andNoEqualTo(userDTO.getNo()).andPasswordEqualTo(userDTO.getPassword())
                .andRoleIdEqualTo(userDTO.getRoleId());
        List<User> userList = userMapper.selectByExample(userExample);
        if (userList == null || userList.size() != 1) {
            return ResponseDTO.errorByMsg(CodeMsg.NO_PASSWORD_ERROR);
        }
        // 生成登录token并存入Redis中
        UserDTO selectedUserDto = CopyUtil.copy(userList.get(0), UserDTO.class);
        String token = UuidUtil.getShortUuid();
        selectedUserDto.setToken(token);
        // 把token存入redis中 有效期1小时
        stringRedisTemplate.opsForValue().set("USER_" + token, JSON.toJSONString(selectedUserDto), 3600,
                TimeUnit.SECONDS);
        return ResponseDTO.successByMsg(selectedUserDto, "登录成功!");
    }
相关推荐
格子软件1 小时前
2026年GEO贴牌代理:分布式多级分账状态机源码深度解构
java·vue.js·分布式·vue·geo
程序员 Harry7 小时前
AriesMusic Free Music
vue
格子软件10 小时前
2026年分布式GEO代理流量调度:源码级状态机防重挂实战
java·vue.js·人工智能·spring boot·分布式·vue
万亿少女的梦16810 小时前
基于Spring Boot的天空影院电影网站系统设计与实现
java·spring boot·mysql·vue·系统设计
万亿少女的梦16810 小时前
基于Spring Boot的社区管理系统设计与实现
java·spring boot·mysql·vue·系统设计
万亿少女的梦16811 小时前
基于Spring Boot的楚雄旅游景区门票售卖系统设计与实现
java·spring boot·mysql·vue·系统设计
折哥的程序人生 · 物流技术专研12 小时前
Java面试通关⑨:SpringBoot核心全集
自动配置·springboot·starter·校招·java面试·面试真题·内嵌容器
格子软件13 小时前
2026年分布式GEO代理架构:多租户动态数据源隔离与流控源码解构
java·vue.js·人工智能·分布式·架构·vue·geo
格子软件1 天前
2026年GEO优化系统源码的分布式状态机深度拆解
java·前端·vue.js·vue·geo
ShiXZ2131 天前
PDF-OCR文件识别篇(八):配置、运维与排错
java·运维·ocr·dubbo·springboot