前端社团

Node.js在线考试系统——服务器与客户端的交互

2016-12-24  本文已影响984人  折柳画马

Node.js在线考试系统

1. 系统结构

系统考虑使用Nodejs和SocketIo实现服务器端逻辑,前端使用HTML5,数据库使用MongoDB

2.数据结构

用户表

var userSchema = new Schema({
    userId:String,          //用户ID
    username: String,       //用户名称
    password: String,       //账户密码,初始化为a
    status:String,          //用户状态,分为四个状态,在线landing,离线offline,提交完毕submited,考试中testing
    identity:String,        //用户身份,分别为student和teacher
});

科目表

var subjectsSchema = new Schema({
    course:String,      //考试科目名称
    startTime:Date,     //考试开始时间
    lastTime:Number,    //考试持续时间,单位为分
    numOfQuestions:Number,//问题总数
    questions:[{        //问题
        index:Number,   //当前问题序号
        question:String,//问题内容
        score:Number    //问题分值
    }]
});

答题表

var answersSchema = new Schema({
    user:{type:Schema.Types.ObjectId, ref:'User' },//用户表引用
    subject:{type:Schema.Types.ObjectId,ref:'Subjects'},//科目表引用
    questions:[{
        index:Number,//当前问题序号
        content:String,//答案
        score:Number //后期打分分值
    }]
});

3.系统模块设计

3.1前端
3.1.1学生模块

  • 绘制倒计时界面
* 提示学生还有多少时间开始考试
* 如果考试时间未到,学生点击开始考试会弹出"未到考试时间"提示框
* 开始考试,学生点击开始考试按钮,进入考试界面并且发送消息给服务器,服务器转发给教师端,更新教师端当前学生状态
准备界面.jpg

3.1.2教师模块

  • 绘制学生状态界面
* 将每一个学生的名字与学生ID作为一个单位,有序的排列在界面上
* 绑定点击学生信息单元,进入对应学生的打分界面
* 通过socket实时更新学生状态
学生状态界面
  • 发送数据模块
* 向服务器提交学生考试答案(学生端)
* 向服务器提交教师所改学生答卷题目分数(教师端)
* 向服务器提交开始考试信息(学生端)
* 向服务器提交结束考试信息(学生端)

3.2后台
3.2.1数据库操作模块

  • 向登录的学生发送数据(考试开始时间,考试内容)

3.2.2服务器socket模块

  • 接收来自客户端所提交的学生考试答案,并调用数据库模块存储学生考卷答案

4.功能分析

学生模块与教师模块中的界面绘制之前已有文章提及,这里略去
学生模块的本地数据存储功能

学生登录->
后台路由发送数据data->

router.get('/student', function(req, res, next) {
  dbHelper.findUsrInfo(req, function (success, data) {
    //console.log(data.friends);
    //data.identify身份,data.subject考试题目,data.answer考试答案
    res.render('student', {
      scriptData:JSON.stringify(data),
      data:data
    });
  })
});

因为服务器应用express模块,data数据用于渲染前端页面,但无法被js文件获取到,所以需要经过处理,data数据被转换为json格式,并存入window.scriptData->

<script>
    window.scriptData = JSON.stringify({{{scriptData}}});
    window.scriptData=eval("("+window.scriptData+")");//转换为json对象
</script>

学生成功登录后,进入准备开始考试界面,运行modalBox.init()函数,并将modalBox对象并赋值给STUDENT,default存储本地数据,window.scriptData.subject.questions即为服务器所发考试相关数据中的题目对象->

var STUDENT;
new modal_student();
var modal_student = function () {
     var modalBox = {
        default:{
              questions:[],//题目对象
              answers:[],//学生所写答案
              index:0//当前题目编号,
        },
        init:function () {
            ptrThis = this;
            ptrThis.setQuestions();
        },
        setQuestions:function () {
            ptrThis.default.questions = window.scriptData.subject.questions;
            console.log(ptrThis.default.questions);
        },
     }
    STUDENT = modalBox;
    return modalBox.init();
}

今后学生切换题目时只需要从本地获取数据,而不需要多次与服务器进行请求

教师模块的本地数据存储功能

