diff --git a/web/src/main/java/cn/jlw/util/QuestionUtil.java b/web/src/main/java/cn/jlw/util/QuestionUtil.java new file mode 100644 index 00000000..9b4d37d2 --- /dev/null +++ b/web/src/main/java/cn/jlw/util/QuestionUtil.java @@ -0,0 +1,169 @@ +package cn.jlw.util; + +import cn.hutool.core.lang.Range; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.ObjectUtil.defaultIfNull; +import static java.util.stream.Collectors.joining; + + +/** + * 选择题选项实体, 部分参数封装 + * 题目选项随机工具 + * + * @author mlx + */ +@Slf4j +public class QuestionUtil { + + /** + * 定义字母区间 + */ + private static Range LETTER_RANGE = new Range<>("A", "Z", (current, end, index) -> { + char c = current.toCharArray()[0]; + if (c == end.toCharArray()[0]) { + return null; + } + return String.valueOf(++c); + }); + + /** + * 横表结构转纵表结构 + * 题目选项获取 + * + * @param obj 需要保证传入的题目,类型必须是1单选,2多选,3判断这三种, + * 其他的不支持,因为可能会没有选项值。 + * @param stemFieldName 题干属性名 + * @param optionFieldPrefixName 选项属性名前缀 + * @param trueAnswerFieldName 答案的属性名 + * @param questionTypeFieldName 题型的属性名 + * @return + */ + public static Collection convertToRandomOrderQuestionList(T obj, final String stemFieldName, + final String optionFieldPrefixName, final String trueAnswerFieldName, final String questionTypeFieldName) { + // 不取父类的属性 + Field[] fieldsDirectly = ReflectUtil.getFieldsDirectly(obj.getClass(), false); + // 正确答案 值(字母值) + String trueAnswer = defaultIfNull(ReflectUtil.getFieldValue(obj, trueAnswerFieldName), "").toString(); + // 题干 值 + String questionStem = defaultIfNull(ReflectUtil.getFieldValue(obj, stemFieldName), "").toString(); + String questionType = defaultIfNull(ReflectUtil.getFieldValue(obj, questionTypeFieldName), "").toString(); + // 选项的属性 + Field[] optionFields = ArrayUtil.filter(fieldsDirectly, field -> field.getName().contains(optionFieldPrefixName)); + + // 使用Map的无序性,进行重排序 + Map Kv = new HashMap<>(6); + // 选项字段遍历 + for (Field optionField : optionFields) { + String optionText = defaultIfNull(ReflectUtil.getFieldValue(obj, optionField), "").toString(); + // 为空跳出 + if (StrUtil.isBlank(optionText)) { + continue; + } + String optionName = optionField.getName(); + QuestionInfo questionInfo = QuestionInfo.builder().questionStem(questionStem) + .questionType(EnumUtil.likeValueOf(ResourcesQuestionTypeEnum.class, Integer.valueOf(questionType))) + .optionText(optionText).isTrue(trueAnswer.contains(optionName)) + .build(); + Kv.put(optionName, questionInfo); + } + + // 这里需要先重置,以防万一 + LETTER_RANGE.reset(); + // 设置选项的字母 + Kv.forEach((k, v) -> { + v.setOptionLetter(LETTER_RANGE.next()); + }); + + return Kv.values(); + } + + /** + * 填充一些属性到指定的实体类中 + * @param collection 转换后的集合 + * @param t + * @param stemFieldName + * @param optionFieldPrefixName + * @param trueAnswerFieldName + * @return + * @param + * @throws IllegalAccessException + * @throws InstantiationException + */ + public static void fullSomeField(Collection collection, T t, final String stemFieldName, + final String optionFieldPrefixName, final String trueAnswerFieldName) { + + try { + LETTER_RANGE.reset(); + // 正确答案 + String answer = collection.stream().filter(QuestionInfo::getIsTrue) + .map(QuestionInfo::getOptionLetter).sorted().collect(joining(",")); + // 选项的属性名字 + String[] optionNames = collection.stream() + .map(item -> optionFieldPrefixName.concat(item.getOptionLetter())).toArray(String[]::new); + + QuestionInfo[] questionInfos = collection.toArray(new QuestionInfo[]{}); + for (int i = 0; i < questionInfos.length; i++) { + QuestionInfo questionInfo = questionInfos[i]; + ReflectUtil.setFieldValue(t, stemFieldName, questionInfo.getQuestionStem()); + ReflectUtil.setFieldValue(t, trueAnswerFieldName, answer); + for (int j = 0; j < optionNames.length; j++) { + ReflectUtil.setFieldValue(t, optionNames[j], questionInfos[i].getOptionText()); + } + } + }catch (Exception e) { + log.debug(e.getMessage()); + } + } + + /** + * 随机打乱题目的选项 + * @param t + * @param stemFieldName + * @param optionFieldPrefixName + * @param trueAnswerFieldName + * @param + */ + public static void shuffleQuestion(T t, final String stemFieldName, + final String optionFieldPrefixName, final String trueAnswerFieldName, final String questionTypeFieldName) { + Collection questionInfos = convertToRandomOrderQuestionList(t, stemFieldName, optionFieldPrefixName, trueAnswerFieldName, questionTypeFieldName); + fullSomeField(questionInfos, t, stemFieldName, optionFieldPrefixName, trueAnswerFieldName); + } + + @Data + @Builder + public static class QuestionInfo { + /** + * 题干 + */ + private String questionStem; + /** + * 题目选项 + */ + private String optionText; + /** + * 选项字母 + */ + private String optionLetter; + /** + * 是否正确答案 + */ + private Boolean isTrue; + /** + * 题型 + */ + private ResourcesQuestionTypeEnum questionType; + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java index 4b297949..709d3035 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java @@ -5,8 +5,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; -import com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum; -import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; @@ -23,6 +21,7 @@ import static org.apache.commons.lang3.StringUtils.upperCase; * * @author mlx */ +@Deprecated public class ResourcesQuestionOptionEntity { @@ -76,11 +75,13 @@ public class ResourcesQuestionOptionEntity { /** * 随机打乱选项的顺序, 支持所有题型的处理 + * 废弃 * * @param answerFieldName 答案的字段名,只能是逗号分割 * @param prefix 选项的属性前缀 questionOption * @param lastIsUppercase 最后一个字段是否大写 */ + @Deprecated public static void shuffleOrderOptions(Object obj, final String answerFieldName, final String prefix, Boolean lastIsUppercase) { String questionAnswer = (String)ReflectUtil.getFieldValue(obj, answerFieldName); Assert.notBlank(questionAnswer, "答案不能为空!"); @@ -128,8 +129,8 @@ public class ResourcesQuestionOptionEntity { // 答案和选项ABCDEF..对应 Map answerMap = new HashMap<>(6); // 判断题目是否有重复的值 -// Assert.isTrue(optionTextList.size() == lastLetterFieldNameList.size(), -// "答案中有重复的值,请检查!"); + Assert.isTrue(optionTextList.size() == lastLetterFieldNameList.size(), + "答案中有重复的值,请检查!"); // 乱序集合处理 for (int i = 0; i < lastLetterFieldNameList.size(); i++) { @@ -175,51 +176,4 @@ public class ResourcesQuestionOptionEntity { Field[] fields = ReflectUtil.getFieldsDirectly(clazz, false); verifyFieldExists(fields, answerFieldName, prefix); } - - /** - * 横表结构转纵表结构 - * 题目选项获取 - * @param obj - * @param stemFieldName 题干属性名 - * @param optionFieldName 选项属性名 - * @param trueOptionFieldName 答案的属性名 - * @param lastIsUppercase 选项属性名是否结尾大写 - * @return - */ -// public static List getQuestionList(Object obj, final String stemFieldName, -// final String optionFieldName, -// final String trueOptionFieldName, Boolean lastIsUppercase) { -// ReflectUtil.getFields(obj, field -> { -// field.getName(). -// }) -// } - - @Data - public static class QuestionInfo { - - /** - * 题干 - */ - private String questionStem; - - /** - * 题目选项 - */ - private String optionText; - - /** - * 选项字母 - */ - private String optionLetter; - - /** - * 是否正确答案 - */ - private boolean isTrue; - - /** - * 题型 - */ - private ResourcesQuestionTypeEnum questionType; - } } diff --git a/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java b/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java index 47d7414f..8295c8bd 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java @@ -563,7 +563,7 @@ public class GeneralQuestionLogService extends CoreBaseService()); + resourcesQuestionSnapshots = defaultIfNull(resourcesQuestionSnapshots, new ArrayList<>()); // 选项乱序(限单选、多选) if (TRUE_CONST.equals(hwSetting.getGeneralQuestionSettingOptionNoOrder())) { // 单题选项排序处理 resourcesQuestionSnapshots.forEach(value -> { - shuffleOrderOptions(value, "questionAnswer","questionOption", true); + shuffleQuestion(value, "questionStem","questionOption", + "questionAnswer", "questionType"); }); } diff --git a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java index 75cdd0ef..2ebba624 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java @@ -45,6 +45,7 @@ import static cn.hutool.core.util.ArrayUtil.join; import static cn.jlw.util.CacheUserUtil.getStudent; import static com.ibeetl.admin.core.util.user.CacheUserUtil.getUserId; import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.FINALLY_SUBMIT; +import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.PRE_SUBMIT; import static com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum.EXAM; import static java.util.stream.Collectors.groupingBy; @@ -369,12 +370,14 @@ public class TeacherOpenCourseQuestionLogService extends CoreBaseService { -// shuffleOrderOptions(value, "questionAnswer","questionOption", true); +// shuffleQuestion(value, "questionStem","questionOption", +// "questionAnswer", "questionType"); }); } diff --git a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java index e5198d4e..273d239c 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java @@ -117,6 +117,7 @@ public class TeacherOpenCourseQuestionLogController { @PostMapping(API + "/addQuestionLog.do") @ResponseBody public JsonResult addQuestionLog(@RequestBody QuestionLogAddDTO questionLogAddDTO, @SCoreUser CoreUser coreUser) { + Assert.notNull(coreUser, "请登录后再操作"); Assert.isTrue(coreUser.isStudent(), "非学生身份,无法提交!"); teacherOpenCourseQuestionLogService.insertQuestionLog(questionLogAddDTO); return JsonResult.success();