基于SpringBoot+VUE(PC端+小程序端)的智能在线考试系统毕业设计

Posted 编程指南针

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于SpringBoot+VUE(PC端+小程序端)的智能在线考试系统毕业设计相关的知识,希望对你有一定的参考价值。

作者主页:编程指南针

作者简介:Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师

主要内容:Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助

收藏点赞不迷路  关注作者有好处

文末获取源码 

项目编号:

一,项目简介

随着计算机技术的不断发展,我们的日常生活和工作都与计算机技术的关系越来越密切。计算机技术的发展改变了我们日常的生活和工作习惯,也改变了社会的发展速度,使得我们的生活更加便利和高效。伴随着计算机技术发展起来的互联网技术将我们的生活带领进信息化时代,改变了我们的学习和工作环境,例如我们经常面对的考试也随着互联网技术的发展产生了改变,伴随着信息技术的发展,在线无纸化的考试系统应运而生,不仅彻底改变了传统纸质考试的习惯和环境,更是提高了考试效率,保证了考试效果,达到了考试目的[1]。传统的纸质考试具有很多局限性和不足,主要包括以下几点:

1.传统纸质考试需要较多的人力资源和时间资源进行题目的设定,同时题目的难易程度和考核价值水平很难达到基本的要求;

2.传统纸质考试的阅卷采用人工的方式,人工阅卷难免会出现阅卷差错或者分数合算差错,这也会对考试的效果造成影响;

3.传统纸质考试的人工阅卷模式也会浪费大量的人力资源和时间资源,不能保证工作效率和工作质量;

4.传统纸质考试对考试的总结能力较差,不能够全面具体的分析考试结果,教师也很难得到基本的考试结果分析的数据信息,而这些数据信息是提高教学质量和教学效果的关键因素;

5.传统纸质考试对考试时间以及考试纪律的要求不能达到统一,这也会影响到考试的公平性。根据以上分析的传统纸质考试的不足之处,新型的结合计算机技术以及互联网技术的在线考试系统应运而生,不仅通过一种新的技术解决了传统纸质考试的基本问题,还提供了一种新的考试思路和考试理念,纠正了传统纸质考试的弊端,提供更加合理有效的考试过程。

二,环境介绍

三,技术说明

后端系统前端系统微信小程序
spring-boot 2.1.6.RELEASEvue 采用新版,使用了vue-cli4搭建的系统iView 主题样式
spring-boot-security 用户登录验证element-ui 最流行的vue UI框架
undertow web容器vue-element-admin 模版
mybatis/mybatis_plusecharts 图表统计
hikari 速度最快的数据库连接池ueditor 题目编辑器

四,功能列表

4.1 学生系统功能

模块介绍
登录用户名、密码
注册年级、用户名、密码
任务中心管理员发布的年级任务,每个学生只能做一次
考试题干支持文本、图片、数学公式、表格等,学生答题支持:文本
固定试卷可重复练习、自行批改的试卷
时段试卷在时间限制内,可重复练习、自行批改的试卷
考试记录查看答卷记录和试卷信息
错题本答错题目会自动进入错题本,显示题目基本信息
个人信息显示学生个人资料
更新信息修改个人资料、头像
个人动态显示用户最近的个人动态
消息中心用于接收管理员发送的消息

4.2 管理系统功能

模块介绍
登录用户名、密码
主页试卷总数、题目总数、用户活跃度、题目月数量
学生列表显示系统所有的学生,新增、修改、删除、禁用
管理员列表显示系统所有的管理员,新增、修改、删除、禁用
学科列表学科查询、修改、删除
学科创编创建学科
试卷列表试卷查询、修改、删除
试卷创编创建的试卷为时段试卷、固定试卷、任务试卷
题目列表题目查询、修改、删除
题目创建题目支持单选题、多选题、判断题、填空题、简答题,题干支持文本、图片、表格、数学公式
任务列表任务查询、修改、删除
消息列表显示已发送的消息,消息已读人数等信息
消息发送发送消息给多个用户
用户日志显示所有用户日志
个人资料显示管理员用户名、真实姓名
时间线显示管理员创建时间
修改资料修改姓名、手机号

