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

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
  • 绘制答题界面
    • 绘制题目下标、题目标题、题目内容
    • 绘制答题框
    • 绑定上一题、提交答案、下一题三个按钮点击事件
  • 存储数据


    答题界面.jpg
  • 提交答案
    • 发送学生ID、题目序号、题目答案到服务器,服务器存入数据库

3.1.2教师模块

  • 绘制学生状态界面
* 将每一个学生的名字与学生ID作为一个单位,有序的排列在界面上
* 绑定点击学生信息单元,进入对应学生的打分界面
* 通过socket实时更新学生状态
学生状态界面
  • 绘制打分界面
    • 主要调用学生模块的绘制答题界面函数
    • 添加打分栏
    • 更改提交答案按钮为打分按钮=


      改成绩界面
  • 存储数据
    3.1.3客户端socket模块
  • 发送数据模块
* 向服务器提交学生考试答案(学生端)
* 向服务器提交教师所改学生答卷题目分数(教师端)
* 向服务器提交开始考试信息(学生端)
* 向服务器提交结束考试信息(学生端)
  • 接受数据模块
    • 接受来自服务器发送的确认登录消息(学生端、教师端)
    • 接受来自服务器发送的状态改变消息(教师端)

3.2后台
3.2.1数据库操作模块

  • 向登录的学生发送数据(考试开始时间,考试内容)
  • 向登录的教师发送数据(学生列表,学生考试答案)
  • 更改用户状态(在线、进行考试、结束考试、离线)
  • 存储学生考卷答案
  • 存储学生考卷题目得分

3.2.2服务器socket模块

  • 接收来自客户端所提交的学生考试答案,并调用数据库模块存储学生考卷答案
  • 接收来自客户端提交的教师所改学生答卷题目分数,并调用数据库模块存储学生考卷得分
    。。。。。。
    服务器socket模块实现的功能与客户端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项目

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 《ijs》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 10...
    叶染柒丶阅读 4,730评论 0 7
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,419评论 1 37
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,621评论 2 17
  • 工具:Node.js + MongoDB + Socket.IO 完成进度 教师端: 学生的添加删除等操作 考题和...
    淡就加点盐阅读 2,767评论 8 28
  • 一个人对另一个人,一个人对一件东西或一件事情,总会有那么一些或多或少的感觉,这感觉或平淡、或兴奋、或难过、或忧伤、...
    知樱阅读 272评论 0 1