构建多分校网校平台(三):考试题库高效组卷技术详解

随着在线教育的迅速发展,教育机构正在积极构建多分校网校平台,以实现资源共享、精细化运营和高效管理。在这种背景下,考试题库的管理和高效组卷技术成为教学平台中的关键环节之一。本文将详细探讨构建多分校网校平台中考试题库的高效组卷技术,帮助教育机构打造智能化、灵活化的考试系统。

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);
}

高效的考试题库组卷技术是构建多分校网校平台的重要组成部分。通过智能组卷算法、题库的结构化管理与动态更新,以及数据分析与在线模拟测试功能,教育机构可以有效提升考试组织效率、优化教学资源配置,并最终实现更好的教学效果。

未来,随着人工智能、大数据技术的进一步发展,考试题库管理与组卷技术将变得更加智能化和个性化,为教育行业带来更多创新与可能性。希望本文的技术详解能为教育机构提供有价值的参考,助力其在数字化转型的道路上取得更多成果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值