题库管理系统设计文档.docx
《题库管理系统设计文档.docx》由会员分享,可在线阅读,更多相关《题库管理系统设计文档.docx(34页珍藏版)》请在冰豆网上搜索。
![题库管理系统设计文档.docx](https://file1.bdocx.com/fileroot1/2023-6/12/e0f71ff3-44ae-4476-8cb2-4cc86f7401c1/e0f71ff3-44ae-4476-8cb2-4cc86f7401c11.gif)
题库管理系统设计文档
(此文档为word格式,下载后您可任意编辑修改!
)
华南农业大学
软件开发技术实践
课程设计实验报告
小组成员:
教师:
林毅申
提交日期:
2013年11月30日
华南农业大学信息学院
课程设计
实验题目
题库管理中心
起止日期
2013学年第一学期
课程名称
软件开发技术实践
学生资料
实验内容
每一位教师都可将自己平日教学中积累的题目添加到题库中,与传统意义的题库不同的是,在这里教师可以将题库中的题目按照各种不同的属性(章节、知识点、考试时间、考试地区、难易度、助记码等)把每一道题目定义,方便教师搜索查阅,从不同的角度判断题目是否对自己的教学有益,运用到教学中是否能有的放矢、因材施教。
评
语
与
成
绩
项目/分数
优
良
中
及格
不及格
系统分析设计
□
□
□
□
□
程序设计水平
□
□
□
□
□
内容完成情况
□
□
□
□
□
设计报告撰写质量
□
□
□
□
□
课程设计总结情况
□
□
□
□
□
附加说明:
成绩(优/良/及格/不及格)指导教师林毅申
2013年12月
1、基本信息
1.1小组基本信息
学号
姓名
手机
邮箱
所做工作简述
签名
赖威
数据库设计,数据库实体化工作以及数据库面向业务层的接口
李志杰
客户端界面以及相关数据绑定
周加伟
业务逻辑层设计与实现,具体功能的算法实现
1.2项目基本信息
这次我们小组开发的是一个面向一所大学内的任课老师以及在校学生的题库管理系统。
作为数字化校园的一部分,该系统的主要的功能包括为教师用户提供的查阅题库,新组试卷功能和面向学生的在线考试功能。
题库分为两部分,一部分是之前教师利用组卷功能得到并保存的试卷,一部分是不同类型的题目。
题目范围为学校内各门课程期末考试的试题,包括公共基础课以及各学院开设的不同课程。
题目不仅包括普通的文字类题目,还包括一些包含复杂元素如图片,特殊公式等的题目,做到基本兼容现在学校内能看到的题目。
考虑到小组成员的技术水平以及项目的实际需要,该系统是采用C/S模式。
为提高效能,服务器方面采用IOCP模型,客户端采用WPF技术来改善Winform在界面上的劣势。
1.3项目技术信息
开发语言
C#
平台
Windows7
技术
客户端(WPF),服务器(IOCP模型)
工具
VisualStudio2010SqlServer2008
2、系统架构图
注:
实线表示数据传输方向,虚线表示模块间调用关系,由被调用者指向调用者
服务器程序:
数据加密模块:
采用一种加密算法,(具体哪种算法等系统实现时,再进行确定),将即将发送的数据进行加密。
保证数据再传输过程中的安全性。
与客户端中的数据加密模块原理一样。
数据解密模块:
采用与加密算法相对应的解密算法,将接受到数据,进行解密,还原得到原始的数据。
与客户端中的数据解密模块原理一样。
消息队列:
为了能实现同时让尽量多的客户连接到服务器,所以不使用一线程处理一客户模型的服务器。
这里的消息队列是将所有用户对服务器的各种请求按照先进先出的规则排列起来。
等待处理。
如果实现是使用windowsio完成端口和对应的SOCKET进行绑定,这个消息队列是由系统生成和维护的。
工作者线程池:
线程池用于维护和管理服务器中所有以工作者身份存在的线程,工作者线程就是用来处理客户请求的线程。
线程池的线程会互斥的到消息队列中取出对应的客户请求进行处理。
没有请求时候,所有线程都处于阻塞状态等待消息队列中出现客户请求。
当工作者线程获得客户请求的时候会按顺序去调用数据解密模块,数据传输格式解析模块,业务逻辑处理模块,加密发送数据模块。
数据传输格式解析模块:
数据在网络传输的过程中,我们给它规定特定的格式。
通过该模块,服务器可以清楚的知道,客户需要服务器为他实现哪些功能。
但是这里解析的数据需要通过解密之后,才能解析。
对应的客户端想要与服务器进行交互,就需要使用这个特定的格式发送数据。
业务逻辑处理模块:
这个模块就是服务器具体工作的模块,模块中包含服务器能够为客户端提供的所有服务。
工作者线程通过解析数据格式,知道客户的需求,再从这个模块中调用指定的服务。
这个模块也是直接和数据库进行交互的模块。
数据发送加密模块:
业务逻辑模块处理完客户的请求的服务之后,需要为客户返回处理后的结果。
可能是简单的操作成功和失败的返回结果,也可能是直接数据的传输。
和加密模块结合在一起,说明数据发送前需要进行加密。
数据库模块:
采用sqlserver做数据库管理,ADO.NET技术实现面向业务层的接口。
3、系统功能
客户端:
学生:
在这个题库管理中心中,我们设定的学生功能有查看试卷、导出试卷和在线考试并评分。
查看试卷:
学生可以查看不同科目的试卷,选择科目时需要先选择科目分类、学院等上级目录,试卷包括历年考试卷和各学科老师自己组合试题的模拟卷。
导出试卷:
学生查看了试卷之后如果需要带出,可以选择导出试卷,导出的试卷根据学生选择存储位置以WORD文档存储。
老师:
老师可以使用的功能包括:
查看题目、组卷。
组卷分为按要求自动组卷和手动组卷两个部分。
查看题目:
跟学生的一样,首先选择科目分类、学院等目录,选择了科目之后还需要选择章节和知识点、或者选择某一套题(具体的操作在编码时确定),进入单个题目可以看到题目分类、难易度、内容和答案,还可以把该题目加入到组卷区里(类似网络购物加入购物车的形式)。
组卷:
自动组卷:
老师选择科目、难易度,填入试卷总分和考试时间,选择题数目和选择题总分,填空题数目和填空题总分,客观题视科目需要而定,可以选择简答题、计算题、材料分析题等题型,最后各题总分要跟试卷总分相同,全部填写好之后就可以进行自动组卷,组卷成功之后老师看是否满意,不满意可以重来或者可以留下某一些需要的题其他题目随机加入。
数据传输:
用户进行操作需要读取数据的时候发出命令传给服务器,服务器传过来的数据接受后给用户看。
服务器:
提取数据:
根据客户端传来的命令来进行数据库的读取,读取后给传输模块。
数据传输:
数据提取出来之后,经过格式化以后再进行加密,之后就可以传输给客户端进行解密并呈现给用户。
4、数据库设计
PDM图
数据字典
清单:
表名
解释
QB_ACCOUNTS
用户信息表
QB_CHAPTERS
课程章节表
QB_COLLEGES
学院信息表
QB_COURSES
课程信息表
QB_ORIGIN
题目出处表
QB_PAPERS
试题信息表
QB_POINTS
知识点信息表
QB_PUBLICKINDS
公共课信息表
QB_QUESTIONS
题目信息表
QB_STUDENTS
学生信息表
QB_TEACHERS
教师信息表
表名:
QB_ACCOUNTS解释:
用户信息表
QB_ACCOUNTS(用户信息表)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
ACCOUNT_ID
Account_Id
INT
N
用户id
ACCOUNT_NAME
Account_Name
NCHAR(10)
10
N
用户的姓名
ACCOUNT_ACCOUNT
Account_Account
NCHAR(20)
20
N
用户的登陆账号
ACCOUNT_PASSWORD
Account_Password
NCHAR(20)
20
N
“123456”
用户的登陆密码
ACCOUNT_KIND
Account_Kind
BIT
N
用户的类型。
0代表教师,1代表学生
表名:
QB_CHAPTERS解释:
章节信息表
QB_CHAPTERS(QB_Chapters)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
CHAPTER_ID
Chapter_Id
INT
N
章节id
CHAPTER_COURSEID
Chapter_CourseId
INT
N
课程信息表中的课程id
章节所属的课程
表名:
QB_COLLEGES解释:
学院信息表
QB_COLLEGES(QB_Colleges)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
COLLEGE_ID
College_ID
INT
N
学院id
COLLEGE_NAME
College_Name
NCHAR(10)
10
N
学院名
COLLEGE_KIND
College_Kind
INT
N
学院所属类别,如:
工科
表名:
QB_COURSES解释:
课程信息表
QB_COURSES(QB_Courses)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
COURSE_ID
Course_Id
INT
N
课程类别
COURSE_NAME
Course_Name
NCHAR(10)
10
N
课程名
COURSE_KIND
Course_Kind
BIT
N
课程类别:
0代表学院专业课;1代表公共基础课
COURSE_KINDID
Course_KindId
INT
N
学院信息表中的学院id或公共课信息表中的公共课id
学院id或者是公共课类别
表名:
QB_ORIGIN解释:
题目来源表
QB_ORIGIN(QB_Origin)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
ORIGIN_ID
Origin_Id
INT
N
来源id
ORINGIN_DATE
Oringin_Date
DATE
N
题目对应的年份
ORIGIN_KIND
Origin_Kind
NCHAR(10)
10
N
题目的出处
表名:
QB_PAPERS解释:
试题信息表
QB_PAPERS(QB_Papers)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
PAPER_ID
Paper_Id
INT
N
试题id
PAPER_MADEDATE
Paper_MadeDate
DATE
N
出题时间
PAPER_AUTHOR
Paper_Author
NCHAR(10)
10
N
出卷人姓名
PAPER_END
Paper_End
NCHAR(10)
10
N
试题用处
考试时间
表名:
QB_POINTS解释:
知识点信息表
QB_POINTS(QB_Points)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
POINT_ID
Point_Id
INT
N
知识点id
POINT_CHAPTERID
Point_ChapterId
INT
N
章节信息表中的章节id
知识点所对应的章节的id
表名:
QB_PUBLICKINDS解释:
公共课信息表
QB_PUBLICKINDS(QB_PublicKinds)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
PUBLICKIND_ID
PublicKind_Id
INT
N
公共课id
PUBLICKIND_NAME
PublicKind_Name
NCHAR(10)
10
N
公共课的类型,如全校公共基础课
表名:
QB_QUESTIONS解释:
题目信息表
QB_QUESTIONS(QB_Questions)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
QUESTION_ID
Question_Id
INT
N
题目id
QUESTION_POINTID
Question_PointId
INT
N
知识点信息表中的知识点id
题目所属知识点的id
QUESTION_ORINGINID
Question_OringinId
INT
N
来源表中的来源id
题目的来源的id
QUESTION_LEVEL
Question_Level
SMALLINT
N
1
题目的难易度,分5个级别,数值越大代表题目越难
QUESTION_CODEID
Question_CodeId
INT
N
助记符id
QUESTION_KIND
Char(10)
题目类型
表名:
QB_STUDENTS解释:
学生信息表
QB_STUDENTS(QB_Students)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
STUDENT_ID
Student_Id
INT
N
学生学号
STUDENT_ACCOUNTID
Student_AccountId
INT
N
用户信息表中的用户id
学生的账号信息
STUDENT_COLLEGE
Student_College
INT
N
学院信息表中的学院id
学生所在学院
表名:
QB_TEACHERS解释:
教师信息表
QB_TEACHERS(QB_Teachers)
是否主键
字段名
字段描述
数据类型
长度
可空
约束
缺省值
备注
是
TEACHER_ID
Teacher_Id
INT
N
教师编号
TEACHER_ACCOUNTID
Teacher_AccountId
INT
N
用户表中的用户id
教师的账户信息
TEACHER_DESCRIPTION
Teacher_Description
NCHAR(100)
100
N
教师介绍
5、程序结构
整个系统的数据流大致如上图。
用户在客户端上进行操作时,某些需要用到数据库里的数据的操作,客户端就会编写对应的命令编号以及相应的参数,转换成字节流,经过加密发送到服务器。
服务器收到之后经过解密,并且转换回字符串,可以得到相应的命令和参数,根据这个可以进行对应的操作,操作之后返回的是一个布尔值的字符串或者整型数组、字符串数组、题目数组,转换为字节流之后发送给客户端,跟服务器一样接收之后解密转换得到原有的数组或字符串,再分别绑定到对应的控件上。
这是一次完整的客户端和服务器之间的数据传输。
Client:
客户端类,与服务器传输的代码。
Parse:
服务器返回的数据的转换。
Paper:
试卷的类。
Questios:
题目的类,里面有选择题、填空题、简答题继承题目类。
SecuritySystem:
加密解密。
Download:
下载文件,利用Tomcat进行传输。
Login:
登录Page。
MainWindow:
主界面,Grid加载Login。
SelectCourse:
选择科目的Page。
Look:
查看题目或试卷的Page。
AutoPaper:
自动组卷的窗口。
QLMain、QLMainStu:
老师、学生的界面,加载科目和查看的Page。
Detail:
题目详情窗口。
PaperDetailxaml:
试卷详情窗口。
BufferManager:
缓冲区管理器。
SecuritySystem:
加密解密。
Paper:
试卷的类。
Questios:
题目的类,里面有选择题、填空题、简答题继承题目类。
Parser:
客户端发送的数据的转换。
Server:
服务器类,与服务器传输的代码。
Function:
跟数据库打交道,利用SQL语句取数据。
Database:
数据库打开、链接等基本操作。
SocketAsyncEventArgsPool:
管理栈。
AsyncUserToken:
记录客户端信息的类。
Program:
程序入口。
6、关键功能及代码实现
6.1多并发量的实现
为了实现尽量多的并发量,一般不会使用阻塞的socket模型去编写服务器。
因为阻塞的socket操作,需要大量的线程,才能让每个客户都能及时的得到响应,因此cpu在进行频繁的线程切换的过程就浪费了不少cpu的资源。
所以必须要选择一个异步的socket模型,在windows中,IOCP(完成端口)是最适合用来实现网络服务器的,所以就选择IOCP。
在.net的平台中MSDN中对SocketAsyncEventArgs的描述是:
”SocketAsyncEventArgs类就是这一组增强功能的一部分。
该类专为需要高性能的网络服务器应用程序而设计。
应用程序可以完全使用增强的异步模式,也可以仅仅在目标热点区域(例如,在接收大量数据时)使用此模式。
”所以这次实现选择了SocketAsyncEventArgs类来实现服务器。
具体会有以下几个方面的实现
6.1.1实现SocketAsyncEventArgsPool(SocketAsyncEventArgs对象池)
这个对象池用于存放SocketAsyncEventArgs对象。
对象池中的对象都是预先分配的,而且也是可重用的,当有客户连接的时候,就在对象池中给客户取出一个对象使用。
当客户退出时就回收客户使用的SocketAsyncEventArgs对象,放回对象池当中,使用这个对象池的好处是在服务器运行阶段,减少了对象申请释放的操作。
对于多客户的连接中,就需要多次分配。
也为服务器运行的过程节省一些cpu时间。
主要代码如下,简单的实现对象池的效果,实际上就是用栈来管理对象
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Net.Sockets;
namespaceServer_
{
classSocketAsyncEventArgsPool
{
//SocketAsyncEventArgs管理栈
privateStackpool_;
publicSocketAsyncEventArgsPool(intcapacity)
{
pool_=newStack(capacity);
}
//回收没用的SocketAsyncEventArgs
publicvoidPush(SocketAsyncEventArgsitem)
{
if(item==null)
{
thrownewArgumentNullException("ItemsaddedtoaSocketAsyncEventArgsPoolcannotbenull");
}
lock(pool_)
{
pool_.Push(item);
}
}
//分配SocketAsyncEventArgs
publicSocketAsyncEventArgsPop()
{
lock(pool_)
{
returnpool_.Pop();
}
}
//返回可用SocketAsyncEventArgs的个数
publicintCount
{
get{returnpool_.Count;}
}
}
}
6.1.2实现缓冲区管理(BufferManager类)
自定义的一个类,因为在每一个SocketAsyncEventArgs对象中,都必须为其指定一个缓冲区。
用于数据接收(来自socket缓冲区)用于数据的发送(写进socket缓冲区)。
因此假设对于没分配一个SocketAsyncEventArgs对象就必须对应的为其分配一个缓冲区,所以将所有缓冲区也预先分配好,然后统一为SocketAsyncEventArgs指定,还有回收。
好处跟上述的对象池差不多。
classBufferManager
{
privateintnum_byte_;//大缓冲区总大小
privatebyte[]buffer_;//缓冲区数组
privateStackfree_index_pool_;//已回收的空闲的缓冲区
privateintcurrent_index_;//为分配缓冲区的下标
privateintbuffer_size_;//小缓冲区的大小
//构造函数
//totalBytes大缓冲区总的大小
//bufferSize小缓冲区的大小
publicBufferManager(inttotalBytes,intbufferSize)
{
num_byte_=totalBytes;
buffer_size_=bufferSize;
current_index_=0;
//用于保存已经回收的缓冲区的栈
free_index_pool_=newStack();
}
//申请空间初始化大缓冲区
publicboolinitBuffer()
{
buffer_=newbyte[num_byte_];
if(buffer_==null)
returnfalse;
returntrue;
}
//将缓冲区分配出去
//先从栈中,获取小缓冲区
//若栈空,就在当前下标下获取小缓冲区
publicboolsetBuffer(SocketAsyncEventArgsargs)
{
if(free_index_pool_.Count>0)
{
args.SetBuffer(buffer_,free_index_pool_.Pop(),buffer_size_);
}
else
{