4.3 小程序功能

模块介绍
登录用户登录登出功能,登录会自动绑定微信账号,登出会解绑
注册年级、用户名、密码
任务中心管理员发布的年级任务,每个学生只能做一次
考试题干支持文本、图片、数学公式、表格等,学生答题支持:文本
固定试卷可重复练习、自行批改的试卷
时段试卷在时间限制内,可重复练习、自行批改的试卷
考试记录查看答卷记录和试卷信息
错题本答错题目会自动进入错题本,显示题目基本信息
个人信息显示学生个人资料
更新信息修改个人资料、头像
个人动态显示用户最近的个人动态
消息中心用于接收管理员发送的消息

五,数据库设计

仅展示部分数据库字段设计

5.1 试卷表

字段名类型注释
idint
namevarchar试卷名称
subject_idint学科
paper_typeint试卷类型( 1.固定试卷 4.时段试卷 6.任务试卷 )
grade_levelint年级
scoreint试卷总分(千分制)
question_countint题目数量
suggest_timeint建议时长(分钟)
limit_start_timedatetime时段试卷 开始时间
limit_end_timedatetime时段试卷 结束时间
frame_text_content_idint试卷框架 内容为JSON
create_userint
create_timedatetime
deletedbit
task_exam_idint

5.2 试卷答案表

字段名类型注释
idint
exam_paper_idint
paper_namevarchar试卷名称
paper_typeint试卷类型( 1.固定试卷 4.时段试卷 6.任务试卷 )
subject_idint学科
system_scoreint系统判定得分
user_scoreint最终得分(千分制)
paper_scoreint试卷总分
question_correctint做对题目数量
question_countint题目总数量
do_timeint做题时间(秒)
statusint试卷状态(1待判分 2完成)
create_userint学生
create_timedatetime提交时间
task_exam_idint

5.3 题目表

字段名类型注释
idint
question_typeint1.单选题 2.多选题 3.判断题 4.填空题 5.简答题
subject_idint学科
scoreint题目总分(千分制)
grade_levelint级别
difficultint题目难度
correcttext正确答案
info_text_content_idint题目 填空、 题干、解析、答案等信息
create_userint创建人
statusint1.正常
create_timedatetime创建时间
deletedbit

5.4 学科表

字段名类型注释
idint
namevarchar语文 数学 英语 等
levelint年级 (1-12) 小学 初中 高中 大学
level_namevarchar一年级、二年级等
item_orderint排序
deletedbit

5.5 用户表

字段名类型注释
idint
user_uuidvarchar
user_namevarchar用户名
passwordvarchar
real_namevarchar真实姓名
ageint
sexint1.男 2女
birth_daydatetime
user_levelint学生年级(1-12)
phonevarchar
roleint1.学生 3.管理员
statusint1.启用 2禁用
image_pathvarchar头像地址
create_timedatetime
modify_timedatetime
last_active_timedatetime
deletedbit是否删除
wx_open_idvarchar微信openId

5.6 用户日志表

字段名类型注释
idint
user_idint用户id
user_namevarchar用户名
real_namevarchar真实姓名
contenttext内容
create_timedatetime时间

其他表的设计省略............

六,系统展示

6.1 后台管理

主页

用户管理 

试卷管理

题目管理 

添加题目

添加试卷 

 

任务管理

教育管理 

成绩管理

 6.2 学生端

首页登录与注册

学生端首页 

试卷中心 

考试记录 

错题本

 个人中心

6.3 小程序端

登录与注册

 

首页 

试卷考试 

考试记录 

我的 

七,核心代码展示



