美文网首页前端社团程序员让前端飞
在线考试系统之模块分析

在线考试系统之模块分析

作者: 淡就加点盐 | 来源:发表于2016-11-23 23:15 被阅读725次

    工具:Node.js + MongoDB + Socket.IO

    完成进度
    教师端:
    1. 学生的添加删除等操作
    2. 考题和分数的添加删除编辑修改等操作
    3. 在查看考试情况页面显示所有考生的姓名和学号,以及状态信息(红色:已登录;灰色:未登录;蓝色:考试中;绿色:已交卷)
    4. 点击已提交的考生对象,进入该考生的阅卷界面,显示该考生提交考卷和答案的信息,并且可以批阅该考卷的得分;
    5. 实时显示考生的状态
    学生端:
    1. 考生登入系统后,若时间未到,显示倒计时,点击题号弹出警告框;若时间到,可进行答题
    2. 点击题号后进入答题状态,同时教师端会实时显示考生的状态;
    3. 点击其他题号答题自动保存;
    4. 考试时间到自动提交等;
    预览
    教师端实时显示考生状态 教师端界面 学生端界面

    1. 数据结构定义

    1.用户表

    用户学号userId,姓名username, 密码password, 类型category(学生/老师), 状态status(初始,登录,答题,提交)

    var userSchema = new Schema({
        userId: String,
        username: String,
        password: String,
        category: String,   //分类-学生
        status: String, //状态
        meta: {
            updateAt: {type:Date, default: Date.now()},
            createAt: {type:Date, default: Date.now()}
        }
    });
    
    2.答题表

    题目内容content,分数score

    var questionSchema = new Schema({
        content: String,
        score: Number,
        meta: {
            updateAt: {type:Date, default: Date.now()},
            createAt: {type:Date, default: Date.now()}
        }
    });
    
    3. 学生答题内容表

    学生IDuserId,问题IDquestionId,回答内容answerCtn, 批阅后得到的分数score

    var answerSchema = new Schema({
        userId: {type: ObjectId, ref: 'User'},
        questionId: {type: ObjectId, ref: 'Question'},
        answerCtn: String,
        score: Number,
        meta: {
            updateAt: {type:Date, default: Date.now()},
            createAt: {type:Date, default: Date.now()}
        }
    });
    

    2. 教师端模块分解

    2.1 学生管理
    • 学生列表:查看已添加的学生学号和姓名
    • 添加学生:添加新学生
    2.2 题目管理
    • 查看题目列表:点击题号显示保存的题目内容和分数,点击文本框修改内容
    • 添加题目:添加新题目和分数
    2.3 考试情况
    • 学生考试状态:
    1. 实时查看学生的各种状态信息(红色:已登录;灰色:未登录;蓝色:考试中;绿色:已交卷)
    2. 可点击已交卷的学生块,进行对该学生的阅卷操作
    • 学生考试成绩:查看学生的考试成绩信息

    3. 学生端模块分解

    3.1 倒计时模块
    倒计时模块
    • 未到达开考时间显示 “距离考试开始” 的倒计时;
    • 到达开考时间显示 “距离考试结束” 的倒计时,直到考试结束倒计时停止;
    3.2 答题模块
    • 若考试时间未到点击题号,弹出警告框(考试时间未到);
    • 考试时间到学生点击题号进入答题状态,教师端更新学生状态;
    • 考试未结束考生点击提交或者考试时间到,考生转换成提交状态,教师端更新学生状态,提交状态的考生无法继续答题;

    4. 模块代码分析

    4.1 登录检测

    用户类型分为考生和教师,在登录时检测用户的类型,如果是教师则登入教师端页面,如果是考生则进入考生页面。

    // result为登录成功返回的用户信息
    if (result.data.category === "TEACHER") {
        location.href = "/p/index";
    } else {
        location.href = "/p/indexStudent";
    }
    
    4.2 添加学生(添加题目方法类似)
    添加学生页面元素
    //postData()为之后的post提供函数
    function postData(url, data, cb) {
        var promise = $.ajax({
            type: "post",
            url: url,
            dataType: "json",
            contentType: "application/json",
            data:data
        });
        promise.done(cb);
    }
    
    //传递JSON
    function doAddStudent() {
        var jsonData = JSON.stringify({
            'usrId': usrId,
            'pwd': pwd,
            'username': username
        });
        postData(urlAddStudent, jsonData, cbAddStudent);
    }
    
    //返回结果
    function cbAddStudent(result) {
        if (result.code == 99) {
            alert(result.msg);
        } else {
            alert("添加成功!");
            location.href = '/p/index';
        }
    }
    
    4.3 阅卷(查看考题信息和学生答题模块方法类似)

    进入页面通过POST从数据库获得题目列表,渲染出题号列表,每个题号给予一个data-id

    post获取题目列表,通过$.format(QUESTION_LIST, list[i]._id, i+1);渲染每一个题号,添加到(".item-number"里;
    QUESTION_LIST模板
    var QUESTION_LIST = "<div class='question-item' data-toggle='select' data-id='{0}'>{1}</div>";

    //获取题目列表
    function getQuestionList() {
        var jsonData = JSON.stringify({});
        postData(urlGetQuestionList, jsonData, cbQuestionList);
    }
    function cbQuestionList(result) {
        var list = result.results;
        for(var i = 0; i < list.length; i++) {
            var html = $.format(QUESTION_LIST, list[i]._id, i+1);
            $(".item-number").append(html);
        }
    }
    

    点击题号获得data-id
    $("body").on("click", "[data-toggle='select']", showContent);

    //显示题目内容和学生答题内容
    function showContent(e) {
        $(".answer-wrap").removeClass("hide");
        e.preventDefault();
        var $this = $(this);
        questionId = $this.data('id');
        $("#question-head").text("第" + $(this).text() + "题");
        getQuestionCtn();
        getAnswerOne();
        if(questionId != 0) {
            saveMark();
        }
    }
    

    post获取题目内容,返回结果放到指定div内
    $("#questionContent").text(result.content);

    //获取题目内容
    function getQuestionCtn() {
        var jsonData = JSON.stringify({
            "_id": questionId
        });
        postData(urlGetQuestionCtn, jsonData, cbShowQuestionCtn);
    }
    function cbShowQuestionCtn(result) {
        $("#que-score").text("分值:" + result.score);
        $("#questionContent").text(result.content);
    }
    

    post获取学生答题内容,返回的结果放到指定div内
    $("#answerCtn").text(result.answerCtn);

    //获得学生答案
    function getAnswerOne() {
        var jsonData = JSON.stringify({
            "userId": studentId,
            "questionId": questionId
        });
        postData(urlGetAnswerOne, jsonData, cbShowAnswer);
    }
    function cbShowAnswer(result) {
        if(result != "99") {
            $("#give-score").val(result.score);
            $("#answerCtn").text(result.answerCtn);
        } else {
            $("#answerCtn").text("该学生没有完成该题目");
        }
    }
    
    4.4 考生端倒计时
    1. 将教师设定的开考时间和结束时间分别与当前时间比较,得到相差的时间差毫秒seconds。对seconds进行处理得到格式化的字符串表示时间。
    1. 若当前时间小于开考时间,显示距离考试开始倒计时,到时间seconds <= 0,进入答题倒计时,显示距离考试结束倒计时,直到seconds <= 0,停止倒计时并自动提交考卷,考生转换成提交状态SUBMIT
    function getTimeDifference(y, n, M, h, m) {
        var now = new Date();
        var startTime =  new Date(y, n, M, h, m);
        var timeDifference = startTime.getTime() - now.getTime();
        var second = parseInt(timeDifference / 1000);
        var time = {
            remain: second,
            second: (second < 60) ? second : second % 60,
            hour: parseInt(second / 3600),
            minute: parseInt((second - parseInt(second / 3600) * 3600) / 60)
        };
        return time;
    }
    //考试开始时间
    function timeBefore() {
        var timer = setInterval(function() {
            var time = getTimeDifference(2016, 10, 24, 18, 56);
            $('#time-title').text("距离考试开始");
            $('#time-ctn').text(time.hour + " : " + time.minute + " : " + time.second);
            if(time.remain <= 0) {
                status = START;
                showExamTime();
                clearInterval(timer);
            }
        }, 1000);
    }
    //考试结束倒计时
    function showExamTime() {
        var timer = setInterval(function() {
            var time = getTimeDifference(2016, 10, 24, 23, 59);
            $('#time-title').text("距离考试结束");
            $('#time-ctn').text(time.hour + " : " + time.minute + " : " + time.second);
            if(time.remain <= 0) {
                status = END;
                doUpdate(SUBMIT);
                clearInterval(timer);
            }
        }, 1000);
    }
    

    5. 将考生端的考生状态实时更新到教师端

    1. 考生登录系统发送带有用户IDuserId和用户类型categorylogin 消息给服务器,服务器保存该用户(user[userId] = socket),接着判断该用户是否为教师,若是则保存teacherId;最后在数据库中将该用户状态更新为登录LOGIN状态,向教师端发送reload消息,教师端接收到后重新post获取学生状态;
    1. 开考时间到,考生处于可考试状态WAIT,考生点击题号转换成答题状态EXAM,post到数据库更新状态EXAM,同时向服务器发送状态转换消息update status,服务器接收到后向教师端发送reload消息`;
    2. 考生点击提交按钮,数据库更新状态SUBMIT,同时向服务器发送状态转换消息update status,服务器接收到后向教师端发送reload消息`;
    考生端
    //socket初始化
    function socketInit() {
        var data = {
            userId: userId,
            userCategory: userCategory
        };
        socket.emit("login", data);
        status = LOGIN;
    }
    
    function showQuestion(e) {
        //如果为开考状态且用户不处于提交状态
        if(status == START && userStatus != SUBMIT) {
            getQuestionCtn();  //获取题目内容
            getAnswerOne();   //获取保存的答题内容
            doUpdate(EXAM);  //转换为答题状态
        } else if(userStatus == SUBMIT) {
            alert("你已提交答卷,请等候老师批阅。");
        } else if(status != START) {
            alert("考试时间未到!");
        }
    }
    
    //获取题目列表
    function getQuestionCtn() {}
    function cbShowQuestionCtn(result) {}
    
    //保存答题内容
    function doSaveAnswer() {}
    
    //获取答题保存的内容
    function getAnswerOne() {}
    function cbShowAnswer(result) {}
    
    //数据库更新用户状态SUBMIT,向教师端发送reload消息
    function doUpdate(status) {
        socket.emit("update status");
    }
    
    //转换成SUBMIT状态
    function cbUpdateStatus(result) {
         userStatus = SUBMIT;
    }
    
    服务器
    io.on('connection', function(socket){
      //用户登录
      socket.on('login', function (data) {
        socket.name = data.userId;
        user[data.userId] = socket;
        var data2 = {
          userId: socket.name,
          status: "LOGIN"
        };
        dbHelper.updateStatus(data2, function (success, doc) {});
        //用户类型-老师
        if(data.userCategory === "TEACHER") {
          teacherId = data.userId;
        }
        //向老师的客户端发送重新加载命令
        if(teacherId !== 0) {
          user[teacherId].emit("reload");
        }
      });
      socket.on('update status', function () {
        if(teacherId !== 0) {
          user[teacherId].emit("reload");
        }
      });
      //用户退出
      socket.on('disconnect', function () {
        var data = {
          userId: socket.name,
          status: "INIT"
        };
        dbHelper.updateStatus(data, function (success, doc) {});
        if(socket.name === teacherId) {
          teacherId = 0;
        } else if(teacherId !== 0){
          user[teacherId].emit("reload");
        }
        delete user[socket.name];
      });
    });
    

    Github参考代码

    相关文章

      网友评论

        本文标题:在线考试系统之模块分析

        本文链接:https://www.haomeiwen.com/subject/qqavpttx.html