微信小程序|考试系统|基于微信小程序和SpringBoot+VUE的智能在线考试系统毕业设计

简介: 微信小程序|考试系统|基于微信小程序和SpringBoot+VUE的智能在线考试系统毕业设计

项目编号:BS-GX-067

一,项目简介

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

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

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

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

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

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

二,环境介绍

三,技术说明

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

四,功能列表

4.1 学生系统功能

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

4.2 管理系统功能

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

4.3 小程序功能

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

五,数据库设计

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

5.1 试卷表

字段名 类型 注释
id int
name varchar 试卷名称
subject_id int 学科
paper_type int 试卷类型( 1.固定试卷 4.时段试卷 6.任务试卷 )
grade_level int 年级
score int 试卷总分(千分制)
question_count int 题目数量
suggest_time int 建议时长(分钟)
limit_start_time datetime 时段试卷 开始时间
limit_end_time datetime 时段试卷 结束时间
frame_text_content_id int 试卷框架 内容为JSON
create_user int
create_time datetime
deleted bit
task_exam_id int

5.2 试卷答案表

字段名 类型 注释
id int
exam_paper_id int
paper_name varchar 试卷名称
paper_type int 试卷类型( 1.固定试卷 4.时段试卷 6.任务试卷 )
subject_id int 学科
system_score int 系统判定得分
user_score int 最终得分(千分制)
paper_score int 试卷总分
question_correct int 做对题目数量
question_count int 题目总数量
do_time int 做题时间(秒)
status int 试卷状态(1待判分 2完成)
create_user int 学生
create_time datetime 提交时间
task_exam_id int

5.3 题目表

字段名 类型 注释
id int
question_type int 1.单选题 2.多选题 3.判断题 4.填空题 5.简答题
subject_id int 学科
score int 题目总分(千分制)
grade_level int 级别
difficult int 题目难度
correct text 正确答案
info_text_content_id int 题目 填空、 题干、解析、答案等信息
create_user int 创建人
status int 1.正常
create_time datetime 创建时间
deleted bit

5.4 学科表

字段名 类型 注释
id int
name varchar 语文 数学 英语 等
level int 年级 (1-12) 小学 初中 高中 大学
level_name varchar 一年级、二年级等
item_order int 排序
deleted bit

5.5 用户表

字段名 类型 注释
id int
user_uuid varchar
user_name varchar 用户名
password varchar
real_name varchar 真实姓名
age int
sex int 1.男 2女
birth_day datetime
user_level int 学生年级(1-12)
phone varchar
role int 1.学生 3.管理员
status int 1.启用 2禁用
image_path varchar 头像地址
create_time datetime
modify_time datetime
last_active_time datetime
deleted bit 是否删除
wx_open_id varchar 微信openId

5.6 用户日志表

字段名 类型 注释
id int
user_id int 用户id
user_name varchar 用户名
real_name varchar 真实姓名
content text 内容
create_time datetime 时间

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

六,系统展示

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月前
|
监控 安全 JavaScript
2025基于springboot的校车预定全流程管理系统
针对传统校车管理效率低、信息不透明等问题,本研究设计并实现了一套校车预定全流程管理系统。系统采用Spring Boot、Java、Vue和MySQL等技术,实现校车信息管理、在线预定、实时监控等功能,提升学校管理效率,保障学生出行安全,推动教育信息化发展。
|
6月前
|
JavaScript Java 关系型数据库
基于springboot的高校运动会系统
本系统基于Spring Boot、Vue与MySQL,实现高校运动会报名、赛程安排及成绩管理的全流程信息化,提升组织效率,杜绝信息错漏与冒名顶替,推动体育赛事智能化发展。
|
6月前
|
JavaScript 安全 Java
基于springboot的大学生兼职系统
本课题针对大学生兼职信息不对称、权益难保障等问题,研究基于Spring Boot、Vue、MySQL等技术的兼职系统,旨在构建安全、高效、功能完善的平台,提升大学生就业竞争力与兼职质量。
|
6月前
|
JavaScript Java 关系型数据库
基于springboot的美食城服务管理系统
本系统基于Spring Boot、Java、Vue和MySQL技术,构建集消费者服务、商家管理与后台监管于一体的美食城综合管理平台,提升运营效率与用户体验。
|
6月前
|
JavaScript Java 关系型数据库
基于springboot的摄影师分享交流社区系统
本系统基于Spring Boot与Vue构建摄影师分享交流平台,旨在打造专业社区,支持作品展示、技术交流与合作互动。采用Java、MySQL等成熟技术,提升摄影爱好者创作水平,推动行业发展。
|
6月前
|
Java 关系型数据库 MySQL
基于springboot的网咖网吧管理系统
本文探讨了基于Java、MySQL和SpringBoot的网吧管理系统的设计与实现。随着信息化发展,传统管理方式难以满足需求,而该系统通过先进技术提升管理效率、保障数据安全、降低运营成本,具有重要意义。
|
6月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。
|
6月前
|
搜索推荐 JavaScript Java
基于springboot的儿童家长教育能力提升学习系统
本系统聚焦儿童家长教育能力提升,针对家庭教育中理念混乱、时间不足、个性化服务缺失等问题,构建科学、系统、个性化的在线学习平台。融合Spring Boot、Vue等先进技术,整合优质教育资源,提供高效便捷的学习路径,助力家长掌握科学育儿方法,促进儿童全面健康发展,推动家庭和谐与社会进步。
|
6月前
|
JavaScript Java 关系型数据库
基于springboot的古树名木保护管理系统
本研究针对古树保护面临的严峻挑战,构建基于Java、Vue、MySQL与Spring Boot技术的信息化管理系统,实现古树资源的动态监测、数据管理与科学保护,推动生态、文化与经济可持续发展。

热门文章

最新文章

下一篇
开通oss服务