教师登录->
后台发送数据data->

router.get('/teacher', function(req, res, next) {
  dbHelper.findStudentsInfo(req, function (success, data) {
    //console.log(data.friends);
    //data.students学生信息,data.subject课程信息
    res.render('teacher', {
      students:data.students,
      scriptData:JSON.stringify(data)
    });
  })
});

与上同->

<script>
    window.scriptData = JSON.stringify({{{scriptData}}});
    window.scriptData=eval("("+window.scriptData+")");//转换为json对象
</script>

教师成功登录后,进入浏览学生名单界面,运行modalBox.init()函数,并将modalBox对象并赋值给TEACHER->

var TEACHER;
new modal_teacher();
var modal_teacher = function () {
    var ptrThis;
     var modalBox = {
         default:{
             studentIndex:0,//当前正在进行批改的学生的下标
             studentsList:[]
         },
        init:function () {
            ptrThis = this;
            ptrThis.default.studentsList = window.scriptData.students;
        }
    }
    TEACHER = modalBox;
    return modalBox.init();
}

studentsList存储学生信息列表,包含学生姓名、学号、状态,以及学生每道题目的答案
studentsList格式

var studentList = [{
      user:{
          username:String,//学生姓名
          userId:String,//学生ID
          status:String,//学生状态
      },
      questions:[{
          content:String,//答案
          index:Number//当前题目下标
      }]
}]

学生交卷

modalBox.bindEnd()绑定了结束考试button的点击事件->

       bindEnd:function () {
            var end = document.getElementById("end");
            end.addEventListener("click",function () {
               ptrThis.saveAnswer();//将当前答案保存到本地文件
               socketFun.end();//调用客户端socket模块的end()函数
            });
        },

点击事件触发,调用客户端socket模块的end()函数->

var socket = io.connect('http://localhost:3000');
var X = window.scriptData;                          //截取服务器发送过来的数据
var socketFun = {
    //省略
    end:function () {
        var obj = {
            userId:X.userId,
            indentify:X.identify
        }
        obj ["answers"] = STUDENT.default.answers;
        if(X.status=="submited"){
            alert("你已经提交过答案!");
        }else {
            socket.emit("end", obj );
        }
    }
}

客户端socket模块将学生学号userId、学生身份identify、学生答案answers发送到服务器->

        socket.on("end",function (result) {
            User.update({userId:result.userId},{status:"submited"}).exec(function (err,doc) {//更新数据库中的学生状态为submited(提交答案)
                User.findOne({userId:result.userId}).exec(function (err,doc) {//根据学生ID查询该条学生记录
                    var change = {
                        studentId:result.userId,
                        status:"submited"
                    };
                    if(teacherOnline){//如果教师在线,则向其发送学生状态改变的消息
                        teacher.emit("statusChange",change);
                    }
                    Answers.findOne({user:doc._id}).exec(function (err,answer) {//答题表根据学生记录的_id查询到对应记录,更新答案
                        answer.questions = result.answers;
                        answer.save(function (err) {})
                    })
                });
            })
        }),

教师模块通过socket实时更新学生状态

客户端socket模块接收到来自服务器的学生状态改变信息->

var socket = io.connect('http://localhost:3000');
socket.on("statusChange",function (change) {
    TEACHER.changeStatus(change);
})

调用教师模块的changeStatus函数,以change为参数->

         changeStatus:function (change) {
             console.log(ptrThis.default.studentsList);
            for(var i = 0;i<ptrThis.default.studentsList.length;++i){//遍历当前学生列表
                console.log(ptrThis.default.studentsList[i].user.userId+"与"+change.studentId);
                if(ptrThis.default.studentsList[i].user.userId==change.studentId){//重新设置status
                    ptrThis.default.studentsList[i].user.status = change.status;
                    var studentInfo = document.getElementsByClassName("studentInfo");
                    studentInfo[i].setAttribute("class","studentInfo "+change.status);//修改界面上学生列表中对应的学生的状态(改变颜色以提示教师有学生状态改变)
                    break;
                }
            }
         },

5.具体代码见

github下的onlineTest项目

上一篇下一篇

猜你喜欢

热点阅读