1、设计背景
近日有用户提出需求,需要在原网站中实现一套视频学习模块及在线考试模块,在这里我先介绍以下在线考试模块的设计思路。
实现一个基本功能的在线考试系统,需要以下几个要素:
1、题库管理【可能需要针对不同的考试设计不同的题库,比如驾考科目一、道路运输资格证】
2、章节管理【对应的是考试培训资料的章节】
3、考题管理【题库中包含的所有考题】
4、考题选项【每一道题的选择项】
5、准确答案【每一道考题的准确答案】
6、考试管理【在网站上发布最新的考试连接,用户可以通过链接进入考试】
7、考试结果查看【完成考试后,实时查看考试成绩及错题】 实现以上功能后,基本上算是一个比较完整的考试系统了,接下来我们针对这些功能点进行表结构设计。
2、表结构设计
根据以上细化的内容,我们先设计以下数据库,这里直接贴图了,注释写的很清晰
3、部分功能的实现过程及关键代码片段
代码写的也比较乱,小功能也没讲究太多,将就看吧。
架构环境介绍:整体是基于jfinal_cms开发,以jfinal为基础mvc框架,模板引擎是beetl,前台还是传统的bootstrap和jquery
如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
3.1题库管理
这里比较简单,没什么逻辑
3.2题目管理
下面是设置正确选项的代码,这里单独加了多选题的处理逻辑,把准确选项的id用逗号分隔拼接后保存
private void set_right(TbExamAnswer model,TbExamQuestions questionsModel) {
if(questionsModel.getQuestionsType()!=111) {//单选和判断题准确答案设置
questionsModel.setRightAnswerId(model.getId().toString());
questionsModel.setRightAnswerTitle(model.getAnswerTitle());
}else {
List<TbExamAnswer> answerList=TbExamAnswer.dao.findByWhere(" where questions_id=? and answer_right=1 order by id asc",questionsModel.getId());
StringBuffer right_ids=new StringBuffer("");
StringBuffer right_title=new StringBuffer("");
for(int i=0;i<answerList.size();i++) {
TbExamAnswer answer=answerList.get(i);
right_ids.append(answer.getId().toString());
right_title.append(answer.getAnswerTitle());
if(i<answerList.size()-1) {
right_ids.append(",");
right_title.append(",");
}
}
questionsModel.setRightAnswerId(right_ids.toString());
questionsModel.setRightAnswerTitle(right_title.toString());
}
questionsModel.update();
}
3.3 考试发布
这里是考试发布后的前台页面,用户可以通过这个连接进入考试页面
考试发布操作需要设置基础信息题目总数字段用来控制每一个用户答题试卷的抽题数量,规则里需要设置每一章节的抽题数量,以下是抽题部分的代码。
public void index() {
// 数据列表
SysUser user = (SysUser) getSessionUser();
if (user == null) {
redirect(CommonController.firstPage);
return;
}
int examPublishId = getParaToInt();
TbExamPublish examPublish=TbExamPublish.dao.findById(examPublishId);
TbExamPaper paper=new TbExamPaper();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar nowTime=Calendar.getInstance();
if(examPublish.getExamTimeLength()==null) {
nowTime.add(Calendar.MINUTE, 60);
}else {
nowTime.add(Calendar.MINUTE, examPublish.getExamTimeLength());
}
paper.setUserId(user.getUserid());//用户id
paper.setUserName(user.getUserName());//用户名称
paper.setExamPublishId(examPublish.getId());//考试发布id
paper.setExamName(examPublish.getExamName());
paper.setExamCode(examPublish.getExamCode());//考试编号
paper.setExamStatus(1);//考试中
paper.setExamStartDate(sdf.format(new Date()));
paper.setExamEndDate(sdf.format(nowTime.getTime()));
paper.save();
List<TbExamPublishRule> ruleList =TbExamPublishRule.dao.findByWhere(" where exam_publish_id="+examPublish.getId());
String questionWhere="tb_exam_questions where tk_id=? and chapter_id=? and questions_type=? ORDER BY RAND() LIMIT ?";
for(int i=0;i<ruleList.size();i++) {
TbExamPublishRule rule=ruleList.get(i);
List<TbExamQuestions> singleChoiceQuestions=TbExamQuestions.dao.findByWhere(questionWhere, rule.getExamTkId(),rule.getExamChapterId(),109,rule.getSingleChoiceCount());//抽取单选题
List<TbExamQuestions> multipleChoiceQuestions=TbExamQuestions.dao.findByWhere(questionWhere, rule.getExamTkId(),rule.getExamChapterId(),111,rule.getMultipleChoiceCount());//抽取多选题
List<TbExamQuestions> judgeQuestions=TbExamQuestions.dao.findByWhere(questionWhere, rule.getExamTkId(),rule.getExamChapterId(),110,rule.getJudgeCount());//抽取判断题
singleChoiceQuestions.addAll(multipleChoiceQuestions);//合并单选和多选list
for(TbExamQuestions q:singleChoiceQuestions) {
TbExamPaperQuestions paperQuestion=new TbExamPaperQuestions();
paperQuestion.setPaperId(paper.getId());//试卷id
paperQuestion.setQuestionsId(q.getId());//题目id
paperQuestion.setQuestionsTitle(q.getQuestionsTitle());//题目
paperQuestion.setQuestionsType(q.getQuestionsType());//题目类型
paperQuestion.setRightAnswerId(q.getRightAnswerId());//准确选项id
paperQuestion.setRightAnswerTitle(q.getRightAnswerTitle());//准确选项标题
paperQuestion.save();
List<TbExamAnswer> answers=TbExamAnswer.dao.findByWhere(" where questions_id=? order by id asc", q.getId());//查询选项列表
for(TbExamAnswer a:answers) {
TbExamPaperAnswer paperAnswer=new TbExamPaperAnswer();
paperAnswer.setPaperId(paper.getId());//试卷id
paperAnswer.setQuestionsId(q.getId());//题目id
paperAnswer.setAnswerId(a.getId().toString());//选项id
paperAnswer.setAnswerTitle(a.getAnswerTitle());//选项标题
paperAnswer.setAnswerContent(a.getAnswerContent());//选项内容
paperAnswer.setAnswerRight(a.getAnswerRight()==null?null:a.getAnswerRight().toString());//是否正确答案
paperAnswer.save();
}
}
for(TbExamQuestions q:judgeQuestions) {
TbExamPaperQuestions paperQuestion=new TbExamPaperQuestions();
paperQuestion.setPaperId(paper.getId());//试卷id
paperQuestion.setQuestionsId(q.getId());//题目id
paperQuestion.setQuestionsTitle(q.getQuestionsTitle());//题目
paperQuestion.setQuestionsType(q.getQuestionsType());//题目类型
paperQuestion.setRightAnswerId(q.getRightAnswerId());//准确选项id
paperQuestion.setRightAnswerTitle(q.getRightAnswerTitle());//准确选项标题
paperQuestion.save();
}
//BeanUtils.copyProperties(q,choiceQuestions.get(0));
//log.info(rule.getExamChapterName()+ " 选择题--"+choiceQuestions.size()+"判断题--"+judgeQuestions.size());
}
redirect("/front/onlineExam/toExam/"+paper.getId());
return;
}
public void toExam() {
log.info("开始考试啦");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
int paperId = getParaToInt();
TbExamPaper paper=TbExamPaper.dao.findById(paperId);//试卷实体
List<TbExamPaperQuestions> questionsList=TbExamPaperQuestions.dao.findByWhere(" where paper_id=? order by questions_type desc", paperId);//查询题目列表
Map<Integer,List<TbExamPaperAnswer>> answerMap=new HashMap<Integer,List<TbExamPaperAnswer>>();//试题选项map
for(TbExamPaperQuestions q:questionsList) {
List<TbExamPaperAnswer> answers=TbExamPaperAnswer.dao.findByWhere(" where paper_id=? and questions_id=?", paperId,q.getQuestionsId());
answerMap.put(q.getQuestionsId(), answers);
}
//try {
Date d=paper.getExamEndDate();
setAttr("examEndDate",sdf1.format(d));
setAttr("paper",paper);
setAttr("questionsList",questionsList);
setAttr("answerMap",answerMap);
renderAuto(path + "exam_page.html");
}
以下是考试页面,实现方式主要是遍历所以试题,根据题目类型渲染不同的dom标签。
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/84ccf4e896324bccae9382d0dc8775ca~tplv-k3u1fbpfcp-watermark.image)
这个js方法主要是为了在答题过程中,实时保存答题数据,防止页面刷新时丢失数据
function commitAnswer(paperId,questionsId,questionsType,answerId,domName){
if(questionsType==111){
var selAnswerId="";
$("input:checkbox[name='"+domName+"']:checked").each(function(index) { // 遍历name=test的多选框
size=$("input:checkbox[name='"+domName+"']:checked").length;
if(index==size-1){
selAnswerId+=$(this).val(); // 每一个被选中项的值
}else{
selAnswerId+=$(this).val()+","; // 每一个被选中项的值
}
});
answerId=selAnswerId;
}
var url = '${BASE_PATH }front/onlineExam/saveAnswer';
$.ajax({
type:'POST',
dataType:'json',
data:{
"paperId":paperId,
"questionsId":questionsId,
"questionsType":questionsType,
"answerId":answerId
},
url:url,
success:function(data){
//$("#chapter_id").empty();
//for(var i=0;i<data.length;i++){
// var item=data[i];
// $("#chapter_id").append("<option value='"+item.id+"'>"+item.chapterName+"</option>");
//}
},
error:function(html){
}
});
}
这段代码是确认提交后,计算分数的方法,逻辑很简单,就通过比对答题选项与准确答案选项
public void commitPaper() {
int paperId = getParaToInt();
int score=0;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TbExamPaper paper=TbExamPaper.dao.findById(paperId);//试卷实体
List<TbExamPaperQuestions> list=TbExamPaperQuestions.dao.findByWhere(" where paper_id=?",paperId);
for(int i=0;i<list.size();i++) {
TbExamPaperQuestions q=list.get(i);
if(q.getRightAnswerId()==null)continue;
if(q.getRightAnswerId().equals(q.getUserAnswerId()))score++;
}
int examTimeLength=getBetweenMinutes(paper.getExamStartDate(),new Date());
paper.setExamScore(score);
paper.setExamTimeLength(examTimeLength);
paper.setExamEndDate(sdf.format(new Date()));
paper.setExamStatus(-1);//置为交卷状态
paper.update();
log.info("总分数"+score);
redirect("/front/onlineExam/viewPaperResult/"+paper.getId());
}
这里是考试结果页面,页面中可以显示答对题目及错题
这里是管理员查看考试列表的页面,可以完善筛选条件或其他个性功能,这里只实现了查询功能
至此,基本完成了一套完整的在线考试系统,但是从设计思路上还是代码实现上都有诸多问题,在这里抛砖引玉一下,如果大家有更好的思路也可以在评论区一起交流。
最后
粉丝福利:以下是Java面试1—到5年以上开发必问到的面试问点,也都是一线互联网公司Java面试必备技能,下面是参照阿里年薪50W所需具备的技能图,大家可以参考下!
同时针对这12个技能,我在这整理了一份Java架构进阶面试专题PDF文档(含450题解析,包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发,设计模式,MySQL等知识点解析,内容丰富,图文结合!)
这份专题文档是免费分享的,有需要的朋友可以看向下面来获取!!
需要完整版文档的小伙伴,可以一键三连,下方获取免费领取方式!