@Service
public class TaskExamServiceImpl extends BaseServiceImpl<TaskExam> implements TaskExamService 

    protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();
    private final TaskExamMapper taskExamMapper;
    private final TextContentService textContentService;
    private final ExamPaperMapper examPaperMapper;

    @Autowired
    public TaskExamServiceImpl(TaskExamMapper taskExamMapper, TextContentService textContentService, ExamPaperMapper examPaperMapper) 
        super(taskExamMapper);
        this.taskExamMapper = taskExamMapper;
        this.textContentService = textContentService;
        this.examPaperMapper = examPaperMapper;
    

    @Override
    public PageInfo<TaskExam> page(TaskPageRequestVM requestVM) 
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                taskExamMapper.page(requestVM)
        );
    

    @Override
    @Transactional
    public void edit(TaskRequestVM model, User user) 
        ActionEnum actionEnum = (model.getId() == null) ? ActionEnum.ADD : ActionEnum.UPDATE;
        TaskExam taskExam = null;
        if (actionEnum == ActionEnum.ADD) 
            Date now = new Date();
            taskExam = modelMapper.map(model, TaskExam.class);
            taskExam.setCreateUser(user.getId());
            taskExam.setCreateUserName(user.getUserName());
            taskExam.setCreateTime(now);
            taskExam.setDeleted(false);

            //保存任务结构
            TextContent textContent = textContentService.jsonConvertInsert(model.getPaperItems(), now, p -> 
                TaskItemObject taskItemObject = new TaskItemObject();
                taskItemObject.setExamPaperId(p.getId());
                taskItemObject.setExamPaperName(p.getName());
                return taskItemObject;
            );
            textContentService.insertByFilter(textContent);
            taskExam.setFrameTextContentId(textContent.getId());
            taskExamMapper.insertSelective(taskExam);

         else 
            taskExam = taskExamMapper.selectByPrimaryKey(model.getId());
            modelMapper.map(model, taskExam);

            TextContent textContent = textContentService.selectById(taskExam.getFrameTextContentId());
            //清空试卷任务的试卷Id,后面会统一设置
            List<Integer> paperIds = JsonUtil.toJsonListObject(textContent.getContent(), TaskItemObject.class)
                    .stream()
                    .map(d -> d.getExamPaperId())
                    .collect(Collectors.toList());
            examPaperMapper.clearTaskPaper(paperIds);

            //更新任务结构
            textContentService.jsonConvertUpdate(textContent, model.getPaperItems(), p -> 
                TaskItemObject taskItemObject = new TaskItemObject();
                taskItemObject.setExamPaperId(p.getId());
                taskItemObject.setExamPaperName(p.getName());
                return taskItemObject;
            );
            textContentService.updateByIdFilter(textContent);
            taskExamMapper.updateByPrimaryKeySelective(taskExam);
        

        //更新试卷的taskId
        List<Integer> paperIds = model.getPaperItems().stream().map(d -> d.getId()).collect(Collectors.toList());
        examPaperMapper.updateTaskPaper(taskExam.getId(), paperIds);
        model.setId(taskExam.getId());
    

    @Override
    public TaskRequestVM taskExamToVM(Integer id) 
        TaskExam taskExam = taskExamMapper.selectByPrimaryKey(id);
        TaskRequestVM vm = modelMapper.map(taskExam, TaskRequestVM.class);
        TextContent textContent = textContentService.selectById(taskExam.getFrameTextContentId());
        List<ExamResponseVM> examResponseVMS = JsonUtil.toJsonListObject(textContent.getContent(), TaskItemObject.class).stream().map(tk -> 
            ExamPaper examPaper = examPaperMapper.selectByPrimaryKey(tk.getExamPaperId());
            ExamResponseVM examResponseVM = modelMapper.map(examPaper, ExamResponseVM.class);
            examResponseVM.setCreateTime(DateTimeUtil.dateFormat(examPaper.getCreateTime()));
            return examResponseVM;
        ).collect(Collectors.toList());
        vm.setPaperItems(examResponseVMS);
        return vm;
    

    @Override
    public List<TaskExam> getByGradeLevel(Integer gradeLevel) 
        return taskExamMapper.getByGradeLevel(gradeLevel);
    




