|
|
@ -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<String> 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 <T> Collection<QuestionInfo> 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<String, QuestionInfo> 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 <T>
|
|
|
|
|
|
|
|
* @throws IllegalAccessException
|
|
|
|
|
|
|
|
* @throws InstantiationException
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static <T> void fullSomeField(Collection<QuestionInfo> 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 <T>
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static <T> void shuffleQuestion(T t, final String stemFieldName,
|
|
|
|
|
|
|
|
final String optionFieldPrefixName, final String trueAnswerFieldName, final String questionTypeFieldName) {
|
|
|
|
|
|
|
|
Collection<QuestionInfo> 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|