線上考試系統思路總結
最近正在完成一個電網的線上考試系統,這個專案的整個業務部分和功能模組部分已經確定。
在這個專案中,主幹部分主要難點是四個部分,分為:設計資料庫、考試隨機出題、提交試卷評分、頁面上題下題提交試卷的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
,在抽取試題時,將所有的單選、多選、判斷分別存到三個集合中,然後使用隨機數工具類,要求不同題型多少道就獲取多少不重複的隨機數,然後分別在集合中獲取試題。這種方式較笨重,但刪除操作不會造成任何影響,適合題量較少、經常修改的考試系統、效能較低。
/**
* 隨機指定範圍內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> </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> </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> </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;
}
}
}
}
基本上思路就是這樣了,本人能力有限,歡迎指點迷津。