diff --git a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java index 4134fead..dcf01d10 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java @@ -314,9 +314,7 @@ public class ResourcesQuestion extends BaseEntity{ List result = new ArrayList<>(); - Field[] fields = ReflectUtil.getFields(this.getClass(), f -> f.getName().startsWith("questionOption")); - - for (Field field : fields) { + for (Field field : optionFields) { Object fieldValue = ReflectUtil.getFieldValue(this, field); if (fieldValue != null) { result.add(fieldValue.toString()); @@ -325,14 +323,17 @@ public class ResourcesQuestion extends BaseEntity{ return result; } + /** + * 选项的属性 + */ + private final static Field[] optionFields = ReflectUtil.getFields(ResourcesQuestion.class, f -> f.getName().startsWith("questionOption")); + /** * 查找为Null的题目选项字段 * @return */ private Field findNullQuestionOptionField() { - Field[] fields = ReflectUtil.getFields(this.getClass(), f -> f.getName().startsWith("questionOption")); - - for (Field field : fields) { + for (Field field : optionFields) { Object fieldValue = ReflectUtil.getFieldValue(this, field); if (fieldValue == null) { return field; @@ -346,16 +347,16 @@ public class ResourcesQuestion extends BaseEntity{ */ @UpdateIgnore @InsertIgnore - private List questionOptionList; + private List resourcesQuestionOptionList; - public List getQuestionOptionList() { + public List getResourcesQuestionOptionList() { List result = takeQuestionOptionConcatList(); - result.addAll(defaultIfNull(questionOptionList, Collections.emptyList())); + result.addAll(defaultIfNull(resourcesQuestionOptionList, Collections.emptyList())); return result; } - public void setQuestionOptionList(List questionOptionList) { - this.questionOptionList = questionOptionList; + public void setResourcesQuestionOptionList(List questionOptionList) { + this.resourcesQuestionOptionList = questionOptionList; } /** diff --git a/web/src/main/java/com/ibeetl/jlw/entity/vo/ImportQuestionByWordTemplateResultVO.java b/web/src/main/java/com/ibeetl/jlw/entity/vo/ImportQuestionByWordTemplateResultVO.java new file mode 100644 index 00000000..9efb0664 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/vo/ImportQuestionByWordTemplateResultVO.java @@ -0,0 +1,39 @@ +package com.ibeetl.jlw.entity.vo; + +import com.ibeetl.jlw.entity.ResourcesQuestion; +import lombok.Data; + +import java.util.List; + +/** + * 功能描述:
+ * word导入试题的结果类 + * 题目集合,返回的是基础类。ResourcesQuestion + * + * @author: mlx + * @description: + * @date: 2023/1/9 22:07 + * @version: 1.0 + */ +@Data +public class ImportQuestionByWordTemplateResultVO { + + /** + * 导入的题目标题 + */ + private String title; + /** + * 其他信息暂存,集合可能存在多条记录; + */ + private List otherMsg; + + /** + * 题目总数 + */ + private Integer questionCount; + + /** + * 题目列表 + */ + private List questionList; +} diff --git a/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java b/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java index 030ab8f5..ac55a2f6 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java @@ -15,6 +15,7 @@ import com.ibeetl.jlw.dao.CourseInfoDao; import com.ibeetl.jlw.dao.ResourcesQuestionDao; import com.ibeetl.jlw.entity.*; import com.ibeetl.jlw.entity.dto.QuestionSettingDTO; +import com.ibeetl.jlw.entity.vo.ImportQuestionByWordTemplateResultVO; import com.ibeetl.jlw.entity.vo.QuestionTypeCountVO; import com.ibeetl.jlw.enums.AddTypeEnum; import com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum; @@ -22,6 +23,7 @@ import com.ibeetl.jlw.service.strategy.StrategyContext; import com.ibeetl.jlw.validator.QuestionValidator; import com.ibeetl.jlw.web.query.CourseInfoQuery; import com.ibeetl.jlw.web.query.ResourcesQuestionQuery; +import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.util.Strings; @@ -53,6 +55,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -798,20 +801,18 @@ public class ResourcesQuestionService extends CoreBaseService * @Author: lx * @Date: 2023/1/8 22:49 */ - public JsonResult importQuestionByWordTemplate(@NotEmpty List fileEntityList, CoreUser coreUser) { + public ImportQuestionByWordTemplateResultVO importQuestionByWordTemplate(@NotEmpty List fileEntityList, CoreUser coreUser) { /** 找出段落, 逐段落处理 */ - AtomicReference inputStream = new AtomicReference<>(); AtomicReference xDocument = new AtomicReference<>(); - //装结果 key:卷子访问地址 value:{标题,题目List} - Map resultMap = new HashMap<>(); + ImportQuestionByWordTemplateResultVO resultVO = new ImportQuestionByWordTemplateResultVO(); /** 仅用于存放标题 */ final String[] title = { Strings.EMPTY }; /** 标题暂存,可能是多个 */ final StringBuilder titleStb = new StringBuilder(); /** 循环文件集合 */ - fileEntityList.forEach(fileEntity -> { + for (FileEntity fileEntity: fileEntityList) { /** 上传文件的临时路径 */ String importPath = fileEntity.getAbsoluteUrl(); /** 新建文件夹绝对路径 */ @@ -820,8 +821,8 @@ public class ResourcesQuestionService extends CoreBaseService String absolutePath = absoluteDirPath + fileEntity.getTempName(); //各种流 try { - inputStream.set(new FileInputStream(importPath)); - xDocument.set(new XWPFDocument(inputStream.get())); + @Cleanup FileInputStream fileInputStream = new FileInputStream(importPath); + xDocument.set(new XWPFDocument(fileInputStream)); /** 集合拷贝 */ List myParagraphs = BeanCopyUtil.copyListProperties(xDocument.get().getParagraphs() , MyXWPFParagraph::new, (t, d) -> { d.setXwpfParagraph(t); @@ -844,35 +845,39 @@ public class ResourcesQuestionService extends CoreBaseService /** 题干计数 */ AtomicInteger recordsCount = new AtomicInteger(); /** 遍历所有段落 */ - myParagraphs.forEach(e -> { + for (MyXWPFParagraph myParagraph : myParagraphs) { /** 策略中的参数设置 */ - strategyContext.setText(e.getText(absoluteDirPath, fileEntity.getTempName())).start(c -> { + Consumer> mapConsumer = item -> { /** 第一行 */ - if (recordsCount.get() == 1 && StringUtils.isBlank(title[0])) { + if (recordsCount.get() == 1 && StringUtils.isBlank(title[0])) { /** 暴力取值 */ titleStb.append(title[0] = myParagraphs.get(0).getText(absoluteDirPath, fileEntity.getTempName())); } /** k 策略枚举:v 处理后的段落文字 */ - c.forEach((k, v) -> { + item.forEach((k, v) -> { /** 记录段落类型, 未查询类型之外的类型 */ - if (!k.equals(OTHER)) {paragraphTypeEnum.set(k);} - switch(k) { + if (!k.equals(OTHER)) { + paragraphTypeEnum.set(k); + } + switch (k) { /** 未匹配到的段落 */ case OTHER: { /** 如果非标题字符,可能是前一个段落的副行。需要追加到相应的段落中 */ - insertCorrespondingParagraph(typeConcatEnum, paragraphTypeEnum.get() ,v, question, questionStem, questionAnswer); - } break; + insertCorrespondingParagraph(typeConcatEnum, paragraphTypeEnum.get(), v, question, questionStem, questionAnswer); + } + break; /** 题目类型,分值,该类型的总分 */ case TYPE: { /** 题目类型作用域 */ String[] sp = v.split(","); typeConcatEnum.set(StrategyContext.QuestionTypeConcatEnum.matchText(sp[2])); - questionScore.set(NumberUtil.toBigDecimal(sp[0]).setScale(1, RoundingMode.HALF_UP)); - } break; + questionScore.set(NumberUtil.toBigDecimal(sp[0]).setScale(1, RoundingMode.HALF_UP)); + } + break; /** 题干 */ case STEM: { final String numStartRegex = "^\\d+[\\.\\、\\.]"; - if(ReUtil.contains(numStartRegex, v)) { + if (ReUtil.contains(numStartRegex, v)) { /** 题干的编号需要移除掉 */ questionStem.set(v.replaceFirst(numStartRegex, Strings.EMPTY).trim()); /** 题数++ */ @@ -890,37 +895,36 @@ public class ResourcesQuestionService extends CoreBaseService resourcesQuestion.setUserId(coreUser.getId()); resourcesQuestion.setOrgId(coreUser.getOrgId()); - if (StringUtils.isNotBlank(questionStem.get())){ - resourcesQuestion.setQuestionStem(questionStem.get().replace("<","<").replace(">",">")); - }else { + if (StringUtils.isNotBlank(questionStem.get())) { + resourcesQuestion.setQuestionStem(questionStem.get().replace("<", "<").replace(">", ">")); + } else { resourcesQuestion.setQuestionStem(questionStem.get()); } question.set(resourcesQuestion); questionList.add(question.get()); } - } break; + } + break; /** 选项 */ case OPTION: { putQuestionOption(v, question.get()); - } break; + } + break; /** 答案 questionAnswer不包含答案: 字样 */ case ANSWER: { questionAnswer.set(questionAnswer.get().concat(v)); // questionAnswer.get() 默认不包含 【答案】 字样 questionAnswer.set(secondFormat(questionAnswer.get(), typeConcatEnum.get())); question.get().setQuestionAnswer(questionAnswer.get()); - } break; + } + break; } }); - }); - }); + }; - titleStb.append("
解析结果:"); - titleStb.append(recordsCount.get()); - titleStb.append(" 道题目"); - - String finalTitle = titleStb.toString(); + strategyContext.setText(myParagraph.getText(absoluteDirPath, fileEntity.getTempName())).start(mapConsumer); + } /** 最后检测属性, 并行处理,没有向外部非线程安全的变量写入东西,无需担心丢数据 */ questionList.parallelStream().forEach(item -> { @@ -932,15 +936,18 @@ public class ResourcesQuestionService extends CoreBaseService checkLocalQuestionsUnique(questionList); /** 拼成前端需要的数据 */ - resultMap.put(fileEntity.getUrl(), new Object[]{ finalTitle, questionList }); + resultVO.setTitle(titleStb.toString()); + resultVO.setOtherMsg(Collections.emptyList()); + resultVO.setQuestionCount(recordsCount.get()); + resultVO.setQuestionList(questionList); + } catch (Exception ex) { ex.printStackTrace(); } finally { - try {inputStream.get().close(); xDocument.get().close(); } + try {xDocument.get().close(); } catch (Exception ioe) { ioe.printStackTrace(); } } - }); - - return JsonResult.success(resultMap); + } + return resultVO; } /** 取选项的正则 */ @@ -960,10 +967,10 @@ public class ResourcesQuestionService extends CoreBaseService if(matcher.find()){ String questionOption = text.substring(2); // 在现有的列表中添加 - if(null != question.getQuestionOptionList()){ - List questionOptionList = question.getQuestionOptionList(); - questionOptionList.add(questionOption); - question.putQuestionOptionList(questionOptionList); + List optionList = question.getResourcesQuestionOptionList(); + if(null != optionList){ + optionList.add(questionOption); + question.putQuestionOptionList(optionList); } // 新建 else { diff --git a/web/src/main/java/com/ibeetl/jlw/validator/QuestionValidator.java b/web/src/main/java/com/ibeetl/jlw/validator/QuestionValidator.java index 3afe9421..95db599b 100644 --- a/web/src/main/java/com/ibeetl/jlw/validator/QuestionValidator.java +++ b/web/src/main/java/com/ibeetl/jlw/validator/QuestionValidator.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.util.Strings; import org.springframework.stereotype.Component; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static com.ibeetl.jlw.service.strategy.StrategyContext.QuestionTypeConcatEnum.*; @@ -95,7 +96,10 @@ public class QuestionValidator { * @return */ private String optionIncludeAnswer(ResourcesQuestion question, List questionOptionConcatList, String questionAnswer) { - if(ObjectUtil.isEmpty(questionOptionConcatList)) { + Integer questionType = question.getQuestionType(); + + // 单选和多选的选项不能为空 + if(Arrays.asList(SINGLE.getTypeConcat(), MULTIPLE.getTypeConcat()).contains(questionType) && ObjectUtil.isEmpty(questionOptionConcatList)) { return "选项为空"; } if(StringUtils.isBlank(questionAnswer)){ @@ -103,13 +107,13 @@ public class QuestionValidator { } // 是判断题 - if (DECIDE.getTypeConcat().equals(question.getQuestionType())) { + if (DECIDE.getTypeConcat().equals(questionType)) { // 答案只能是对或错,前后不能有空格 return !questionAnswer.matches("^[对错]$") ? "判断题答案有误": ""; } // 是单选题 - if (SINGLE.getTypeConcat().equals(question.getQuestionType())) { + if (SINGLE.getTypeConcat().equals(questionType)) { // 单选题只能有一个答案 return questionAnswer.length() != 1 ? "单选题只能有一个答案": ""; } diff --git a/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java b/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java index 7470cbd1..fb953ce1 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/ResourcesQuestionController.java @@ -13,6 +13,7 @@ import com.ibeetl.admin.core.web.JsonResult; import com.ibeetl.jlw.entity.CourseInfo; import com.ibeetl.jlw.entity.FileEntity; import com.ibeetl.jlw.entity.ResourcesQuestion; +import com.ibeetl.jlw.entity.vo.ImportQuestionByWordTemplateResultVO; import com.ibeetl.jlw.entity.vo.QuestionTypeCountVO; import com.ibeetl.jlw.enums.AddTypeEnum; import com.ibeetl.jlw.service.CourseInfoService; @@ -636,7 +637,7 @@ public class ResourcesQuestionController{ * 功能描述:
* word方式导入题目 * - * @param fileEntityList 文件列表 + * @param fileEntityList 文件列表 (前端传递name:file) * @param coreUser 传入的用户,使用机构ID和用户ID * @return {@link JsonResult} * @Author: lx @@ -644,8 +645,7 @@ public class ResourcesQuestionController{ */ @PostMapping(MODEL + "/importQuestionByWordTemplate.json") @ResponseBody - public JsonResult importQuestionByWordTemplate(@RFile List fileEntityList, @SCoreUser CoreUser coreUser) { - JsonResult jsonResult = resourcesQuestionService.importQuestionByWordTemplate(fileEntityList, coreUser); - return jsonResult; + public JsonResult importQuestionByWordTemplate(@RFile List fileEntityList, @SCoreUser CoreUser coreUser) { + return JsonResult.success(resourcesQuestionService.importQuestionByWordTemplate(fileEntityList, coreUser)); } }