diff --git a/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java b/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java index 1425460b..7787299a 100644 --- a/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java +++ b/admin-core/src/main/java/com/ibeetl/admin/core/util/BeanUtil.java @@ -5,12 +5,14 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.json.JSONUtil; import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMapAdapter; import java.lang.reflect.Field; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -34,8 +36,13 @@ public class BeanUtil extends cn.hutool.core.bean.BeanUtil { public static Map beanToMapStr(Object obj) { Map result = new ConcurrentHashMap<>(16); beanToMap(obj).forEach((s, o) -> { - if (o instanceof Date) { + if (o == null) { + result.put(s, ""); + } else if (o instanceof Date) { result.put(s, DateUtil.date((Date) o).toString()); + } else if(o instanceof List) { + List list = (List)o; + result.put(s, ObjectUtil.isEmpty(list)? "": JSONUtil.toJsonStr(o)); } else { result.put(s, String.valueOf(o)); } diff --git a/admin-test/src/main/java/com/ibeetl/admin/test/util/test/RandomUtils.java b/admin-test/src/main/java/com/ibeetl/admin/test/util/test/RandomUtils.java index 6a6d7044..e4644281 100644 --- a/admin-test/src/main/java/com/ibeetl/admin/test/util/test/RandomUtils.java +++ b/admin-test/src/main/java/com/ibeetl/admin/test/util/test/RandomUtils.java @@ -136,7 +136,7 @@ public class RandomUtils { public static String getRandomString(List list, int randomLength, Function function, CharSequence delimiter) { Objects.requireNonNull(list); // 取随机数长度的集合 - List questionList = RandomUtil.randomEleList(list, RandomUtil.randomInt(min(list.size(), randomLength))); + List questionList = RandomUtil.randomEleList(list, RandomUtil.randomInt(min(list.size() == 0 ? 1: list.size(), randomLength))); return questionList.stream() .map(e -> String.valueOf(function.apply(e))) 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 e3024a7c..6e319f71 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestion.java @@ -1,17 +1,11 @@ package com.ibeetl.jlw.entity; -import cn.hutool.core.lang.Assert; -import cn.jlw.util.EnumUtil; import cn.jlw.validate.ValidateConfig; import com.ibeetl.admin.core.entity.BaseEntity; -import lombok.Getter; import org.beetl.sql.annotation.entity.AutoID; import javax.validation.constraints.NotNull; import java.math.BigDecimal; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /* * 资源管理 - 题库管理 @@ -85,62 +79,7 @@ public class ResourcesQuestion extends BaseEntity{ public ResourcesQuestion(){ } - public enum AnswerEnum { - // TODO mlx 题目乱序 - A("getQuestionOptionA"), - B("getQuestionOptionB"), - C("getQuestionOptionC"), - D("getQuestionOptionD"), - E("getQuestionOptionE"); - - @Getter - private String methodName; - - AnswerEnum(String methodName) { - this.methodName = methodName; - } - - /** - * 通过题目的选项获取属性的方法 - * @param name 单个选项 - * @return - */ - public static String getMethodNameByEnumName(String name) { - List methodNameObjList = EnumUtil.getFieldValues(AnswerEnum.class, "methodName"); - - for (AnswerEnum value : values()) { - if (value.name().equalsIgnoreCase(name)) { - return value.getMethodName(); - } - } - throw new RuntimeException("未获取到选项对应的方法名!"); - } - - /** - * 通过题目的选项获取属性的方法 - * @param names 选项的ABCD集合 - * @return - */ - public static Map getMethodNameByEnumName(String... names) { - Map nameAndMethod = new HashMap<>(6); - for (AnswerEnum value : values()) { - for (String name : names) { - if (value.name().equalsIgnoreCase(name)) { - nameAndMethod.put(name, value.getMethodName()); - } - } - } - return Assert.notEmpty(nameAndMethod, "未获取到选项对应的方法名!"); - } - } - - /** - * 随机打乱选项的顺序 - */ - public void ShuffleOrderOptions() { - - } /**ID diff --git a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java new file mode 100644 index 00000000..ce093582 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionOptionEntity.java @@ -0,0 +1,139 @@ +package com.ibeetl.jlw.entity; + +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 java.lang.reflect.Field; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.apache.commons.lang3.StringUtils.join; +import static org.apache.commons.lang3.StringUtils.upperCase; + + +/** + * 选择题选项实体, 部分参数封装 + * 题目选项随机工具 + * + * @author mlx + */ +public class ResourcesQuestionOptionEntity { + + private Map optionTextMap = new HashMap(6); + + public void put(String optionText, Boolean value) { + optionTextMap.put(optionText, value); + } + + public Boolean get(String optionText) { + return optionTextMap.getOrDefault(optionText, false); + } + + public boolean remove(String optionText) { + return optionTextMap.remove(optionText); + } + + public boolean contains(String optionText) { + return optionTextMap.containsKey(optionText); + } + + /** + * 获取是正确答案的列表,无序 + * + * @return + */ + public List getOptionTextList() { + List arrayList = new ArrayList(optionTextMap.keySet()); + Collections.shuffle(arrayList); + return Collections.unmodifiableList(arrayList); + } + + /** + * 获取乱序后的ABCDE答案 + * + * @param answerMap <题目,ABCD..> + * @return + */ + public List getAnswerList(Map answerMap) { + // 最后的答案 + List answerList = new CopyOnWriteArrayList<>(); + answerMap.forEach((optionText, newAnswer) -> { + optionTextMap.forEach((optionText2, isAnswer) -> { + if (optionText2.equals(optionText) && isAnswer) { + answerList.add(newAnswer); + } + }); + }); + return answerList; + } + + /** + * 随机打乱选项的顺序, 支持所有题型的处理 + * + * @param answerFieldName 答案的字段名,只能是逗号分割 + * @param prefix 选项的属性前缀 questionOption + * @param lastIsUppercase 最后一个字段是否大写 + */ + public static void shuffleOrderOptions(Object obj, final String answerFieldName, final String prefix, Boolean lastIsUppercase) { + String questionAnswer = (String)ReflectUtil.getFieldValue(obj, answerFieldName); + Assert.notBlank(questionAnswer, "答案不能为空!"); + Assert.notBlank(prefix, "选项的属性前缀不能为空!"); + + // Map的简单封装 + ResourcesQuestionOptionEntity optionEntity = new ResourcesQuestionOptionEntity(); + + // 答案字段处理 + String[] options = ArrayUtil.removeBlank(questionAnswer.split(",")); + + // 记录属性名中的最后一位,记录ABCDEF.... + List lastLetterFieldNameList = new ArrayList<>(6); + + // 遍历当前对象的所有属性,不包含父类 + for (Field field : ReflectUtil.getFieldsDirectly(obj.getClass(), false)) { + + // 选项questionOption 开头的属性 + if (field.getName().startsWith(prefix)) { + // 反射,来获取当前属性最后一个字母:选项 + String lastLetter = field.getName().substring(field.getName().length() - 1); + // 转大写控制 + lastLetter = lastIsUppercase.equals(true)? upperCase(lastLetter): lastLetter; + // 选项里的内容 + String optionText = (String)ReflectUtil.getFieldValue(obj, field); + if (null == optionText) { continue; } + // 记录ABCDEF.... + lastLetterFieldNameList.add(lastLetter); + optionEntity.put(optionText, false); + + // 正确答案中,是否包含当前选项 + if (ArrayUtil.containsIgnoreCase(options, lastLetter)) { + // 标记为正确答案 + optionEntity.put(optionText, true); + } + } + } + + // 乱序后 + List optionTextList = optionEntity.getOptionTextList(); + // 答案和选项ABCDEF..对应 + Map answerMap = new HashMap<>(6); + // 乱序集合处理 + for (int i = 0; i < lastLetterFieldNameList.size(); i++) { + // 选项的子母 + String optionLetter = lastLetterFieldNameList.get(i); + // 选项中的文本 + String optionText = optionTextList.get(i); + answerMap.put(optionText, optionLetter); + // 等价于这样赋值:this.questionOptionA = ""; + ReflectUtil.setFieldValue(obj, prefix.concat(optionLetter), optionText); + } + + // 如果没有选项,可能是单选或者其他类型的题目 + List answerList = optionEntity.getAnswerList(answerMap); + List defaultAnswer = Collections.singletonList(questionAnswer); + List answer = ObjectUtil.isNotEmpty(answerList)? answerList: defaultAnswer; + // 乱序后的正确答案赋值 + ReflectUtil.setFieldValue(obj, answerFieldName, join(answer, ",")); + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionSnapshot.java b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionSnapshot.java index 66fedbce..49b7d9b5 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionSnapshot.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/ResourcesQuestionSnapshot.java @@ -13,7 +13,7 @@ import java.math.BigDecimal; * 资源管理 - 题库管理-快照-关联来源 * gen by Spring Boot2 Admin 2022-09-23 */ -public class ResourcesQuestionSnapshot extends BaseEntity{ +public class ResourcesQuestionSnapshot extends BaseEntity { //题目快照ID 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 b5f99c10..52cd7010 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/ResourcesQuestionService.java @@ -1,7 +1,7 @@ package com.ibeetl.jlw.service; -import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; import cn.jlw.util.ToolUtils; import com.ibeetl.admin.core.service.CoreBaseService; import com.ibeetl.admin.core.service.CorePlatformService; @@ -38,7 +38,7 @@ import java.text.SimpleDateFormat; import java.util.*; import static cn.hutool.core.util.ArrayUtil.join; -import static com.ibeetl.jlw.entity.ResourcesQuestion.AnswerEnum.getMethodNameByEnumName; +import static com.ibeetl.jlw.entity.ResourcesQuestionOptionEntity.shuffleOrderOptions; import static java.util.stream.Collectors.groupingBy; @@ -58,7 +58,7 @@ public class ResourcesQuestionService extends CoreBaseService @Resource private CorePlatformService platformService; - public PageQueryqueryByCondition(PageQuery query){ + public PageQuery queryByCondition(PageQuery query){ PageQuery ret = resourcesQuestionDao.queryByCondition(query); queryListAfter(ret.getList()); return ret; @@ -638,12 +638,12 @@ public class ResourcesQuestionService extends CoreBaseService * 随机打乱题目中选项的顺序 * @param questionList */ - public void ShuffleOrderOptions(List questionList) { + public void orderOptions(List questionList) { + if (ObjectUtil.isEmpty(questionList)) { + return; + } questionList.forEach(resourcesQuestion -> { - // 答案集合 - String[] answers = ArrayUtil.removeBlank(resourcesQuestion.getQuestionAnswer().split(",")); - Map nameAndMethod = getMethodNameByEnumName(answers); - + shuffleOrderOptions(resourcesQuestion, "questionAnswer","questionOption", true); }); } } \ No newline at end of file diff --git a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseHomeworkService.java b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseHomeworkService.java index 98c64018..07b36131 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseHomeworkService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseHomeworkService.java @@ -1,5 +1,7 @@ package com.ibeetl.jlw.service; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.extra.validation.BeanValidationResult; import cn.hutool.extra.validation.ValidationUtil; import cn.jlw.util.ToolUtils; @@ -9,10 +11,13 @@ import com.ibeetl.admin.core.service.CoreBaseService; import com.ibeetl.admin.core.util.PlatformException; import com.ibeetl.admin.core.web.JsonResult; import com.ibeetl.admin.core.web.JsonReturnCode; +import com.ibeetl.jlw.dao.ResourcesQuestionSnapshotDao; import com.ibeetl.jlw.dao.TeacherOpenCourseHomeworkDao; +import com.ibeetl.jlw.entity.ResourcesQuestionSnapshot; import com.ibeetl.jlw.entity.TeacherOpenCourseHomework; import com.ibeetl.jlw.entity.TeacherOpenCourseHomeworkSetting; import com.ibeetl.jlw.enums.FromTypeEnum; +import com.ibeetl.jlw.web.query.ResourcesQuestionSnapshotQuery; import com.ibeetl.jlw.web.query.TeacherOpenCourseHomeworkQuery; import com.ibeetl.jlw.web.query.TeacherOpenCourseHomeworkSettingQuery; import org.apache.commons.lang3.StringUtils; @@ -24,10 +29,14 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; import static cn.hutool.json.JSONUtil.toJsonStr; +import static com.ibeetl.jlw.entity.ResourcesQuestionOptionEntity.shuffleOrderOptions; import static com.ibeetl.jlw.web.query.TeacherOpenCourseHomeworkQuery.TeacherOpenCourseHomeworkTypeEnum.getClazzByEnum; +import static java.util.stream.Collectors.groupingBy; /** * 开课作业 Service @@ -40,6 +49,7 @@ public class TeacherOpenCourseHomeworkService extends CoreBaseServicequeryByCondition(PageQuery query){ PageQuery ret = teacherOpenCourseHomeworkDao.queryByCondition(query); @@ -183,12 +193,12 @@ public class TeacherOpenCourseHomeworkService extends CoreBaseService homeworkDetail(final Long teacherOpenCourseHomeworkId) { // 常量 True的Integer表现 final Integer TRUE_CONST = 1; - - TeacherOpenCourseHomework homework = getInfo(teacherOpenCourseHomeworkId); + List resourcesQuestionSnapshots = new ArrayList<>(); + TeacherOpenCourseHomework homework = getInfo(teacherOpenCourseHomeworkId); cn.hutool.core.lang.Assert.notNull(homework, "未查询到"); TeacherOpenCourseHomeworkSettingQuery settingQuery = new TeacherOpenCourseHomeworkSettingQuery(); @@ -197,29 +207,61 @@ public class TeacherOpenCourseHomeworkService extends CoreBaseService + shuffleOrderOptions(value, "questionAnswer","questionOption", true)); } // 题目乱序(同一大题内) if (TRUE_CONST.equals(hwSetting.getTeacherOpenCourseHomeworkSettingQuestionNoOrder())) { + shuffleOrderQuestions(resourcesQuestionSnapshots); + } + + return resourcesQuestionSnapshots; + } + /** + * 同一个大题内,随机排序 + * @param resourcesQuestionSnapshots + */ + private void shuffleOrderQuestions(List resourcesQuestionSnapshots) { + + // 安全判空 + if (ObjectUtil.isEmpty(resourcesQuestionSnapshots)) { + return; } - return null; + // 题目根据类型分组 + Map> questionTypeMap = resourcesQuestionSnapshots.stream() + .collect(groupingBy(ResourcesQuestionSnapshot::getQuestionType)); + + + // 同一道大题内,乱序 + questionTypeMap.forEach((questionType, questionSnapshots) -> { + Collections.shuffle(questionSnapshots); + }); + + + // 大题数字升序 + MapUtil.sort(questionTypeMap); } } diff --git a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkController.java b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkController.java index 5385bbe4..f2fb4cb2 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkController.java @@ -219,10 +219,10 @@ public class TeacherOpenCourseHomeworkController { * @param teacherOpenCourseHomeworkId 开课作业ID * @return */ - @PostMapping(MODEL + "/homework.json") + @PostMapping(MODEL + "/homeworkDetail.json") @Function("teacherOpenCourseHomework.query") - public JsonResult getHomeworkDetail(Long teacherOpenCourseHomeworkId) { - return JsonResult.success(teacherOpenCourseHomeworkService.getHomeworkDetail(teacherOpenCourseHomeworkId)); + public JsonResult homeworkDetail(Long teacherOpenCourseHomeworkId) { + return JsonResult.success(teacherOpenCourseHomeworkService.homeworkDetail(teacherOpenCourseHomeworkId)); } } diff --git a/web/src/test/java/com/ibeetl/jlw/web/ResourcesQuestionControllerTest.java b/web/src/test/java/com/ibeetl/jlw/web/ResourcesQuestionControllerTest.java new file mode 100644 index 00000000..d5ba2604 --- /dev/null +++ b/web/src/test/java/com/ibeetl/jlw/web/ResourcesQuestionControllerTest.java @@ -0,0 +1,37 @@ +package com.ibeetl.jlw.web; + +import base.BaseTest; +import com.ibeetl.admin.core.util.BeanUtil; +import com.ibeetl.jlw.web.query.ResourcesQuestionQuery; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.util.MultiValueMap; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ResourcesQuestionControllerTest extends BaseTest { + + private static final String MODEL = "/jlw/resourcesQuestion"; + private static final String API = "/api/resourcesQuestion"; + + @Test + void list() throws Exception { + MultiValueMap multiValueMap = BeanUtil.beanToStrMultiMap(new ResourcesQuestionQuery()); + + //构造请求参数 + RequestBuilder rb = MockMvcRequestBuilders.post(MODEL + "/list.json") + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .params(multiValueMap); + + //发送请求,验证返回结果 + String result = mvc.perform(rb) + .andExpect(status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0")) + .andReturn().getResponse().getContentAsString(); + + System.out.println(result); + } +} \ No newline at end of file diff --git a/web/src/test/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkControllerTest.java b/web/src/test/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkControllerTest.java index 47f82751..423a70de 100644 --- a/web/src/test/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkControllerTest.java +++ b/web/src/test/java/com/ibeetl/jlw/web/TeacherOpenCourseHomeworkControllerTest.java @@ -8,12 +8,15 @@ import com.ibeetl.admin.core.util.BeanUtil; import com.ibeetl.jlw.dao.ResourcesQuestionDao; import com.ibeetl.jlw.dao.SchoolClassDao; import com.ibeetl.jlw.dao.TeacherOpenCourseDao; +import com.ibeetl.jlw.dao.TeacherOpenCourseHomeworkDao; import com.ibeetl.jlw.entity.ResourcesQuestion; import com.ibeetl.jlw.entity.SchoolClass; import com.ibeetl.jlw.entity.TeacherOpenCourse; +import com.ibeetl.jlw.entity.TeacherOpenCourseHomework; import com.ibeetl.jlw.web.query.TeacherOpenCourseHomeworkQuery; import com.ibeetl.jlw.web.query.TeacherOpenCourseHomeworkSettingQuery; import org.junit.Assert; +import org.junit.FixMethodOrder; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.RequestBuilder; @@ -28,6 +31,7 @@ import static com.ibeetl.admin.test.util.test.RandomUtils.getRandomString; import static com.ibeetl.admin.test.util.test.RandomUtils.randomPojo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@FixMethodOrder() class TeacherOpenCourseHomeworkControllerTest extends BaseTest { private static final String MODEL = "/jlw/teacherOpenCourseHomework"; @@ -39,6 +43,10 @@ class TeacherOpenCourseHomeworkControllerTest extends BaseTest { private SchoolClassDao schoolClassDao; @Resource private TeacherOpenCourseDao teacherOpenCourseDao; + @Resource + private TeacherOpenCourseHomeworkDao teacherOpenCourseHomeworkDao; + + @Test void getPageList() { @@ -174,4 +182,30 @@ class TeacherOpenCourseHomeworkControllerTest extends BaseTest { System.out.println(result); } + + @Test + void homeworkDetail() throws Exception { + + // 获取一个状态正常的作业ID + TeacherOpenCourseHomework entity = new TeacherOpenCourseHomework(); + entity.setTeacherOpenCourseHomeworkStatus(1); + List homeworkList = teacherOpenCourseHomeworkDao.template(entity); + // 断言 + Assert.assertTrue(ObjectUtil.isNotEmpty(homeworkList)); + + Long teacherOpenCourseHomeworkId = RandomUtil.randomEle(homeworkList).getTeacherOpenCourseHomeworkId(); + + //构造请求参数 + RequestBuilder rb = MockMvcRequestBuilders.post(MODEL + "/homeworkDetail.json") + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("teacherOpenCourseHomeworkId", String.valueOf(teacherOpenCourseHomeworkId)); + + //发送请求,验证返回结果 + String result = mvc.perform(rb) + .andExpect(status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0")) + .andReturn().getResponse().getContentAsString(); + + System.out.println(result); + } } \ No newline at end of file