在线考试系统思路总结

最近正在完成一个电网的在线考试系统,这个项目的整个业务部分和功能模块部分已经确定。
在这个项目中,主干部分主要难点是四个部分,分为:设计数据库、考试随机出题、提交试卷评分、页面上题下题提交试卷的JS。

下面就模拟一个小的考试系统,给大家演示:
一、数据库部分
在设计数据库时,
1、先设计题型的表,在这部分每个题型都是一张独立的表,主要是单选表、多选表、判断表、类型表。在表字段的设计上,都是类似的。主要字段和一条虚拟数据如下

单选表:
id      |  content      | a   |   b    |    c   |   d    | answer | 所属专业id 
radio01 |  C语言重要不?  | 重要 | 不重要  | 不了解 | 拒绝回答 |  a     |     1
多选表:
id | content | a | b | c | d | e | answer | 所属专业id 
类似
判断表:
id | content | a | b | answer | 所属专业id 
类似
专业类型表
id  | content
1   | 配电

2、设计试卷参数表,用于设置不同专业出题的类型和数量,主要字段如下。

试卷参数表:

id | 所属专业id | 单选题数量 | 多选题数量 | 判断题数量 | 答题时间 | 
1  |    1      |     20   |     20   |     10    |   30

二、考试随机出题部分
在项目中,业务要求每个考生登录考试,即使报考专业相同,得到的题目也是不同的,在这个模块,有两个思路。

1、在数据库试题表中存储一个自增的id作为主键,或者每次添加的时候获取最大的id数目,添加时原有id+1作为主键,不管怎么实现最后在表中要形成不间断的数字,然后创建一个得到不重复数字的随机数工具类,获取的数字作为id进行获取试题。这种方法较第二种方法,效率较高,但当对某些进行删除后,处理相对麻烦,因为不可以获取空的题。
2、第二种思路是在数据库试题表中存储的主键是具有自己填写规则的varchar类型的字符串,比如201701
,在抽取试题时,将所有的单选、多选、判断分别存到三个集合中,然后使用随机数工具类,要求不同题型多少道就获取多少不重复的随机数,然后分别在集合中获取试题。这种方式较笨重,但删除操作不会造成任何影响,适合题量较少、经常修改的考试系统、性能较低。

下面是获取不重复的随机数工具类:
这个是在网上找到的,随机数写的很好:http://www.cnblogs.com/happyday56/p/5163264.html

/** 
 * 随机指定范围内N个不重复的数 
 * 在初始化的无重复待选数组中随机产生一个数放入结果中, 
 * 将待选数组被随机到的数,用待选数组(len-1)下标对应的数替换 
 * 然后从len-2里随机产生下一个随机数,如此类推 
 * @param max  指定范围最大值 
 * @param min  指定范围最小值 
 * @param n  随机数个数 
 * @return int[] 随机数结果集 
 */  
public class randomSet {
    public static int[] randomArray(int min,int max,int n){  
        int len = max-min+1;  

        if(max < min || n > len){  
            return null;  
        }  

        //初始化给定范围的待选数组  
        int[] source = new int[len];  
           for (int i = min; i < min+len; i++){  
            source[i-min] = i;  
           }  

           int[] result = new int[n];  
           Random rd = new Random();  
           int index = 0;  
           for (int i = 0; i < result.length; i++) {  
            //待选数组0到(len-2)随机一个下标  
               index = Math.abs(rd.nextInt() % len--);  
               //将随机到的数放入结果集  
               result[i] = source[index];  
               //将待选数组中被随机到的数,用待选数组(len-1)下标对应的数替换  
               source[index] = source[len];  
           }  
           return result;  
    }  

三、提交试卷评分部分
在这个部分,主要分为两个模块,一个是多选部分的checkBox、一个是单选和判断部分的radio
1、checkBox部分
当在登录完成后,后台随机出题时,将随机数保存到session中,将在多选部分是在遍历时候,checkBox的name设置为当前题的id,保证名称是唯一,提交后,在后台取出session中的随机数,然后如果试题id是自增的话,直接可以获取试题的所有信息,按id获取考生每道题的选择,在这里由于是CheckBox,所以有可能是多个值,先将值拼接为字符串,然后和试题的答案进行对比,相同的话得分。
2、radio部分
这部分和checkBox部分类似,最后不需要拼接字符串,就可以比对。

四、上下题、提交JS部分
在页面这个部分,分为三个部分进行遍历,在页面上有一个大的form表单,用于提交数据,单form表单中有三个foreach用于遍历,然后在foreach中有一个div,三个类型的题具有三个不同结构的id,同时使用varStatus属性,实现dan1、dan2、dan3这种效果。然后将所有div隐藏。在最上方的rnum、cnum、tnum分别代表不同类型的试题数目。在form的上方实现显示所有题目号,并通过下面的方式将链接中的class和遍历题的id设为一样的值,用于实现上下题和点击显示题目的效果。
具体代码如下
1、表单和页面设置

