在线考试及练习系统
开题报告
目标:
随着社会经济的发展,人们对教育越来越重视。考试是教育中的一个重要环节,近几年来考试的类型不断增加以及考试要求不断提高,传统的考试方式要求教师打印考卷,监考、批卷,使教师的工作量越来越大,并且这些环节由于全部由人工完成,非常容易出错。因此,本在线考试及练习系统通过为考生提供更全面、更灵活的服务,解决了管理成本和人力、物力的大量投入,同时。考生希望对自己的学习情况进行客观、科学的评价;教务人员希望有效地改进现有的考试模式,提高考试效率。为了满足考生和教务人员的需求,在线考试及练习系统应包含考试及练习、成绩查询等功能。
内容:
1、用户管理模块:登录,注册,修改信息,安全退出,用户统计。
2、资源模块:查看资源,上传资源,下载资源,资源统计。
3、考试及练习模块:进行考试及练习,新增考试及练习,修改考试及练习,查看结果,考试及练习统计。
4、试卷管理模块:试卷导入,试卷查看,试卷修改,试卷删除,试卷统计。
ps:第一版本,不代表最终效果
数据库设计
ps:第一版本,不代表最终效果
介绍
本项目采用NET MVC作为前后端开发框架,使用了SQL Server作为数据库做数据存储,Entity FramWork作为数据操作框架,Database First数据操作模式,bootstrap用于页面渲染,jQuery用于数据提交,echarts用于图表渲染及操作,多控制器多页面思想,面向对象思想,算法优化思想等等技术。
但是项目其中的算法以及数据处理和提交所用的技术方法尚未成熟,数据库设计思想较差,存在很大瑕疵,尤其是大数据量的操作,以及对各种设备的兼容仍存在较大缺陷,所以本项目仅能用作新手的一个项目参考,也希望广大网友能多多提出意见或建议。
脑图
大致就这么几个模块,一个模块一个控制器,每个控制器又有不同的页面。整个系统大致的设计就是这样。
项目
母版
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#example-navbar-collapse">
<span class="sr-only">切换导航</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" onmouseover="this.style.color='#9c9c9c'" onmouseout="this.style.color='#000000'" href="~/Main/Home" style="color: #000000; font-weight: bold; ">在线考试及练习系统</a>
</div>
<div class="collapse navbar-collapse" id="example-navbar-collapse">
<ul class="nav navbar-nav">
<li><a onmouseover="this.style.color='#9c9c9c'" onmouseout="this.style.color='#000000'" href="~/Main/Home" style="color: #000000; font-weight: bold; ">首页</a></li>
<li><a onmouseover="this.style.color='#9c9c9c'" onmouseout="this.style.color='#000000'" href="~/Home/Index" style="color: #000000; font-weight: bold; ">考试</a></li>
<li><a onmouseover="this.style.color='#9c9c9c'" onmouseout="this.style.color='#000000'" href="~/Reso/Index" style="color: #000000; font-weight: bold; ">资源大全</a></li>
<li><a onmouseover="this.style.color='#9c9c9c'" onmouseout="this.style.color='#000000'" href="~/Test/Index" style="color: #000000; font-weight: bold; ">试卷管理</a></li>
</ul>
<!--向右对齐-->
<ul class="nav navbar-nav navbar-right" id="example-navbar-collapse">
<li class="dropdown" style="color:#000000;">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<b>我的 </b><b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="~/My/RecordsInfo">我的成绩</a></li>
@*<li><a href="#">我的错题</a></li>*@
<li><a data-toggle="modal" data-target="#UpUser" onclick="getuser()">修改信息</a></li>
<li class="divider"></li>
<li><a href="~/Main/Index">安全退出</a></li>
</ul>
</li>
</ul>
@if (Session["name"] != null)
{
<p class="navbar-text navbar-right" style="color:#000000;"><b>您好!@Session["name"]</b></p>
}
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<!-- 模态框(Modal) -->
<div class="modal fade" id="UpUser" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">个人信息修改</h4>
</div>
<form action="~/My/UpdateUser" method="post" class="bs-example bs-example-form" role="form">
<div class="modal-body">
<div class="input-group" style="margin:20px 0px;">
<span class="input-group-addon">昵称</span>
<input type="text" class="form-control" name="name" value="@Session["name"]" required placeholder="昵称">
</div>
<div class="input-group" style="margin:20px 0px;">
<span class="input-group-addon">账号</span>
<input type="text" class="form-control" name="id" value="@Session["id"]" required placeholder="账号">
</div>
<div class="input-group" style="margin:20px 0px;">
<span class="input-group-addon">密码</span>
<input type="text" class="form-control" name="pwd" value="@Session["pwd"]" required placeholder="密码">
</div>
</div>
<div class="modal-footer">
<a class="btn btn-default" data-dismiss="modal">关闭</a>
<input type="submit" value="提交更改" class="btn btn-primary">
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
<footer>
<p>© 在线考试及练习系统</p>
</footer>
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
登录注册
<div class="dowebok" id="dowebok">
<div class="form-container sign-up-container">
<form action="~/Main/Enrolls" method="post">
<h1>注册</h1>
<span>或使用邮箱注册</span>
<div style="height:10px;"></div>
<input type="text" name="Name" placeholder="昵称" required />
<input type="text" name="Id" placeholder="账号" required />
<input type="password" name="Pwd" id="Pwd" placeholder="密码" required />
<select id="selects" name="Postss" onchange="posts()">
<option value="2">管理员</option>
<option value="3">用户</option>
</select>
<div id="tests" style="display:none;">
<input type="text" name="Test" id="lishu" placeholder="隶属人" />
</div>
<input type="submit" class="but" value="注册" style="color:#000;" />
</form>
</div>
<div class="form-container sign-in-container">
<form action="~/Main/login" method="post">
<h1>登录</h1>
<span>或使用您的帐号</span>
<div class="social-container"></div>
<input type="text" name="userid" placeholder="账号" value="@ViewBag.user" required />
<input type="password" name="userpwd" placeholder="密码" value="@ViewBag.pwd" required />
<label style="text-align: left; width: 250px; font-size: 13px; vertical-align: middle;">
<input type="checkbox" name="jizhu" checked id="jizhu" style="width: 20px; vertical-align: middle;" /><label for="jizhu">记住密码</label>
</label>
<input class="but" type="submit" value="登录" style="color:#000;" />
</form>
</div>
<div class="overlay-container" style="background-color:#fff;">
<div class="overlay">
<div class="overlay-panel overlay-left">
<h1> 已有帐号?</h1>
<p>请使用您的帐号进行登录</p>
<button class="ghost but" id="signIn">登录</button>
</div>
<div class="overlay-panel overlay-right">
<h1> 没有帐号?</h1>
<p>立即加入我们!</p>
<button class="ghost but" id="signUp">注册</button>
</div>
</div>
</div>
</div>
<script>
function posts() {
if ($("#selects").val() == "3") {
$("#tests").css('display', 'block');
$("#lishu").attr('required', 'required');
} else {
$("#tests").css('display', 'none');
$("#lishu").removeAttr('required');
}
}
</script>
if (!string.IsNullOrEmpty(userId))
{
ViewBag.user = userId;
}
else
{
HttpCookie cookie = Request.Cookies.Get("Example");
if (cookie != null)
{
ViewBag.user = cookie.Values["UserName"].ToString();
ViewBag.pwd = cookie.Values["UserPwd"].ToString();
}
}
获取及保存账号
if (jizhu!=null)
{
HttpCookie hc = new HttpCookie("Example");
hc["UserName"] = userid;
hc["UserPwd"] = userpwd;
hc.Expires = DateTime.Now.AddDays(3);
Response.Cookies.Add(hc);
}
else
{
HttpCookie hc = Request.Cookies.Get("Example"); ;
if (hc != null)
{
hc.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(hc);
}
}
Users u = DAL.GetUser(userid,userpwd);
if (u.userName__userName.Equals("0x80004005"))
{
Response.Write("<script>alert('数据库连接失败!');window.history.go(-1);</script>");
}
if (u!=null)
{
NowUser.nowUser = u;
Session["name"] = u.userName__userName;
Session["id"] = u.userAccount;
Session["pwd"] = u.userPassword;
Response.Write("<script>window.location.href='Home';</script>");
}
else
{
Response.Write("<script>alert('账号或密码错误!');window.history.go(-1);</script>");
}
登录实现
//账号查重
if (DAL.GetUserByAct(Id)!=null)
{
Response.Write("<script>alert('账号重复!');window.history.go(-1);</script>");
return;
}
Users u = new Users();
u.userName__userName = Name;
u.userAccount = Id;
u.userPassword = Pwd;
u.postsID = int.Parse(Postss);
if (!string.IsNullOrEmpty(Test))
{
u.TestUser = DAL.GetUserByAct(Test).userID;
}
if (DAL.AddUsers(u))
{
Response.Write("<script>alert('注册成功!');window.location.href='Index?userId="+Id+"';</script>");
}
else
{
Response.Write("<script>alert('注册失败!');window.history.go(-1);</script>");
}
注册实现
首页
首页主要是页面的跳转以及跳转路径标签的装饰渲染,没有数据操作的功能,仅有一个时间显示的js
<script>
var div = document.getElementById("showtime");
var divs = document.getElementById("showtimes");
setInterval(function () {
var nowdate = new Date();
var year = nowdate.getFullYear(),
month = nowdate.getMonth() + 1,
date = nowdate.getDate(),
day = nowdate.getDay(),
week = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
h = nowdate.getHours(),
m = nowdate.getMinutes(),
s = nowdate.getSeconds(),
h = checkTime(h),
m = checkTime(m),
s = checkTime(s);
div.innerHTML = year + "年" + month + "月" + date + "日" + week[day];
divs.innerHTML = h + ":" + m + ":" + s;
}, 1000);
var checkTime = function (i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
</script>
考试
考试主界面就是一个所有试卷的查询显示,单个试卷的信息查询,以及根据id跳转到考试页面或练习界面,没有什么主要功能。
考试页面和练习页面主要是就所有题目的遍历,所有选项的遍历,以及根据题目编号的跳转等等,其中所有题目和选项并不能使用数据库查出来的顺序列表,而是需要中间操作一下,对其顺序进行随机打乱,做到题目无序,选项无序,保证考试的安全性和公平性。题目的遍历则使用for循环,这里其实可以有更好地选择,比如vue的v-for以及双向绑定和数据提交,会比单纯的for循环和jQuery的ajax优秀很多。
提交
$.ajax({
type: "post",
url: "Judging",
data: "xuanze=" + cho + "&panduan=" + jud + "&tiankong=" + fil + "&jianda=" + ans,
success: function (data) {
document.write(data);
setTimeout(function () {
history.back(-1);
}, 10000);
},
error: function (e) {
alert("交卷失败" + e);
}
});
这里写的提交是使用合并字符串和拆解字符串的方式,属于是最下下层的一种方式,现在看来真是太拉了,这里仅做参考,不建议使用这种方式,比较好的方式还是vue的双向绑定提交方式。
打乱算法
Random ran = new Random();
/// <summary>
/// 试题随机打乱算法
/// </summary>
/// <param name="l"></param>
/// <returns></returns>
public List<Chooses> Dischooses(List<Chooses> l)
{
int[] nums = new int[l.Count];
for (int i = 0; i < l.Count; i++)
{
nums[i] = i+1;
}
byte[] b = new byte[l.Count];
ran.NextBytes(b);
Array.Sort(b,nums);
List<Chooses> temp = new List<Chooses>();
for (int i = 0; i < nums.Length; i++)
{
Chooses c = l[nums[i]-1];
temp.Add(DisType(c));
}
return temp;
}
/// <summary>
/// 选项随机打乱算法
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public Chooses DisType(Chooses c)
{
//选项
char[] answer = c.Answer.ToLower().Trim().ToArray();
//保存答案值
string[] ans = new string[answer.Length];
for (int i = 0; i < answer.Length; i++)
{
switch (answer[i])
{
case 'a':
ans[i] = c.TypeA;
break;
case 'b':
ans[i] = c.TypeB;
break;
case 'c':
ans[i] = c.TypeC;
break;
case 'd':
ans[i] = c.TypeD;
break;
case 'e':
ans[i] = c.TypeE;
break;
default:
break;
}
}
//选项值
List<string> l = new List<string>();
if (c.TypeA != null)
{
l.Add(c.TypeA);
}
if (c.TypeB != null)
{
l.Add(c.TypeB);
}
if (c.TypeC != null)
{
l.Add(c.TypeC);
}
if (c.TypeD != null)
{
l.Add(c.TypeD);
}
if (c.TypeE != null)
{
l.Add(c.TypeE);
}
//1-1+1 1-1+2 1+1-1+2 5-5+1 5-5+2 5+1-5+2
//打乱
for (int i = 0; i < l.Count; i++)
{
int lift = 0;
int right = l.Count-1;
string item = "";
switch (ran.Next(6))
{
case 0:
item = l[lift];
l[lift] = l[lift+1];
l[lift+1] = item;
break;
case 1:
item = l[lift];
l[lift] = l[lift + 2];
l[lift + 2] = item;
break;
case 2:
item = l[lift+2];
l[lift+2] = l[lift + 1];
l[lift + 1] = item;
break;
case 3:
item = l[right];
l[right] = l[right - 1];
l[right - 1] = item;
break;
case 4:
item = l[right];
l[right] = l[right - 2];
l[right - 2] = item;
break;
case 5:
item = l[right-2];
l[right-2] = l[right - 1];
l[right - 1] = item;
break;
default:
break;
}
}
string zans = "";
//寻找答案
for (int i = 0; i < ans.Length; i++)
{
for (int j = 0; j < l.Count; j++)
{
if (ans[i].Equals(l[j]))
{
zans += (char)(97 + j);
break;
}
}
}
char[] ch = zans.ToArray();
Array.Sort(ch);
string da = new string(ch);
c.Answer = da.ToUpper();
//赋值
if (l.Count>0)
{
c.TypeA = l[0];
}
if (l.Count > 1)
{
c.TypeB = l[1];
}
if (l.Count > 2)
{
c.TypeC = l[2];
}
if (l.Count > 3)
{
c.TypeD = l[3];
}
if (l.Count > 4)
{
c.TypeE = l[4];
}
return c;
}
判卷算法
string[] cho = xuanze.Split(' ');
//索引
int i = 0;
//错题数量
int sum = 0;
foreach (var item in cho)
{
//去空选项
if (!item.Equals(""))
{
//比对
//多选
if (item.Length>1)
{
char[] t = item.ToCharArray();
Array.Sort(t);
string temp = new string(t);
if (!(daan[i].ToLower()).Equals(temp.ToLower()))
{
sum++;
}
}
else
{
if (!(daan[i].ToLower()).Equals(item.ToLower()))
{
sum++;
}
}
i++;
}
}
string[] jud = panduan.Split(' ');
foreach (var item in jud)
{
if (!item.Equals(""))
{
//比对
if (!(daan[i].ToLower()).Equals(item.ToLower()))
{
sum++;
}
i++;
}
}
string[] fil = tiankong.Split(' ');
foreach (var item in fil)
{
if (!item.Equals(""))
{
//比对
if (!daan[i].Equals(item.ToLower()))
{
sum++;
}
i++;
}
}
string[] ans = jianda.Split(' ');
foreach (var item in ans)
{
if (!item.Equals(""))
{
if (!daan[i].Equals(item.ToLower()))
{
sum++;
}
i++;
}
}
正因为使用了字符串提交的方式,造成无论前端数据处理还是后端数据匹配,复杂度都比较高。不如vue方便简洁。
试卷
这一块呢主要是试卷的导入和Excel的读取,其余操作均是对数据库数据的增删改查。导入就是文件的上传以及保存。
Excel读取
string connstring = "Provider = Microsoft.ACE.OLEDB.12.0; Data Source = " + path + "; Extended Properties = 'Excel 8.0;';";
using (OleDbConnection conn = new OleDbConnection(connstring))
{
conn.Open();
DataTable sheetsName = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "Table" }); //得到所有sheet的名字
string firstSheetName = sheetsName.Rows[0][2].ToString(); //得到第一个sheet的名字
string sql = string.Format("SELECT * FROM [{0}]", firstSheetName); //查询字符串
OleDbDataAdapter ada = new OleDbDataAdapter(sql, connstring);
DataSet set = new DataSet();
ada.Fill(set);
conn.Close();
conn.Dispose();
return set.Tables[0];
}
资源
资源主要就是文件上传与下载,以及页面的展示,展示也是使用for循环方式。
上传
[HttpPost]
public void AddReso(HttpPostedFileBase files, string txtname, string txtintro)
{
if (files == null || string.IsNullOrEmpty(txtname) || string.IsNullOrEmpty(txtintro))
{
Response.Write("<script>alert('资源不允许为空!');window.location.href='Index';</script>");
return;
}
string fileName = files.FileName;
string exten = Path.GetExtension(fileName).ToLower();
string testName = System.Guid.NewGuid().ToString("N");
string url = Server.MapPath("~/Resource/") + testName + exten;
files.SaveAs(url);
//插入数据库
Resources r = new Resources();
r.ResourceName = txtname;
r.ResourceIntro = txtintro;
r.ResourceSize = (files.ContentLength/1024).ToString() + "KB";
r.DownloadCount = 0;
r.UserID = NowUser.nowUser.userID;
r.ResourceCode = testName + exten;
if (DAL.AddResources(r))
{
Response.Write("<script>alert('上传成功!');window.location.href='Index';</script>");
}
}
下载
public FileStreamResult Download(string fileName,string fileid)
{
string filePath = Server.MapPath("~/Resource/"+fileName);
FileStream fs = new FileStream(filePath, FileMode.Open);
//修改下载次数
DAL.SetResoCount(int.Parse(fileid));
return File(fs, "text/plain", fileName);
}
我的
此模块的个人信息转移到了母版里边,其中也是数据操作,主要是应用了echarts图表
<div id="main" style="width:100%;height:400px;margin:40px 0px;"></div>
@section Scripts{
<script>
$.ajax({
url: "Echart",
type: "get",
data: {},
dataType: 'json',
async: false,
success: function (res) {
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({
title: {
text: '我的成绩',
left: 'center'
},
tooltip: {},
xAxis: {
name: '日期',
data: res.RecordTime
},
yAxis: {
type: 'value',
name: '成绩'
},
series: [{
name: '成绩',
type: 'line',
data: res.Score,
areaStyle: {
normal: {
color: 'skyblue'
}
},
itemStyle: {
normal: {
color: 'skyblue',
lineStyle: {
color: 'skyblue'
}
}
},
// 最大最小值
markPoint: {
data: [
{
type: 'max',
name: '最高成绩'
},
{
type: 'min',
name: '最低成绩'
}
]
},
// 平均值
markLine: {
data: [
{
type: 'average',
name: '平均成绩'
}
]
}
}]
})
}
});
</script>
}
大概的功能就是这些,各位大佬有什么意见或者建议都欢迎提出!
资源
暂无,或联系我q+2698666037。