随着在线教育的迅速发展,教育机构正在积极构建多分校网校平台,以实现资源共享、精细化运营和高效管理。在这种背景下,考试题库的管理和高效组卷技术成为教学平台中的关键环节之一。本文将详细探讨构建多分校网校平台中考试题库的高效组卷技术,帮助教育机构打造智能化、灵活化的考试系统。
1. 题库的结构化管理
题库的结构化管理是实现高效组卷的基础。通过对题目进行分类、标签化、难度分级等处理,可以更好地组织和调用题库资源。具体措施包括:
- 题目分类与标签:为每道题目设置分类和标签,如科目、章节、知识点、难度等级等。这样在组卷时,可以快速筛选出符合要求的题目。
- 难度分级:将题目按照难度等级划分为不同级别,方便根据考试需求选择合适的题目。
- 题目版本控制:在题库中引入版本控制机制,确保题目在更新和修改时,不会影响已生成的试卷。
2. 智能组卷算法
智能组卷算法是实现高效组卷的核心技术。该算法需要考虑题目的多样性、难度均衡、知识点覆盖率等因素,以生成符合考试要求的试卷。常用的组卷算法包括:
- 贪心算法:根据预设的规则(如难度、题型比例),逐步选择符合条件的题目,直到满足组卷要求。贪心算法的优点是简单高效,但在应对复杂需求时可能存在局限性。
- 遗传算法:通过模拟生物进化过程,生成多组试卷方案,并通过交叉、变异等操作,不断优化试卷的质量。遗传算法适合用于需要综合考虑多种因素的复杂组卷场景。
- 动态规划算法:动态规划算法通过分阶段解决问题,逐步构建最优解,适合用于题目选择过程中需要考虑多个约束条件的情况。
3. 开发代码
前端代码示例:
<!--
* @Description:
* @version:
* @Author: 李云飞
* @Date: 2021-08-26 12:04:11
* @LastEditors: 李云飞
* @LastEditTime: 2021-08-29 16:14:02
-->
<template>
<a-modal title="添加试题"
v-model="isShowModal"
:destroyOnClose="true"
width="960px"
@ok="handleOk">
<div class="content">
<a-form-model :label-col="{ span: 2 }"
:wrapper-col="{ span: 22 }">
<a-form-model-item label="题型">
<a-radio-group v-model="formData.liftingType">
<a-radio v-for="item in LIFTING_TYPE_LIST"
:key="item.value"
:value="item.value" :disabled="pageType == 1">{{item.desc}}</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="章" v-if="source != 'paper'">
<j-search-select-tag placeholder="请做出你的选择" v-model="formData.parentSectionId" :dictOptions="zList" @change="getjList" style="width: 300px">
</j-search-select-tag>
</a-form-model-item>
<a-form-model-item label="节" v-if="source != 'paper'">
<j-search-select-tag placeholder="请做出你的选择" v-model="formData.sectionId" :dictOptions="jList" style="width: 300px">
</j-search-select-tag>
</a-form-model-item>
<a-form-model-item label="主观题型" v-if="formData.liftingType == 5">
<j-search-select-tag placeholder="请做出你的选择" @change="topicCategoryChange" v-model="topicCategoryVal" :dictOptions="topicCategory" style="width: 300px">
</j-search-select-tag>
</a-form-model-item>
<a-form-model-item label="题干">
<a-input type="textarea" :maxLength="1000"
:auto-size="{ minRows: 4, maxRows: 6 }" v-model="formData.stem"/>
</a-form-model-item>
<a-form-model-item label="题型材料">
<a-radio-group v-model="formData.materialType">
<a-radio v-for="item in MATERIAL_TYPE_LIST"
:key="item.value"
:value="item.value">{{item.desc}}</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="难易度" v-if="formData.liftingType==1 || formData.liftingType==2 || formData.liftingType==5 || formData.liftingType==6">
<a-radio-group v-model="formData.facilityValue">
<a-radio v-for="item in Facility_Value_LIST"
:key="item.value"
:value="item.value">{{item.desc}}</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item v-if="formData.materialType"
label="材料选择">
<template v-if="formData.materialType === 1">
<!-- <MediaUpload :mediaType="formData.materialType"
@change="changeMaterial">
</MediaUpload> -->
<a-row>
<a-col :span="8">
<j-image-upload isMultiple :number="1" text="本地上传" v-model="formData.dataList[0].imageUrl" ></j-image-upload>
(建议图片尺寸:750*420)
</a-col>
<a-col :span="6">
<div class="div-resources" @click="resourceSelect" >
<span class="ant-upload-resources">
<i aria-label="图标: plus" class="anticon-resources" >
<svg viewBox="64 64 896 896" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false">
<path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path>
<path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path>
</svg>
</i>
<div>资源库选取</div>
</span>
</div>
</a-col>
</a-row>
</template>
<template v-if="formData.materialType === 2">
<a-row>
<a-col :span="6">
<div>{{formData.dataList[0].name}}</div>
<div class="div-resources" @click="resourceSelect" >
<span class="ant-upload-resources">
<i aria-label="图标: plus" class="anticon-resources" >
<svg viewBox="64 64 896 896" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false">
<path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path>
<path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path>
</svg>
</i>
<div>资源库选取</div>
</span>
</div>
</a-col>
</a-row>
</template>
<template v-if="formData.materialType === 3">
<MediaUpload :mediaType="formData.materialType"
@change="changeMaterial">
</MediaUpload>
</template>
</a-form-model-item>
<!-- 单选题 && 完形填空 -->
<SingleQuestion ref="SingleQuestion" v-if="formData.liftingType === 1 || formData.liftingType === 6"></SingleQuestion>
<!-- 多选题 -->
<MultipleQuestion ref="MultipleQuestion" v-if="formData.liftingType === 2"></MultipleQuestion>
<!-- 共用题干 && 阅读理解A && 阅读理解B -->
<ShareStemQuestion ref="ShareStemQuestion" v-if="formData.liftingType === 3 || formData.liftingType === 7 || formData.liftingType === 8"></ShareStemQuestion>
<!-- 共用选项 -->
<ShareOptionsQuestion ref="ShareOptionsQuestion" v-if="formData.liftingType === 4"></ShareOptionsQuestion>
<!-- 主管题 -->
<SubjectiveQuestion ref="SubjectiveQuestion" v-if="formData.liftingType === 5"></SubjectiveQuestion>
<wd-resources-material-modal ref="modalTable" @selectedObj="selectedObj" @selectedImage="selectedImage"></wd-resources-material-modal>
</a-form-model>
</div>
</a-modal>
</template>
<script>
import WdResourcesMaterialModal from '@/views/admin/course/coursevideo/modules/WdResourcesMaterialModal'
import MediaUpload from '@/components/MediaUpload'
import SingleQuestion from './components/SingleQuestion'
import MultipleQuestion from './components/MultipleQuestion'
import ShareStemQuestion from './components/ShareStemQuestion'
import ShareOptionsQuestion from './components/ShareOptionsQuestion'
import SubjectiveQuestion from './components/SubjectiveQuestion'
import { LIFTING_TYPE_LIST } from '@/constants/question'
import { findByPid, addQuestions, getSubQuery, editSubQuery, addPaperQuestions, getPaperSubQuery, editPaperSub} from "@api/question";
import {getDictItemsFromCache} from "@api/api";
export default {
name: 'AddEditQuestionModal',
components: {
MediaUpload,
SingleQuestion,
MultipleQuestion,
ShareStemQuestion,
ShareOptionsQuestion,
SubjectiveQuestion,
WdResourcesMaterialModal
},
props: {},
data () {
return {
topicCategoryVal: null,
topicCategory: getDictItemsFromCache("topic_category"),
destroyOnClose: true,
pageType: null,//0新增1修改
pageId: '',
model:{
courseVideo: {
name: ''
}
},
courseVideoImgPath: '', //题型材料图片
zList:[],
jList:[],
queryData: {
majorId: null,
chapterName: null,
tzId:null,
tjId:null
},
isShowModal: false,
// 试题类型枚举 1256单试题,3478多试题
LIFTING_TYPE_LIST,
// 材料类型枚举
MATERIAL_TYPE_LIST: [
{
value: null,
desc: '无'
},
{
value: 1,
desc: '图片'
},
{
value: 2,
desc: '视频'
}
],
Facility_Value_LIST: [
{
value: 1,
desc: '简单'
},
{
value:2,
desc: '中'
},
{
value:3,
desc: '难'
},
{
value: 4,
desc: '困难'
}
],
source: null,//来源: 章节练习 || 试卷管理
formData: {
liftingType: 1, // 试题类型
facilityValue: 1, //难易度
stem: '', // 题干内容
materialType: null, // 试题材料类型
dataList: [{}], // 试题材料列表
score: null, // 试题分值
// 选项列表
optionList: [],
parentId: null, // 共享题干是时子题下面的父id
answer: '', // 答案解析
answerTxt: '', // 答案解析富文本
selectList: [{}], // 解析视频图片
// 子试题
questionList: [],
parentSectionId: '',
sectionId: '',
}
};
},
computed: {},
filters: {},
created () {
console.log(this.LIFTING_TYPE_LIST)
},
mounted () {
},
methods: {
topicCategoryChange() {
console.log(this.topicCategoryVal)
console.log(this.topicCategory)
},
selectedImage(data) {
this.formData.dataList[0].imageUrl = data
this.formData.dataList[0].materialType = 1
this.formData.dataList[0].type = 2
},
selectedObj(data) {
if(this.formData.materialType == 2) {
this.formData.dataList[0].videoUrl = data.material
this.formData.dataList[0].materialType = 2
this.formData.dataList[0].type = 2
// this.formData.dataList[0].name = data.name
this.$set(this.formData.dataList[0],'name',data.name)
console.log(data)
console.log(this.formData)
}
},
resourceSelect() {
this.$nextTick(()=>{
if(this.formData.materialType == 1) this.$refs.modalTable.init()
else if(this.formData.materialType == 2) this.$refs.modalTable.init2()
});
},
showModal (templateId, paperId, subId, type) {
//templateId:章节模板ID,id:修改项id,subId: 试题ID, type:修改1 新增0
console.log(templateId);
this.findByPid (templateId);
if(type == 1) {
this.pageId = subId
this.pageType = type
getPaperSubQuery(subId).then(res => {
console.log(res)
if(res.code == 200) {
this.setData(res.result)
}
}).catch()
} else {
this.pageId = ''
this.pageType = null
this.formData.parentSectionId = null
this.formData.sectionId = null
this.formData = {
liftingType: 1, // 试题类型
facilityValue: 1, //难易度
stem: '', // 题干内容
materialType: null, // 试题材料类型
dataList: [{}], // 试题材料列表
score: null, // 试题分值
// 选项列表
optionList: [],
parentId: null, // 共享题干是时子题下面的父id
answer: '', // 答案解析
answerTxt: '', // 答案解析富文本
selectList: [{}], // 解析视频图片
// 子试题
questionList: [],
}
this.MATERIAL_TYPE_LIST = [
{
value: null,
desc: '无'
},
{
value: 1,
desc: '图片'
},
{
value: 2,
desc: '视频'
}
]
}
this.isShowModal = true
},
setData(data) {
console.log(data)
let bodysss;
data.liftingType = data.liftingType*1
this.formData.liftingType = data.liftingType
this.formData.parentSectionId = data.parentSectionId
this.formData.sectionId = data.sectionId
if(data.dataList && data.dataList[0].materialType) {
data.materialType = data.dataList[0].materialType*1
} else {
data.dataList = [{
imageUrl: '',
videoUrl: '',
}]
data.materialType = null
}
if(data.liftingType == 1 || data.liftingType == 2 || data.liftingType == 5 || data.liftingType == 6){
this.$nextTick(() => {
if(data.liftingType == 1 || data.liftingType == 6) bodysss = this.$refs.SingleQuestion;
if(data.liftingType == 2) bodysss = this.$refs.MultipleQuestion;
if(data.liftingType == 5) bodysss = this.$refs.SubjectiveQuestion;
if(data.selectList && data.selectList[0]) {
bodysss.model.courseVideo = data.selectList[0]
} else {
bodysss.model.courseVideo = {}
this.formData.selectList = [{}]
}
if(data.liftingType != 5) {
bodysss.options = data.optionList
if(data.optionList) {
data.optionList.forEach((item, index) => {
item.rightFlag = item.rightFlag == 1 ? false : true
})
}
}
bodysss.score = data.score*1
// bodysss.answer = data.answer
bodysss.answerTxt = data.answerTxt
})
} else if(data.liftingType == 3 || data.liftingType == 4 || data.liftingType == 7 || data.liftingType == 8) {
console.log(data, 123123)
this.$nextTick(() => {
if(data.liftingType == 3 || data.liftingType == 7 || data.liftingType == 8) bodysss = this.$refs.ShareStemQuestion;
if(data.liftingType == 4) bodysss = this.$refs.ShareOptionsQuestion;
if(data.questionList && data.questionList.length > 0) {
data.questionList.forEach(item => {
if(item.optionList && item.optionList.length > 0) {
item.optionList.forEach(optItem => {
optItem.rightFlag = optItem.rightFlag == 1 ? false : true
})
}
})
bodysss.tags = data.questionList
}
})
}
this.getjList();
this.formData = data
this.topicCategoryVal = this.formData.topicCategory;
},
//通过专业查询章
async findByPid (templateId) {
try {
const result = await findByPid(templateId)
this.zList = result.result || [];
} catch (err) {
} finally {
}
},
//通过章查询节
async getjList () {
try {
const result = await findByPid(this.formData.parentSectionId)
if(this.pageType != 1) this.formData.sectionId=null;
this.jList = result.result || [];
} catch (err) {
} finally {
}
},
// 选择题干材料改变
changeMaterial (file) {
console.log(file)
},
handleOk () {
let bodys;
if(this.formData.liftingType == 1 || this.formData.liftingType == 6) bodys = this.$refs.SingleQuestion
if(this.formData.liftingType == 2) bodys = this.$refs.MultipleQuestion
if(this.formData.liftingType == 3 || this.formData.liftingType == 7 || this.formData.liftingType == 8) bodys = this.$refs.ShareStemQuestion
if(this.formData.liftingType == 4) bodys = this.$refs.ShareOptionsQuestion
if(this.formData.liftingType == 5) bodys = this.$refs.SubjectiveQuestion
this.formData.templateId = this.formData.sectionId
this.formData.dataList[0]['materialType'] = this.formData.materialType
this.formData.optionList = []
if(this.formData.liftingType == 1 || this.formData.liftingType == 2 || this.formData.liftingType == 5 || this.formData.liftingType == 6) {
if(this.formData.liftingType != 5) {
try {
bodys.options.forEach((item,index) => {
this.formData.optionList.push(item)
})
} catch(e) {
}
}
this.formData.score = bodys.score
if(bodys.model.courseVideo.material) {
this.formData.selectList[0].videoUrl = bodys.model.courseVideo.material
}
if(bodys.model.courseVideo.video) {
this.formData.selectList[0].videoUrl = bodys.model.courseVideo.video
}
this.formData.selectList[0].type = 1
this.formData.selectList[0].name = bodys.model.courseVideo.name
// this.formData.answer = bodys.answer
this.formData.answerTxt = bodys.answerTxt
}
if(this.formData.liftingType == 3 || this.formData.liftingType == 4 || this.formData.liftingType == 7 || this.formData.liftingType == 8){
try {
bodys.tags.forEach((item, index) => {
if(!item.stem) throw new Error('请完善所有题目');
// if(!item.answer) throw new Error('请完善所有解析答案');
if(!item.answerTxt) throw new Error('请完善所有解析答案');
/*if(!item.dataList || item.dataList.length<1 || !item.dataList[0].videoUrl) throw new Error('请完善所有解析视频');*/
item.optionList.forEach((IOItem, IOIdx) => {
if(!IOItem.content) throw new Error('请完善所有选项');
})
})
} catch (err) {
console.log(err)
this.$message.warning(err.message)
return
}
}
console.log(this.formData)
if(!this.formData.liftingType) {
this.$message.warning("请选择题型")
return
}
if(!this.formData.parentSectionId) {
this.$message.warning("请选择章")
return
}
if(!this.formData.sectionId) {
this.$message.warning("请选择节")
return
}
if(this.formData.liftingType == 5 && !this.topicCategoryVal) {
this.$message.warning("请选择主观题型")
return
}
if(!this.formData.stem) {
this.$message.warning("请输入题干")
return
}
if(this.formData.materialType == 1) {
if(!this.formData.dataList[0]['imageUrl']) {
this.$message.warning("请上传题型材料")
return
}
}
if(this.formData.materialType == 2) {
if(!this.formData.dataList[0]['videoUrl']) {
this.$message.warning("请上传题型材料")
return
}
}
if(this.formData.liftingType == 1 || this.formData.liftingType == 2 || this.formData.liftingType == 5 || this.formData.liftingType == 6){
if(this.formData.liftingType !== 5) {
console.log(this.formData)
try {
this.formData.optionList.forEach(item => {
if(!item.content || item.content == ''){
throw Error();
}
})
} catch(err) {
this.$message.warning("请完善选项")
return
}
if(this.formData.liftingType == 1 || this.formData.liftingType == 2 || this.formData.liftingType == 6) {
try {
let successNum = 0;
this.formData.optionList.forEach(item => {
if(item.rightFlag == true){
successNum++
}
})
if(successNum<1) throw Error();
} catch(err) {
this.$message.warning("至少保留一个正确答案")
return
}
}
}
/*if(!this.formData.answer) {
this.$message.warning("请输入答案解析")
return
}*/
if(!this.formData.answerTxt) {
this.$message.warning("请输入答案解析")
return
}
// if(!this.formData.selectList[0].videoUrl) {
// this.$message.warning("请选择解析视频")
// return
// }
}
if(this.formData.liftingType == 3 || this.formData.liftingType == 4 || this.formData.liftingType == 7 || this.formData.liftingType == 8){
let tags = JSON.parse(JSON.stringify(bodys.tags));
try {
tags.forEach((item, index) => {
let numb = 0
item.optionList.forEach((IOItem, IOIdx) => {
if(IOItem.rightFlag == true) numb++
})
if(numb < 1) throw Error();
})
} catch (err) {
this.$message.warning("每道试题至少保留一个正确答案")
return
}
try {
tags.forEach((item, index) => {
item.optionList.f
后端代码示例:
@AutoLog(value = "视频课程-分页列表查询")
@ApiOperation(value="视频课程-分页列表查询", notes="视频课程-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(WdCourseVideo wdCourseVideo,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<WdCourseVideo> queryWrapper = QueryGenerator.initQueryWrapper(wdCourseVideo, req.getParameterMap());
queryWrapper.orderByDesc("sort").orderByDesc("create_time").orderByDesc("id");
Page<WdCourseVideo> page = new Page<WdCourseVideo>(pageNo, pageSize);
IPage<WdCourseVideo> pageList = wdCourseVideoService.page(page, queryWrapper);
for (WdCourseVideo record : page.getRecords()) {
if(record.getCourseType() == 1){
try{
record.setClassHour(""+setClassHour(record.getId(), record.getTemplateId(), 1));
wdCourseVideoService.updateById(record);
}catch (Exception e){
System.out.println("计算课时有误");
}
}
}
for (WdCourseVideo record : pageList.getRecords()) {
//查询绑定的标签id
record.setLabelIds(wdCourseLabelRelationService.selectCourseLabelIds(record.getId()));
record.setLabelNames(wdCourseLabelRelationService.selectCourseLabelNames(record.getId()));
//查询是否售卖
record.setIsSale(1);
WdCourseSale courseSale = wdCourseSaleService.getOne(new LambdaQueryWrapper<WdCourseSale>().eq(WdCourseSale::getCourseId, record.getId()));
if (courseSale!=null){
record.setIsSale(courseSale.getIsSale());
}
}
return Result.OK(pageList);
}
高效的考试题库组卷技术是构建多分校网校平台的重要组成部分。通过智能组卷算法、题库的结构化管理与动态更新,以及数据分析与在线模拟测试功能,教育机构可以有效提升考试组织效率、优化教学资源配置,并最终实现更好的教学效果。
未来,随着人工智能、大数据技术的进一步发展,考试题库管理与组卷技术将变得更加智能化和个性化,为教育行业带来更多创新与可能性。希望本文的技术详解能为教育机构提供有价值的参考,助力其在数字化转型的道路上取得更多成果。