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

作者简介:全栈开发工程,从事Java、Python、前端、小程序方面的开发和研究,对大数据应用与开发比较感兴趣,

主要内容:Java项目、前端项目、Python项目、小程序开发、大数据项目、单片机

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

文末获取源码 

感谢您的关注,请收藏以免忘记,点赞以示鼓励,评论给以建议,爱你哟

项目编号:

一,项目简介

随着计算机技术的不断发展,我们的日常生活和工作都与计算机技术的关系越来越密切。计算机技术的发展改变了我们日常的生活和工作习惯,也改变了社会的发展速度,使得我们的生活更加便利和高效。伴随着计算机技术发展起来的互联网技术将我们的生活带领进信息化时代,改变了我们的学习和工作环境,例如我们经常面对的考试也随着互联网技术的发展产生了改变,伴随着信息技术的发展,在线无纸化的考试系统应运而生,不仅彻底改变了传统纸质考试的习惯和环境,更是提高了考试效率,保证了考试效果,达到了考试目的[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做为后端支持。代码结构规整,源码容易阅读,功能完善,非常适合做为毕设来使用。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_469603589

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值