欢迎加入知识星球
帮助你学习编程,带你少走弯路,少踩坑,一起学习、一起交流、一起做项目的知识交流圈
星球目前是88一年,前100名有10元优惠卷
今天主要给大家带来新的功能,题库功能
所谓题库功能,主要就是保存我们的题目信息,我们在考试的时候就会用到题库,需要从题库中抽取题库组成一套试卷;或者我们进行刷题训练的时候也会冲题库中抽取题目信息等等地方都会用到
功能展示
从上面我们可以看出来,我们的功能已全部完成,题目类型主要分为单选题、多选题、填空题、解答题、判断题
主要功能点:
主要技术点:
- 使用工厂模式+策略模式实现不同类型题目的添加或修改
- 导入导出功能,导出到多个sheet或者多个sheet导入数据
针对以上的内容将一一进行讲解
功能技术讲解
分页查询题目
功能展示
查询条件:
- 学院
- 专业
- 课程
- 题目内容
- 题目类型
- 题目难度
const searchParam = reactive({
page: 1,
size: 10,
content: '',
type: '',
collegeUuid: '',
majorUuid: '',
courseUuid: '',
difficultyType: ''
})
主要的sql语句
<select id="pageListToManager"
parameterType="com.codedancer.campuslink.mapper.qo.QuestionPageSearchQo"
resultMap="questionResult">
select q.uuid AS q_uuid,
q.content AS q_content,
q.course_uuid AS q_course_uuid,
q.difficulty_type AS q_difficulty_type,
q.type AS q_type,
q.score AS q_score,
q.create_time AS q_create_time,
q.update_time AS q_update_time
from question AS q
left join course AS c on c.uuid = q.course_uuid
left join major AS m on m.uuid = c.major_uuid
left join college AS c2 on c2.uuid = m.college_uuid
<where>
<if test="questionPageSearchQo.content != null and questionPageSearchQo.content != ''">
and q.content like "%"#{questionPageSearchQo.content}"%"
</if>
<if test="questionPageSearchQo.type != null">
and q.type = #{questionPageSearchQo.type}
</if>
<if test="questionPageSearchQo.difficultyType != null">
and q.difficulty_type =#{questionPageSearchQo.difficultyType}
</if>
<if
test="questionPageSearchQo.collegeUuid != null and questionPageSearchQo.collegeUuid != ''">
and c2.uuid = #{questionPageSearchQo.collegeUuid}
</if>
<if test="questionPageSearchQo.majorUuid != null and questionPageSearchQo.majorUuid != ''">
and m.uuid = #{questionPageSearchQo.majorUuid}
</if>
<if test="questionPageSearchQo.courseUuid != null and questionPageSearchQo.courseUuid != ''">
and c.uuid = #{questionPageSearchQo.courseUuid}
</if>
and q.deleted = 0
</where>
order by q.create_time DESC
</select>
查询知识点很少,只需要基本的sql能力即可解决
添加题目
题目的添加我们针对到具体的课程,每个学院以及每个专业下的课程题目都是不一样的
工厂模式+策略模式
创建QuestionFactory工厂
@Component
public class QuestionFactory implements InitializingBean, ApplicationContextAware {
private ApplicationContext applicationContext;
private static final Map<QuestionType, QuestionCreateHandler> QUESTION_HANDLER_MAP = new HashMap<>();
/**
* 根据题目类型获取对应的处理器
*
* @param type
* @return
*/
public QuestionCreateHandler getQuestionCreateHandler(Integer type) {
if (Objects.equals(RADIO.getValue(), type)) {
// 单选题
return QUESTION_HANDLER_MAP.get(RADIO);
} else if (Objects.equals(MULTIPLE_CHOICE.getValue(), type)) {
// 多选题
return QUESTION_HANDLER_MAP.get(MULTIPLE_CHOICE);
} else if (Objects.equals(PACK.getValue(), type)) {
// 填空题
return QUESTION_HANDLER_MAP.get(PACK);
} else if (Objects.equals(SOLUTION.getValue(), type)) {
// 解答题
return QUESTION_HANDLER_MAP.get(SOLUTION);
} else if (Objects.equals(JUDGEMENT.getValue(), type)) {
// 判断题
return QUESTION_HANDLER_MAP.get(JUDGEMENT);
} else {
throw new BusinessException("题目类型不正确!");
}
}
@Override
public void afterPropertiesSet() throws Exception {
applicationContext.getBeansOfType(QuestionCreateHandler.class).values().forEach(bean -> {
QUESTION_HANDLER_MAP.put(bean.getType(), bean);
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
我们从上面可以看出配置了五种题目类型,根据不同的类型获取到你不同的处理器
题目创建处理器
@Component
public interface QuestionCreateHandler {
/**
* 添加题目
*
* @param questionCreateParamModel
*/
void addQuestion(QuestionCreateParamModel questionCreateParamModel);
/**
* 题目类型
*
* @return
*/
QuestionType getType();
/**
* 修改题目
*
* @param questionCreateParamModel
*/
void updateQuestion(QuestionCreateParamModel questionCreateParamModel);
/**
* 导入题目
*
* @param questionCreateParamModels
*/
void importQuestion(List<QuestionCreateParamModel> questionCreateParamModels);
}
定义了四个接口:
- 添加题目
- 题目类型
- 修改题目
- 导入题目
处理器具体实现
- 单选题处理实现 QuestionRadionHandlerImpl
- 多选题处理实现 QuestionMultipleChoiceHandlerImpl
- 填空题处理实现 QuestionPackHandlerImpl
- 解答题处理实现 QuestionSolutionHandlerImpl
- 判断题处理实现 QuestionJudgementHandlerImpl
单选题
功能展示
在这里我们也设计成了单选题该有的内容:
- 题目类型
- 题目内容(题目标题)
- 题目分数
- 四个选项内容
- 正确选项
- 单选解析内容
参数
const addOrUpdateParam = reactive({
collegeUuid: '',
majorUuid: '',
difficultyType: '',
content: '',
type: '',
analysisContent: '',
isRight: '',
rightAnswer: '',
rightOptions: [],
options: [],
optionA: '',
optionB: '',
optionC: '',
optionD: '',
radioRightOption: '',
score: ''
});
我们把五种类型的题目参数全部放在这个对象里面方便我们进行管理
主要逻辑
Question question = new Question();
question.setContent(questionCreateParamModel.getContent());
question.setScore(questionCreateParamModel.getScore());
question.setAnalysisContent(questionCreateParamModel.getAnalysisContent());
List<Map<String, String>> optionsMap = Lists.newArrayList();
IntStream.range(0, questionCreateParamModel.getOptions().size()).forEach(index -> {
Map<String, String> map = new HashMap<>();
map.put(optionsArray[index], questionCreateParamModel.getOptions().get(index));
optionsMap.add(map);
});
question.setOptions(optionsMap);
question.setRightOptions(questionCreateParamModel.getRightOptions());
question.setCourseUuid(questionCreateParamModel.getCourseUuid());
QuestionType type = QuestionType.fromValue(questionCreateParamModel.getType());
if (ObjectUtil.isEmpty(type)) {
throw new BusinessException("题目类型不正确,请检查");
}
question.setType(type);
QuestionDifficultyType difficultyType = QuestionDifficultyType.fromValue(
questionCreateParamModel.getDifficultyType());
if (ObjectUtil.isEmpty(difficultyType)) {
throw new BusinessException("题目难度参数不正确,请检查");
}
question.setDifficultyType(difficultyType);
questionMapper.insert(question);
多选题
功能展示
这里我们设计的多选题也是默认四个选项,没有新增选项,可以在正确选项中选择多个选项来作为正确答案
参数
和单选题一致
主要逻辑
Question question = new Question();
question.setContent(questionCreateParamModel.getContent());
question.setScore(questionCreateParamModel.getScore());
question.setAnalysisContent(questionCreateParamModel.getAnalysisContent());
List<Map<String, String>> optionsMap = Lists.newArrayList();
IntStream.range(0, questionCreateParamModel.getOptions().size()).forEach(index -> {
Map<String, String> map = new HashMap<>();
map.put(optionsArray[index], questionCreateParamModel.getOptions().get(index));
optionsMap.add(map);
});
question.setOptions(optionsMap);
question.setRightOptions(questionCreateParamModel.getRightOptions());
question.setCourseUuid(questionCreateParamModel.getCourseUuid());
QuestionType type = QuestionType.fromValue(questionCreateParamModel.getType());
if (ObjectUtil.isEmpty(type)) {
throw new BusinessException("题目类型不正确,请检查");
}
question.setType(type);
QuestionDifficultyType difficultyType = QuestionDifficultyType.fromValue(
questionCreateParamModel.getDifficultyType());
if (ObjectUtil.isEmpty(difficultyType)) {
throw new BusinessException("题目难度参数不正确,请检查");
}
question.setDifficultyType(difficultyType);
questionMapper.insert(question);
填空题
功能展示
这里就和单选题或多选题不一致的地方,该有的内容:
- 题目类型
- 题目难度
- 题目内容
- 题目分数
- 正确答案
- 解析内容
参数
和单选题一致
主要逻辑
Question question = new Question();
question.setContent(questionCreateParamModel.getContent());
question.setScore(questionCreateParamModel.getScore());
question.setAnalysisContent(questionCreateParamModel.getAnalysisContent());
question.setRightAnswer(questionCreateParamModel.getRightAnswer());
question.setCourseUuid(questionCreateParamModel.getCourseUuid());
QuestionType type = QuestionType.fromValue(questionCreateParamModel.getType());
if (ObjectUtil.isEmpty(type)) {
throw new BusinessException("题目类型不正确,请检查");
}
question.setType(type);
QuestionDifficultyType difficultyType = QuestionDifficultyType.fromValue(
questionCreateParamModel.getDifficultyType());
if (ObjectUtil.isEmpty(difficultyType)) {
throw new BusinessException("题目难度参数不正确,请检查");
}
question.setDifficultyType(difficultyType);
questionMapper.insert(question);
解答题
完全和填空题一致
判断题
功能展示
判断题和以上的题米都不一致,该有的内容:
- 题目类型
- 题目难度
- 题目内容
- 题目分数
- 是否正确
- 解析内容
参数
和单选题一致
主要逻辑
Question question = new Question();
question.setContent(questionCreateParamModel.getContent());
question.setScore(questionCreateParamModel.getScore());
question.setAnalysisContent(questionCreateParamModel.getAnalysisContent());
question.setIsRight(questionCreateParamModel.getIsRight());
question.setCourseUuid(questionCreateParamModel.getCourseUuid());
QuestionType type = QuestionType.fromValue(questionCreateParamModel.getType());
if (ObjectUtil.isEmpty(type)) {
throw new BusinessException("题目类型不正确,请检查");
}
question.setType(type);
QuestionDifficultyType difficultyType = QuestionDifficultyType.fromValue(
questionCreateParamModel.getDifficultyType());
if (ObjectUtil.isEmpty(difficultyType)) {
throw new BusinessException("题目难度参数不正确,请检查");
}
question.setDifficultyType(difficultyType);
questionMapper.insert(question);
修改题目
和添加题目大致一样,具体可以详见代码
导入题目
题目的导入也是该项目的亮点,我们使用的多个sheet作为不同类型的题目,我们主要在指定的工作区进行添加题目即可
我们就开始来探索这个导入功能
使用的maven依赖
<!-- easypoi -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
这里我们使用的easypoi第三方工具来进行支持
读取文件配置参数
Workbook workBook = ExcelUtil.getWorkBook(file);
// 执行循环导入
int numberOfSheets = workBook.getNumberOfSheets();
ImportParams params = new ImportParams();
循环读取不同的sheet来进行不同的处理逻辑
公共校验
// 校验学院
College college = collegeMapper.findByColumn("name",
radioQuestionImportExcel.getCollegeName())
.orElseThrow(() -> new BusinessException(
"单选题中第" + radioQuestionImportExcel.getRowNum() + "行错误是:【"
+ radioQuestionImportExcel.getCollegeName() + "】所属学院不存在"));
// 校验专业
Major major = majorMapper.getByCollegeUuidAndMajorName(college.getUuid(),
radioQuestionImportExcel.getMajorName());
if (ObjectUtil.isEmpty(major)) {
String errorMsg =
"单选题中第" + radioQuestionImportExcel.getRowNum() + "行错误是:所属学院【"
+ college.getName() + "】下【"
+ radioQuestionImportExcel.getMajorName()
+ "】专业不存在,请检查";
throw new BusinessException(errorMsg);
}
// 校验课程
Course course = courseMapper.getByMajorUuidAndCourseName(major.getUuid(),
radioQuestionImportExcel.getCourseName());
if (ObjectUtil.isEmpty(course)) {
String errorMsg =
"单选题中第" + radioQuestionImportExcel.getRowNum() + "行错误是:所属专业【"
+ major.getName() + "】下【"
+ radioQuestionImportExcel.getCourseName()
+ "】课程不存在,请检查";
throw new BusinessException(errorMsg);
}
// 校验难度类型
QuestionDifficultyType questionDifficultyType = QuestionDifficultyType.fromName(
radioQuestionImportExcel.getDifficultyType());
if (ObjectUtil.isEmpty(questionDifficultyType)) {
String errorMsg =
"单选题中第" + radioQuestionImportExcel.getRowNum() + "行错误是:难度类型【"
+ radioQuestionImportExcel.getDifficultyType() + "不正确,请检查";
throw new BusinessException(errorMsg);
}
导入单选题逻辑
// 转化数据
QuestionCreateParamModel questionCreateParamModel = new QuestionCreateParamModel();
questionCreateParamModel.setContent(radioQuestionImportExcel.getContent());
questionCreateParamModel.setCourseUuid(course.getUuid());
questionCreateParamModel.setScore(radioQuestionImportExcel.getScore());
questionCreateParamModel.setType(QuestionType.RADIO.getValue());
questionCreateParamModel.setDifficultyType(questionDifficultyType.getValue());
questionCreateParamModel.setAnalysisContent(
radioQuestionImportExcel.getAnalysisContent());
List<String> options = Lists.newArrayList();
options.add(radioQuestionImportExcel.getOptionA());
options.add(radioQuestionImportExcel.getOptionB());
options.add(radioQuestionImportExcel.getOptionC());
options.add(radioQuestionImportExcel.getOptionD());
questionCreateParamModel.setOptions(options);
// 将正确选项用,分割开
String[] split = radioQuestionImportExcel.getRightOptions().split(",");
if (split.length != 1) {
String errorMsg = "单选题中第" + radioQuestionImportExcel.getRowNum()
+ "行错误是:正确选项只能是一个,请检查";
throw new BusinessException(errorMsg);
}
List<String> rightOptions = Arrays.asList(split);
questionCreateParamModel.setRightOptions(rightOptions);
log.info("单选题转化后的数据:{}", questionCreateParamModel);
questionCreateParamModels.add(questionCreateParamModel);
}
QuestionCreateHandler questionCreateHandler = questionFactory.getQuestionCreateHandler(
sheetQuestionType.getValue());
questionCreateHandler.importQuestion(questionCreateParamModels);
导入多选题逻辑
// 转化数据
QuestionCreateParamModel questionCreateParamModel = new QuestionCreateParamModel();
questionCreateParamModel.setContent(
multipleChoiceQuestionImportExcel.getContent());
questionCreateParamModel.setCourseUuid(course.getUuid());
questionCreateParamModel.setScore(multipleChoiceQuestionImportExcel.getScore());
questionCreateParamModel.setType(QuestionType.MULTIPLE_CHOICE.getValue());
questionCreateParamModel.setDifficultyType(questionDifficultyType.getValue());
questionCreateParamModel.setAnalysisContent(
multipleChoiceQuestionImportExcel.getAnalysisContent());
List<String> options = Lists.newArrayList();
options.add(multipleChoiceQuestionImportExcel.getOptionA());
options.add(multipleChoiceQuestionImportExcel.getOptionB());
options.add(multipleChoiceQuestionImportExcel.getOptionC());
options.add(multipleChoiceQuestionImportExcel.getOptionD());
questionCreateParamModel.setOptions(options);
// 将正确选项用,分割开
String[] split = multipleChoiceQuestionImportExcel.getRightOptions().split(",");
if (split.length != 1) {
String errorMsg =
"多选题中第" + multipleChoiceQuestionImportExcel.getRowNum()
+ "行错误是:正确选项只能是一个,请检查";
throw new BusinessException(errorMsg);
}
List<String> rightOptions = Arrays.asList(split);
questionCreateParamModel.setRightOptions(rightOptions);
log.info("多选题转化后的数据:{}", questionCreateParamModel);
questionCreateParamModels.add(questionCreateParamModel);
}
QuestionCreateHandler questionCreateHandler = questionFactory.getQuestionCreateHandler(
sheetQuestionType.getValue());
questionCreateHandler.importQuestion(questionCreateParamModels);
导入填空题逻辑
// 转化数据
QuestionCreateParamModel questionCreateParamModel = new QuestionCreateParamModel();
questionCreateParamModel.setContent(
packQuestionImportExcel.getContent());
questionCreateParamModel.setCourseUuid(course.getUuid());
questionCreateParamModel.setScore(packQuestionImportExcel.getScore());
questionCreateParamModel.setType(QuestionType.PACK.getValue());
questionCreateParamModel.setDifficultyType(questionDifficultyType.getValue());
questionCreateParamModel.setAnalysisContent(
packQuestionImportExcel.getAnalysisContent());
questionCreateParamModel.setRightAnswer(
packQuestionImportExcel.getRightAnswer());
log.info("填空题转化后的数据:{}", questionCreateParamModel);
questionCreateParamModels.add(questionCreateParamModel);
}
QuestionCreateHandler questionCreateHandler = questionFactory.getQuestionCreateHandler(
sheetQuestionType.getValue());
questionCreateHandler.importQuestion(questionCreateParamModels);
导入解答题逻辑
// 转化数据
QuestionCreateParamModel questionCreateParamModel = new QuestionCreateParamModel();
questionCreateParamModel.setContent(
solutionQuestionImportExcel.getContent());
questionCreateParamModel.setCourseUuid(course.getUuid());
questionCreateParamModel.setScore(solutionQuestionImportExcel.getScore());
questionCreateParamModel.setType(QuestionType.SOLUTION.getValue());
questionCreateParamModel.setDifficultyType(questionDifficultyType.getValue());
questionCreateParamModel.setAnalysisContent(
solutionQuestionImportExcel.getAnalysisContent());
questionCreateParamModel.setRightAnswer(
solutionQuestionImportExcel.getRightAnswer());
log.info("解答题转化后的数据:{}", questionCreateParamModel);
questionCreateParamModels.add(questionCreateParamModel);
}
QuestionCreateHandler questionCreateHandler = questionFactory.getQuestionCreateHandler(
sheetQuestionType.getValue());
questionCreateHandler.importQuestion(questionCreateParamModels);
导入判断题逻辑
// 转化数据
QuestionCreateParamModel questionCreateParamModel = new QuestionCreateParamModel();
questionCreateParamModel.setContent(
judgementQuestionImportExcel.getContent());
questionCreateParamModel.setCourseUuid(course.getUuid());
questionCreateParamModel.setScore(judgementQuestionImportExcel.getScore());
questionCreateParamModel.setType(QuestionType.JUDGEMENT.getValue());
questionCreateParamModel.setDifficultyType(questionDifficultyType.getValue());
questionCreateParamModel.setAnalysisContent(
judgementQuestionImportExcel.getAnalysisContent());
questionCreateParamModel.setIsRight(
judgementQuestionImportExcel.getIsRight().equals("是"));
log.info("判断题转化后的数据:{}", questionCreateParamModel);
questionCreateParamModels.add(questionCreateParamModel);
}
QuestionCreateHandler questionCreateHandler = questionFactory.getQuestionCreateHandler(
sheetQuestionType.getValue());
questionCreateHandler.importQuestion(questionCreateParamModels);
以上就是全部类型的题目导入功能逻辑,想看详细了解请查看加入知识星球
导出题目
题目的导出功能我们还是通过easypoi来实现的,具体功能代码
log.info("开始导出题目数据,导出时间:{}", DateUtil.now());
QuestionPageSearchQo questionPageSearchQo = BeanUtil.copyProperties(questionPageSearchBo,
QuestionPageSearchQo.class);
List<Question> questions = questionMapper.listExportData(questionPageSearchQo);
// 筛选出单选题
List<Question> radioQuestions = questions.stream()
.filter(question -> question.getType() == QuestionType.RADIO).collect(
Collectors.toList());
// 筛选出多选题
List<Question> multipleChoiceQuestions = questions.stream()
.filter(question -> question.getType() == QuestionType.MULTIPLE_CHOICE).collect(
Collectors.toList());
// 筛选出填空题
List<Question> packQuestions = questions.stream()
.filter(question -> question.getType() == QuestionType.PACK).collect(
Collectors.toList());
// 筛选出解答题
List<Question> solutionQuestions = questions.stream()
.filter(question -> question.getType() == QuestionType.SOLUTION).collect(
Collectors.toList());
// 筛选出判断题
List<Question> judgementQuestions = questions.stream()
.filter(question -> question.getType() == QuestionType.JUDGEMENT).collect(
Collectors.toList());
// 组装单选数据
List<RadioQuestionExportExcel> radioQuestionExportExcels = assembleRadioData(
radioQuestions);
// 组装多选数据
List<MultipleChoiceQuestionExportExcel> multipleChoiceQuestionExportExcels = assembleMultipleChoiceData(
multipleChoiceQuestions);
// 组装填空数据
List<PackQuestionExportExcel> packQuestionExportExcels = assemblePackData(packQuestions);
// 组装解答数据
List<SolutionQuestionExportExcel> solutionQuestionExportExcels = assembleSolutionData(
solutionQuestions);
// 组装判断数据
List<JudgementQuestionExportExcel> judgementQuestionExportExcels = assembleJudgementData(
judgementQuestions);
List<Map<String, Object>> sheetsList = new ArrayList<>();
// 创建参数对象
ExportParams radioParams = new ExportParams();
radioParams.setSheetName("单选题");
ExportParams multipleChoiceParams = new ExportParams();
multipleChoiceParams.setSheetName("多选题");
ExportParams packParams = new ExportParams();
packParams.setSheetName("填空题");
ExportParams solutionParams = new ExportParams();
solutionParams.setSheetName("解答题");
ExportParams judgementParams = new ExportParams();
judgementParams.setSheetName("判断题");
Map<String, Object> radioMap = new HashMap<>();
radioMap.put("title", radioParams);
radioMap.put("entity", RadioQuestionExportExcel.class);
radioMap.put("data", radioQuestionExportExcels);
Map<String, Object> multipleChoiceMap = new HashMap<>();
multipleChoiceMap.put("title", multipleChoiceParams);
multipleChoiceMap.put("entity", MultipleChoiceQuestionExportExcel.class);
multipleChoiceMap.put("data", multipleChoiceQuestionExportExcels);
Map<String, Object> packMap = new HashMap<>();
packMap.put("title", packParams);
packMap.put("entity", PackQuestionExportExcel.class);
packMap.put("data", packQuestionExportExcels);
Map<String, Object> solutionMap = new HashMap<>();
solutionMap.put("title", solutionParams);
solutionMap.put("entity", SolutionQuestionExportExcel.class);
solutionMap.put("data", solutionQuestionExportExcels);
Map<String, Object> judgementMap = new HashMap<>();
judgementMap.put("title", judgementParams);
judgementMap.put("entity", JudgementQuestionExportExcel.class);
judgementMap.put("data", judgementQuestionExportExcels);
sheetsList.add(radioMap);
sheetsList.add(multipleChoiceMap);
sheetsList.add(packMap);
sheetsList.add(solutionMap);
sheetsList.add(judgementMap);
Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
ExcelUtil.downLoadExcel("题目信息" + DateUtil.format(new Date(), "yyyy-MM-dd") + ".xls",
response,
workbook);
log.info("题目数据导出完成,导出完成时间:{}", DateUtil.now());
总结
以上就是本次题库的全部功能,实现的东西也有很多,主要是在工厂模式+策略模式的使用,怎么做到代码优雅,那就是设计模式啦,
以及题目的导入导出功能,主要实现多sheet的导入和导出,使用easypoi实现其功能。
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。