@Service
public class AuthenticationServiceImpl implements AuthenticationService 


    private final UserService userService;
    private final SystemConfig systemConfig;

    @Autowired
    public AuthenticationServiceImpl(UserService userService, SystemConfig systemConfig) 
        this.userService = userService;
        this.systemConfig = systemConfig;
    


    /**
     * @param username username
     * @param password password
     * @return boolean
     */
    @Override
    public boolean authUser(String username, String password) 
        User user = userService.getUserByUserName(username);
        return authUser(user, username, password);
    


    @Override
    public boolean authUser(User user, String username, String password) 
        if (user == null) 
            return false;
        
        String encodePwd = user.getPassword();
        if (null == encodePwd || encodePwd.length() == 0) 
            return false;
        
        String pwd = pwdDecode(encodePwd);
        return pwd.equals(password);
    

    @Override
    public String pwdEncode(String password) 
        return RsaUtil.rsaEncode(systemConfig.getPwdKey().getPublicKey(), password);
    

    @Override
    public String pwdDecode(String encodePwd) 
        return RsaUtil.rsaDecode(systemConfig.getPwdKey().getPrivateKey(), encodePwd);
    



<view class="exam-page">
  <view class="view-wrap">
    <view class="exam-count-down">remainTimeStr</view>
  </view>
  <view class="view-wrap-hidden">
  </view>

  <view>
    <view class="exam-name-title">
      <h1>form.name</h1>
    </view>
    <form bindsubmit='formSubmit'>
      <i-panel title="titleItem.name" wx:for="form.titleItems" wx:for-item="titleItem" wx:key="titleItem.name" i-class="exam-panel-title">
        <i-cell-group i-class="exam-cell">
          <i-cell wx:for="titleItem.questionItems" wx:key="titleItem.id" wx:for-item="questionItem">

            <view wx:if="questionItem.questionType === 1">
              <rich-text nodes="questionItem.itemOrder. questionItem.title" />
              <radio-group class="radio-group" name="questionItem.itemOrder_questionItem.id_questionItem.questionType">
                <label class="radio" wx:for="questionItem.items" wx:key="questionItem.prefix" wx:for-item="radioItem" class="exam-radio-item-label">
                  <radio color="#2d8cf0" value="radioItem.prefix" checked="radioItem.checked" class="exam-item-left" />
                  <rich-text nodes="radioItem.prefix. radioItem.content" class="exam-item-left" />
                </label>
              </radio-group>
            </view>
            <view wx:elif="questionItem.questionType === 2">
              <rich-text nodes="questionItem.itemOrder. questionItem.title" class="exam-item-left" style="line-height:35px" />
              <checkbox-group class="exam-item-left" style="margin-left:10px" name="questionItem.itemOrder_questionItem.id_questionItem.questionType">
                <label wx:for="questionItem.items" wx:key="questionItem.prefix" wx:for-item="radioItem" class="exam-radio-item-label">
                  <checkbox color="#2d8cf0" value="radioItem.prefix" checked="radioItem.checked" class="exam-item-left" />
                  <rich-text nodes="radioItem.prefix. radioItem.content" class="exam-item-left" />
                </label>
              </checkbox-group>
            </view>
            <view wx:elif="questionItem.questionType === 3">
              <rich-text nodes="questionItem.itemOrder. questionItem.title" class="exam-item-left" style="line-height:35px" />
              <radio-group class="radio-group" class="exam-item-left" style="margin-left:10px" name="questionItem.itemOrder_questionItem.id_questionItem.questionType">
                <label class="radio" wx:for="questionItem.items" wx:key="questionItem.prefix" wx:for-item="radioItem" class="exam-radio-item-label">
                  <radio color="#2d8cf0" value="radioItem.prefix" checked="radioItem.checked" class="exam-item-left" />
                  <rich-text nodes="radioItem.content" class="exam-item-left" />
                </label>
              </radio-group>
            </view>
            <view wx:elif="questionItem.questionType === 4">
              <rich-text nodes="questionItem.itemOrder. questionItem.title" />
              <view class="exam-input-contain" wx:for="questionItem.items" wx:key="questionItem.prefix" wx:for-item="inputItem" wx:for-index="idx">
                <view class="exam-input-contain-label">inputItem.prefix</view>
                <input class="exam-input-contain-content" maxlength="-1" name="questionItem.itemOrder_questionItem.id_questionItem.questionType_idx" />
              </view>
            </view>
            <view wx:else>
              <rich-text nodes="questionItem.itemOrder. questionItem.title" />
              <view class="exam-textarea-contain">
                <textarea placeholder="答案" maxlength="-1" name="questionItem.itemOrder_questionItem.id_questionItem.questionType"></textarea>
              </view>
            </view>
          </i-cell>
        </i-cell-group>
      </i-panel>

      <view>
        <button class="i-btn  i-btn-primary i-btn-square" form-type='submit'>提交</button>
      </view>

      <i-action-sheet visible="true" visible="timeOutShow" mask-closable=" false ">
        <view slot="header" style="padding: 16px">
          <view class="exam-timeout-title">考试试卷结束,请提交试卷!</view>
          <button class="i-btn  i-btn-primary i-btn-square" form-type='submit'>提交</button>
        </view>
      </i-action-sheet>
    </form>

    <i-modal title="考试结果" visible="modalShow" bind:ok="returnRecord" bind:cancel="returnRecord">
      <view>得分:result</view>
    </i-modal>

    <i-spin size="large" fix wx:if=" spinShow "></i-spin>
    <i-message id="message" />
  </view>