        <input type="hidden" id="zhi1" value="${rnum}">
        <input type="hidden" id="zhi2" value="${cnum}">
        <input type="hidden" id="zhi3" value="${tnum}">
    <table>
    <tr><th>单选题部分</th></tr>
    <tr>
         <c:forEach  begin="${begin}" end="${rend}" var="r">
          <td><a onclick="getdanxuan(this)" class="dan${r}">【${r}】</a>&nbsp;&nbsp;</td>
         </c:forEach>
    </tr>
    <tr><th>多选题部分</th></tr>
    <br>
    <tr>
        <c:forEach  begin="${begin}" end="${cend}" var="c">
        <td><a onclick="getduoxuan(this)" class="duo${c}">【${c}】</a>&nbsp;&nbsp;</td>
        </c:forEach>
    </tr>
    <tr><th>判断题部分</th></tr>

    <tr>
        <c:forEach  begin="${begin}" end="${tend}" var="t">
        <td><a onclick="getpanduan(this)" class="pan${t}">【${t}】</a>&nbsp;&nbsp;</td>
        </c:forEach>
    </tr>
</table>
    <br>
<c:forEach items="${rflist}" var="rf"  varStatus="state">
<div id="dan${state.count}" class="danxuan" style="display: none">
            <tr>【单选题】${state.count}</tr>
</div>
</forEach>
<br>
<c:forEach items="${cflist}" var="cf"  varStatus="state1">
    <div id="duo${state1.count}" class="duoxuan" style="display: none">
            <tr>【多选题】${state1.count}</tr>
    </div>
</forEach>
<c:forEach items="${tflist}" var="tf"  varStatus="state2">
   <div id="pan${state2.count}" class="panduan" style="display: none;">
            <tr>【判断题】${state2.count}</tr>
   </div>
</forEach>
<input type="button" value="上一题" onclick="shang()">
<input type="button" value="下一题" onclick="xia()">

2、JS部分
在这部分使用JS实现上一题、下一题、和点击题目号显示题目的效果。
(1)在页面显示初始化部分设置单选第一道显示,先获取三个类型的数目,然后获取div的数目,由于只有题目具有div,所以可以通过这种方式进行设置;
(2)在点击题目显示部分,有三个函数,分别对应三类题型,道理相同,首先设置所有div是隐藏的,然后获取目标的class名称,在通过class名称获取指定div,将其设置为block。
(3)点击上下题,显示指定题目,道理是类似的,都是先获取所有div元素,然后判断哪个是显示的,记录下来,将其隐藏,然后将上一个或者下一个进行显示。

<script type="text/javascript">
//页面显示初始化部分
var zhi1=document.getElementById("zhi1").value;
var zhi2=document.getElementById("zhi2").value;
var zhi3=document.getElementById("zhi3").value;
var zhi=parseInt(zhi1)+parseInt(zhi2)+parseInt(zhi3);
var divs=document.getElementsByTagName("div");
divs[0].style.display="block";
//点击单选题目显示
function getdanxuan(obj){
    for(var i=0;i<zhi;i++){
        divs[i].style.display="none";
    }
    var a=obj.className;
    var div1=document.getElementById(a);
     div1.style.display = "block";

}
//点击多选题目显示
function getduoxuan(obj){
    for(var i=0;i<zhi;i++){
        divs[i].style.display="none";
    }
    var b=obj.className;
    var div2=document.getElementById(b);
    div2.style.display="block";
}
//点击判断题目显示
function getpanduan(obj){
    for(var i=0;i<zhi;i++){
        divs[i].style.display="none";
    }
    var c=obj.className;
    var div3=document.getElementById(c);
    div3.style.display="block";
}
//点击上一题
function shang(){
    var divs=document.getElementsByTagName("div");
    for(var i=0;i<divs.length;i++){
        if(divs[i].style.display=='block'){
        if(i==0){
            alert("已经是第一题了");break;
        }else{
                divs[i].style.display="none";
                divs[i-1].style.display="block";
                break;
        }
        }
    }
}
//点击下一题
function xia(){
    var divs=document.getElementsByTagName("div");
    for(var i=0;i<divs.length;i++){
        if(divs[i].style.display=='block'){
            if(i==divs.length-1){
                alert("已经是最后一题了");break;
            }else{
                divs[i].style.display="none";
                divs[i+1].style.display="block";
                break;
            }
            }
        }
}

基本上思路就是这样了,本人能力有限,欢迎指点迷津。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值