</view>

八,项目部署

8.1 目录结构

获取到源码进行解压后,文件列表如下:

 8.2 导入数据库

打开Navicat(或者其他数据库连接工具也可),导入资料中的sql文件。

8.3 后端源码部署

8.3.1 导入源码

打开idea,新建工程【可以任意目录】。把解压后的xzs目录复制到创建好的idea工程中。进行导入。

导入进来之后,idea会进行编译。编译之后没有出现错误说明导入成功。

8.3.2 修改配置文件

修改数据库连接配置,在application-dev.yml配置文件中修改成自己的数据库名和自己的密码。

8.3.3 启动项目 

SpringBoot的程序启动类,相信大部分小伙伴们都知道该怎么启动。不过在这里还是要写一下,以防个别小伙伴不知道。在src下找到XzsApplication启动类,在该类中进行右键运行即可。最后查看控制台有没有报错信息。没有报错信息,启动成功。

 上面说明启动成功

8.4 管理员端和学生端部署

管理员端和学生端部署操作都是一样的,在这里以管理员端为例进行演示。

打开vscode【用其他前端开发工具打开也可】,导入资料中的vue目录下的xzs-admin工程,学生端是xzs-student。打开vscode的终端,进行安装依赖和启动项目。

8.4.1 安装依赖

命令:npm install

 

8.4.2 启动项目

命令:npm run serve

 

 运行成功,端口号8002

8.5 小程序端部署

8.5.1 微信小程序开发工具下载与安装

开发工具的官方下载地址为:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

不支持Windows XP和Windows 7系统,建议使用WIN10。我这里选择Windows 64版本的安装包。

安装过程比较简单,不用设置什么,按照提示来就行了。下面是每一步详细截图

 

 

 8.5.2 项目部署

 打开微信开发工具---> 选择导入--->找到资料中的wx目录,导入xzs_student

 

 到这一步会提示,输入appId,如果自己有就写自己的,没有的话,点击测试,使用测试号

 导入进来之后,会自动提示是否运行,选择【信任并运行】

运行成功 。到此整个项目部署就已经完成了

九,项目总结

该项目是PC端+小程序端。Java做为后端支持。代码结构规整,源码容易阅读,功能完善,非常适合做为毕设来使用。

以上是关于基于SpringBoot+VUE(PC端+小程序端)的智能在线考试系统毕业设计的主要内容,如果未能解决你的问题,请参考以下文章

springboot水果商城开发设计与实现.rar(项目源码)兼容小程序端+pc端

毕业设计So Easy:SpringBoot+Vue图书阅读和管理系统(前端+后端+小程序端)

java毕设项目开源了,springboot+vue的学生考试系统

java毕设项目开源了,springboot+vue的学生考试系统

开源即时通讯IM框架MobileIMSDK的微信小程序端技术概览

uni-app 项目小程序端支持 vue3 介绍