Default Changelist

feature/qzw
秦泽旺 1 month ago
parent d199533049
commit 2620d64f1d

@ -6,23 +6,24 @@
***********************************************************/
package lsgwr.exam.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lsgwr.exam.entity.*;
import lsgwr.exam.enums.QuestionEnum;
import lsgwr.exam.service.ExamService;
import lsgwr.exam.repository.*;
import lsgwr.exam.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.*;
import cn.hutool.core.util.IdUtil;// Hutool工具类用于生成唯一ID等
import cn.hutool.core.util.StrUtil;// Hutool工具类用于字符串处理
import lsgwr.exam.entity.*;// 导入所有实体类
import lsgwr.exam.enums.QuestionEnum;// 导入枚举类
import lsgwr.exam.service.ExamService;// 导入服务接口
import lsgwr.exam.repository.*; // 导入所有仓库接口
import lsgwr.exam.vo.*;// 导入所有值对象View Object
import org.springframework.beans.BeanUtils;// Spring提供的Bean工具类用于属性复制
import org.springframework.stereotype.Service;// Spring提供的注解用于声明服务类
import javax.transaction.Transactional;// 事务注解,用于声明事务性方法
import java.util.*;// 导入Java集合框架相关类
// 使用@Service注解声明这是一个服务类用于实现业务逻辑
@Service
// 使用@Transactional注解声明这个服务类中的所有方法都运行在事务环境中
@Transactional
public class ExamServiceImpl implements ExamService {
// 注入所有需要的仓库接口
private final ExamRepository examRepository;
private final ExamRecordRepository examRecordRepository;
@ -38,8 +39,9 @@ public class ExamServiceImpl implements ExamService {
private final QuestionCategoryRepository questionCategoryRepository;
private final QuestionOptionRepository questionOptionRepository;
// 通过构造函数注入所有仓库接口
public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) {
// 依次赋值给对应的成员变量
this.questionRepository = questionRepository;
this.userRepository = userRepository;
this.questionLevelRepository = questionLevelRepository;
@ -49,37 +51,43 @@ public class ExamServiceImpl implements ExamService {
this.examRepository = examRepository;
this.examRecordRepository = examRecordRepository;
}
// 实现获取所有问题的接口
@Override
public List<QuestionVo> getQuestionAll() {
// 从问题仓库中获取所有问题实体
List<Question> questionList = questionRepository.findAll();
// 将问题实体列表转换为问题值对象列表
return getQuestionVos(questionList);
}
// 私有方法,用于将问题实体列表转换为问题值对象列表
private List<QuestionVo> getQuestionVos(List<Question> questionList) {
// 需要自定义的question列表
// 初始化值对象列表
List<QuestionVo> questionVoList = new ArrayList<>();
// 循环完成每个属性的定制
// 遍历问题实体列表
for (Question question : questionList) {
// 将每个问题实体转换为值对象
QuestionVo questionVo = getQuestionVo(question);
// 将值对象添加到列表中
questionVoList.add(questionVo);
}
// 返回值对象列表
return questionVoList;
}
// 私有方法,用于将单个问题实体转换为问题值对象
private QuestionVo getQuestionVo(Question question) {
// 初始化值对象
QuestionVo questionVo = new QuestionVo();
// 先复制能复制的属性
// 复制问题实体中的属性到值对象
BeanUtils.copyProperties(question, questionVo);
// 设置问题的创建者
// 设置问题的创建者用户名通过用户ID从用户仓库中获取
questionVo.setQuestionCreator(
Objects.requireNonNull(
userRepository.findById(
question.getQuestionCreatorId()
).orElse(null)
).getUserUsername());
).getUserUsername());// 使用orElseThrow抛出异常避免使用requireNonNull和orElse(null)
// 设置问题的难度
// 设置问题的难度描述通过难度ID从难度仓库中获取
questionVo.setQuestionLevel(
Objects.requireNonNull(
questionLevelRepository.findById(
@ -87,7 +95,7 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionLevelDescription());
// 设置题目的类别,比如单选、多选、判断等
// 设置问题的类型描述通过类型ID从类型仓库中获取
questionVo.setQuestionType(
Objects.requireNonNull(
questionTypeRepository.findById(
@ -95,7 +103,7 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionTypeDescription());
// 设置题目分类,比如数学、语文、英语、生活、人文等
// 设置问题的分类名称通过分类ID从分类仓库中获取
questionVo.setQuestionCategory(
Objects.requireNonNull(
questionCategoryRepository.findById(
@ -104,54 +112,67 @@ public class ExamServiceImpl implements ExamService {
).getQuestionCategoryName()
);
// 选项的自定义Vo列表
// 初始化选项值对象列表
List<QuestionOptionVo> optionVoList = new ArrayList<>();
// 获得所有的选项列表
// 从选项仓库中获取所有选项实体
List<QuestionOption> optionList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionOptionIds().split("-"))
);
// 获取所有的答案列表optionList中每个option的isAnswer选项
// 从选项仓库中获取所有答案选项实体
List<QuestionOption> answerList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionAnswerOptionIds().split("-"))
);
// 根据选项和答案的id相同设置optionVo的isAnswer属性
// 遍历选项实体列表,转换为值对象,并设置答案属性
for (QuestionOption option : optionList) {
QuestionOptionVo optionVo = new QuestionOptionVo();
BeanUtils.copyProperties(option, optionVo);
// 判断当前选项是否是答案
for (QuestionOption answer : answerList) {
if (option.getQuestionOptionId().equals(answer.getQuestionOptionId())) {
optionVo.setAnswer(true);
break;// 找到答案后无需继续遍历
}
}
// 将选项值对象添加到列表中
optionVoList.add(optionVo);
}
// 设置的所有选项
// 设置题的所有选项值对象列表
questionVo.setQuestionOptionVoList(optionVoList);
// 返回问题值对象
return questionVo;
}
// 实现更新问题的接口
@Override
public QuestionVo updateQuestion(QuestionVo questionVo) {
// 1.把需要的属性都设置好
// 初始化答案ID字符串构建器
StringBuilder questionAnswerOptionIds = new StringBuilder();
// 初始化选项实体列表
List<QuestionOption> questionOptionList = new ArrayList<>();
// 获取问题值对象中的选项值对象列表
List<QuestionOptionVo> questionOptionVoList = questionVo.getQuestionOptionVoList();
// 获取选项值对象列表的大小
int size = questionOptionVoList.size();
// 遍历选项值对象列表
for (int i = 0; i < questionOptionVoList.size(); i++) {
// 获取当前选项值对象
QuestionOptionVo questionOptionVo = questionOptionVoList.get(i);
// 初始化选项实体
QuestionOption questionOption = new QuestionOption();
// 复制选项值对象中的属性到选项实体
BeanUtils.copyProperties(questionOptionVo, questionOption);
// 将选项实体添加到列表中
questionOptionList.add(questionOption);
// 判断当前选项是否是答案
if (questionOptionVo.getAnswer()) {
if (i != size - 1) {
// 把更新后的答案的id加上去,记得用-连到一起
// 如果不是最后一个答案则添加ID并用"-"连接
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()).append("-");
} else {
// 最后一个不需要用-连接
// 如果是最后一个答案则直接添加ID
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId());
}
}
@ -159,70 +180,80 @@ public class ExamServiceImpl implements ExamService {
// 1.更新问题
Question question = questionRepository.findById(questionVo.getQuestionId()).orElse(null);
// 确保找到的问题不为null否则将抛出异常
assert question != null;
// 将传入的问题对象questionVo的属性复制到持久化对象question
BeanUtils.copyProperties(questionVo, question);
// 更新问题的答案选项ID字符串假设是以某种格式串联的ID字符串
question.setQuestionAnswerOptionIds(questionAnswerOptionIds.toString());
// 保存更新后的问题到数据库
questionRepository.save(question);
// 2.更新所有的option
questionOptionRepository.saveAll(questionOptionList);
// 返回更新后的问题,方便前端局部刷新
// 返回更新后的问题对象转换为VO对象方便前端局部刷新
return getQuestionVo(question);
}
// 重写问题创建方法
@Override
public void questionCreate(QuestionCreateVo questionCreateVo) {
// 问题创建
// 创建一个新的Question对象
Question question = new Question();
// 把能复制的属性都复制过来
// 使用BeanUtils工具类将questionCreateVo中的属性复制到question对象中
BeanUtils.copyProperties(questionCreateVo, question);
// 设置下questionOptionIds和questionAnswerOptionIds需要自己用Hutool生成下
// 初始化选项列表
List<QuestionOption> questionOptionList = new ArrayList<>();
// 获取问题创建对象中的选项列表
List<QuestionOptionCreateVo> questionOptionCreateVoList = questionCreateVo.getQuestionOptionCreateVoList();
// 遍历选项创建对象列表
for (QuestionOptionCreateVo questionOptionCreateVo : questionOptionCreateVoList) {
// 为每个选项创建一个新的QuestionOption对象
QuestionOption questionOption = new QuestionOption();
// 设置选项的内容
// 设置选项的内容
questionOption.setQuestionOptionContent(questionOptionCreateVo.getQuestionOptionContent());
// 设置选项的id
// 使用Hutool的IdUtil生成一个简单的UUID作为选项的ID
questionOption.setQuestionOptionId(IdUtil.simpleUUID());
// 将选项添加到列表中
questionOptionList.add(questionOption);
}
// 把选项都存起来然后才能用于下面设置Question的questionOptionIds和questionAnswerOptionIds
// 将所有选项保存到数据库
questionOptionRepository.saveAll(questionOptionList);
// 初始化选项ID和答案选项ID的字符串
String questionOptionIds = "";
String questionAnswerOptionIds = "";
// 经过上面的saveAll方法所有的option的主键id都已经持久化了
// 遍历选项创建对象列表,获取保存后的选项对象
for (int i = 0; i < questionOptionCreateVoList.size(); i++) {
// 获取指定选项
QuestionOptionCreateVo questionOptionCreateVo = questionOptionCreateVoList.get(i);
// 获取保存后的指定对象
QuestionOption questionOption = questionOptionList.get(i);
// 拼接选项ID
questionOptionIds += questionOption.getQuestionOptionId() + "-";
if (questionOptionCreateVo.getAnswer()) {
// 如果是答案的话
questionAnswerOptionIds += questionOption.getQuestionOptionId() + "-";
}
}
// 把字符串最后面的"-"给去掉
// 去除选项ID和答案选项ID字符串末尾的"-"
questionAnswerOptionIds = replaceLastSeparator(questionAnswerOptionIds);
questionOptionIds = replaceLastSeparator(questionOptionIds);
// 设置选项id组成的字符串
// 设置问题对象的选项ID和答案选项ID
question.setQuestionOptionIds(questionOptionIds);
// 设置答案选项id组成的字符串
question.setQuestionAnswerOptionIds(questionAnswerOptionIds);
// 自己生成问题的id
// 为问题生成一个UUID作为ID
question.setQuestionId(IdUtil.simpleUUID());
// 先把创建时间和更新时间每次都取当前时间吧
// 设置问题的创建时间和更新时间为当前时间
question.setCreateTime(new Date());
question.setUpdateTime(new Date());
// 保存问题到数据库
// 将问题保存到数据库
questionRepository.save(question);
}
// 重写获取选择项的方法
@Override
public QuestionSelectionVo getSelections() {
// 创建一个新的QuestionSelectionVo对象
QuestionSelectionVo questionSelectionVo = new QuestionSelectionVo();
// 设置问题分类列表、问题等级列表和问题类型列表
questionSelectionVo.setQuestionCategoryList(questionCategoryRepository.findAll());
questionSelectionVo.setQuestionLevelList(questionLevelRepository.findAll());
questionSelectionVo.setQuestionTypeList(questionTypeRepository.findAll());
@ -230,27 +261,27 @@ public class ExamServiceImpl implements ExamService {
return questionSelectionVo;
}
/**
* split
*
* @param str
* @return
*/
// 去除字符串末尾的"-"的方法
public static String trimMiddleLine(String str) {
// 如果字符串的最后一个字符是"-",则去除它
if (str.charAt(str.length() - 1) == '-') {
str = str.substring(0, str.length() - 1);
}
return str;
}
// 注意原代码中有一个方法名为replaceLastSeparator但提供的实现是trimMiddleLine这里假设它们功能相同
@Override
// 重写获取问题详情的方法
public QuestionDetailVo getQuestionDetail(String id) {
// 根据ID从数据库获取问题对象
Question question = questionRepository.findById(id).orElse(null);
// 创建一个新的QuestionDetailVo对象
QuestionDetailVo questionDetailVo = new QuestionDetailVo();
// 设置问题详情对象的ID、名称和描述
questionDetailVo.setId(id);
questionDetailVo.setName(question.getQuestionName());
questionDetailVo.setDescription(question.getQuestionDescription());
// 问题类型,单选题/多选题/判断题
// 获取问题类型描述
questionDetailVo.setType(
Objects.requireNonNull(
questionTypeRepository.findById(
@ -258,30 +289,35 @@ public class ExamServiceImpl implements ExamService {
).orElse(null)
).getQuestionTypeDescription()
);
// 获取当前问题的选项
// 获取问题的选项ID字符串去除末尾的"-",然后分割成数组
String optionIdsStr = trimMiddleLine(question.getQuestionOptionIds());
String[] optionIds = optionIdsStr.split("-");
// 获取选项列表
// 根据选项ID数组从数据库获取选项列表
List<QuestionOption> optionList = questionOptionRepository.findAllById(Arrays.asList(optionIds));
// 设置问题详情对象的选项列表
questionDetailVo.setOptions(optionList);
return questionDetailVo;
}
// 重写获取所有考试的方法
@Override
public List<ExamVo> getExamAll() {
// 从数据库获取所有考试对象
List<Exam> examList = examRepository.findAll();
// 将考试对象列表转换为ExamVo列表
return getExamVos(examList);
}
// 定义一个私有方法接收一个Exam对象的列表作为参数返回一个ExamVo对象的列表
private List<ExamVo> getExamVos(List<Exam> examList) {
// 需要自定义的exam列表
// 初始化一个空的ExamVo列表用于存放转换后的对象
List<ExamVo> examVoList = new ArrayList<>();
// 循环完成每个属性的定制
// 遍历传入的Exam列表
for (Exam exam : examList) {
// 为每个Exam对象创建一个对应的ExamVo对象
ExamVo examVo = new ExamVo();
// 先尽量复制能复制的所有属性
// 使用BeanUtils工具类将Exam对象的属性复制到ExamVo对象中对于名称和类型相同的属性
BeanUtils.copyProperties(exam, examVo);
// 设置问题的创建者
// 通过userRepository查找考试创建者的信息并设置到ExamVo对象的相应属性上
// 这里使用了Optional的requireNonNull方法确保找到的User对象不为null
examVo.setExamCreator(
Objects.requireNonNull(
userRepository.findById(
@ -290,385 +326,494 @@ public class ExamServiceImpl implements ExamService {
).getUserUsername()
);
// 获取所有单选题列表并赋值到ExamVo的属性ExamQuestionSelectVoRadioList
// 初始化单选题的列表并设置到ExamVo对象的相应属性
List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
// 根据考试中的单选题ID字符串分割成ID列表并通过questionRepository查找对应的Question对象列表
List<Question> radioQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsRadio().split("-"))
);
// 遍历单选题列表为每个Question对象创建一个对应的ExamQuestionSelectVo对象并设置选中状态为true
for (Question question : radioQuestionList) {
ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, radioQuestionVo);
radioQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
radioQuestionVo.setChecked(true);// 假设考试中的问题在初始化时都被视为已选中
radioQuestionVoList.add(radioQuestionVo);
}
examVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
// 创建一个用于存储考试选择题Vo对象的列表
List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
// 从数据库中获取所有需要检查(可能是单选或多选,但此处标记为检查题)的问题列表
// 通过分割考试对象中的考试问题ID字符串使用"-"作为分隔符并将结果转换为List<Long>
// 然后调用questionRepository的findAllById方法获取Question对象列表
List<Question> checkQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsCheck().split("-"))
);
// 遍历获取到的问题列表
for (Question question : checkQuestionList) {
// 为每个问题创建一个新的ExamQuestionSelectVo对象
ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
// 使用BeanUtils工具类将Question对象的属性复制到新的ExamQuestionSelectVo对象中
BeanUtils.copyProperties(question, checkQuestionVo);
checkQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
// 设置选中状态为true表示这个问题在考试中是已经被选中的
checkQuestionVo.setChecked(true); // 考试中问题肯定被选中的
// 将设置好的Vo对象添加到列表中
checkQuestionVoList.add(checkQuestionVo);
}
examVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
// 从数据库中获取所有需要判断的问题列表
// 通过分割考试对象中的考试问题ID字符串使用"-"作为分隔符并将结果转换为List<Long>
// 然后调用questionRepository的findAllById方法获取Question对象列表
List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
List<Question> judgeQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsJudge().split("-"))
);
// 遍历获取到的问题列表
for (Question question : judgeQuestionList) {
// 为每个问题创建一个新的ExamQuestionSelectVo对象
ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
// 使用BeanUtils工具类将Question对象的属性复制到新的ExamQuestionSelectVo对象中
BeanUtils.copyProperties(question, judgeQuestionVo);
judgeQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
// 设置选中状态为true
judgeQuestionVo.setChecked(true);
// 将设置好的Vo对象添加到列表中
judgeQuestionVoList.add(judgeQuestionVo);
}
examVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
// 把examVo加到examVoList
// 将处理好的ExamVo对象添加到列表
examVoList.add(examVo);
}
// 返回处理好的ExamVo列表
return examVoList;
}
// 重写父类或接口中的方法,用于获取考试问题类型的详细信息
@Override
public ExamQuestionTypeVo getExamQuestionType() {
// 初始化一个ExamQuestionTypeVo对象用于存储考试题型信息
ExamQuestionTypeVo examQuestionTypeVo = new ExamQuestionTypeVo();
// 获取所有单选题列表并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
// 获取所有单选题列表并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
// 从数据库中获取所有单选题通过题型ID筛选这里的QuestionEnum.RADIO.getId()返回单选题的ID
List<Question> radioQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.RADIO.getId());
// 遍历所有单选题
for (Question question : radioQuestionList) {
// 为每个单选题创建一个ExamQuestionSelectVo对象
ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
// 将Question对象的属性复制到radioQuestionVo对象中
BeanUtils.copyProperties(question, radioQuestionVo);
// 将复制后的对象添加到单选题的列表中
radioQuestionVoList.add(radioQuestionVo);
}
// 将单选题列表设置到ExamQuestionTypeVo对象的相应属性中
examQuestionTypeVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
// 初始化多选题的列表
List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
// 从数据库中获取所有多选题通过题型ID筛选这里的QuestionEnum.CHECK.getId()返回多选题的ID
List<Question> checkQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.CHECK.getId());
// 遍历所有多选题
for (Question question : checkQuestionList) {
// 为每个多选题创建一个ExamQuestionSelectVo对象
ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
// 将Question对象的属性复制到checkQuestionVo对象中
BeanUtils.copyProperties(question, checkQuestionVo);
// 将复制后的对象添加到多选题的列表中
checkQuestionVoList.add(checkQuestionVo);
}
// 将多选题列表设置到ExamQuestionTypeVo对象的相应属性中
examQuestionTypeVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
// 初始化判断题的列表
List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
// 从数据库中获取所有判断题通过题型ID筛选这里的QuestionEnum.JUDGE.getId()返回判断题的ID
List<Question> judgeQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.JUDGE.getId());
// 遍历所有判断题
for (Question question : judgeQuestionList) {
// 为每个判断题创建一个ExamQuestionSelectVo对象
ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
// 将Question对象的属性复制到judgeQuestionVo对象中
BeanUtils.copyProperties(question, judgeQuestionVo);
// 将复制后的对象添加到判断题的列表中
judgeQuestionVoList.add(judgeQuestionVo);
}
// 将判断题列表设置到ExamQuestionTypeVo对象的相应属性中
examQuestionTypeVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
// 返回包含所有题型信息的ExamQuestionTypeVo对象
return examQuestionTypeVo;
}
@Override
public Exam create(ExamCreateVo examCreateVo, String userId) {
// 在线考试系统创建
// 创建一个新的考试对象
Exam exam = new Exam();
// 将前端传来的考试创建对象ExamCreateVo的属性复制到新创建的考试对象中
BeanUtils.copyProperties(examCreateVo, exam);
// 为考试生成一个唯一的ID
exam.setExamId(IdUtil.simpleUUID());
// 设置考试的创建者ID
exam.setExamCreatorId(userId);
// 设置考试的创建时间和更新时间(这里都设置为当前时间)
exam.setCreateTime(new Date());
exam.setUpdateTime(new Date());
// Todo:这两个日志后面是要在前端传入的,这里暂时定为当前日期
// Todo: 考试的开始和结束日期应该是前端传入的,这里暂时使用当前日期作为占位符
exam.setExamStartDate(new Date());
exam.setExamEndDate(new Date());
// 初始化字符串用于拼接单选、多选和判断题的ID
String radioIdsStr = "";
String checkIdsStr = "";
String judgeIdsStr = "";
// 从前端传来的对象中获取单选、多选和判断题的列表
List<ExamQuestionSelectVo> radios = examCreateVo.getRadios();
List<ExamQuestionSelectVo> checks = examCreateVo.getChecks();
List<ExamQuestionSelectVo> judges = examCreateVo.getJudges();
// 初始化计数器,用于记录每种类型题目的数量
int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
// 遍历单选题的列表拼接被选中的题目ID并计数
for (ExamQuestionSelectVo radio : radios) {
if (radio.getChecked()) {
radioIdsStr += radio.getQuestionId() + "-";
radioCnt++;
}
}
// 替换最后一个分隔符,避免字符串末尾出现多余的分隔符
radioIdsStr = replaceLastSeparator(radioIdsStr);
// 遍历多选题的列表拼接被选中的题目ID并计数
for (ExamQuestionSelectVo check : checks) {
if (check.getChecked()) {
checkIdsStr += check.getQuestionId() + "-";
checkCnt++;
}
}
// 替换最后一个分隔符
checkIdsStr = replaceLastSeparator(checkIdsStr);
// 遍历判断题的列表拼接被选中的题目ID并计数
for (ExamQuestionSelectVo judge : judges) {
if (judge.getChecked()) {
judgeIdsStr += judge.getQuestionId() + "-";
judgeCnt++;
}
}
// 替换最后一个分隔符
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
// 将所有被选中的题目ID拼接成一个字符串并设置到考试对象中
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
// 设置各个题目的id
// 分别设置单选、多选和判断题的ID串到考试对象中
exam.setExamQuestionIdsRadio(radioIdsStr);
exam.setExamQuestionIdsCheck(checkIdsStr);
exam.setExamQuestionIdsJudge(judgeIdsStr);
// 计算总分数
// 计算考试的总分数
// 总分数 = 单选题数量 * 单选题每题分数 + 多选题数量 * 多选题每题分数 + 判断题数量 * 判断题每题分数
int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
exam.setExamScore(examScore);
// 将考试对象保存到数据库中
examRepository.save(exam);
// 返回创建好的考试对象
return exam;
}
// 重写update方法用于更新考试信息
@Override
public Exam update(ExamVo examVo, String userId) {
// 创建一个新的Exam对象
Exam exam = new Exam();
// 将examVo对象的属性复制到exam对象中
BeanUtils.copyProperties(examVo, exam);
exam.setExamCreatorId(userId); // 考试的更新人为最新的创建人
exam.setUpdateTime(new Date()); // 考试的更新日期要记录下
// 设置考试的更新人为当前的用户ID
exam.setExamCreatorId(userId);
// 记录考试的更新日期为当前时间
exam.setUpdateTime(new Date());
// 初始化字符串变量用于存储不同类型的题目ID
String radioIdsStr = "";
String checkIdsStr = "";
String judgeIdsStr = "";
// 获取单选、多选和判断题的列表
List<ExamQuestionSelectVo> radios = examVo.getExamQuestionSelectVoRadioList();
List<ExamQuestionSelectVo> checks = examVo.getExamQuestionSelectVoCheckList();
List<ExamQuestionSelectVo> judges = examVo.getExamQuestionSelectVoJudgeList();
// 初始化计数器,用于统计每种类型题目的数量
int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
// 遍历单选题目列表拼接被选中的题目ID
for (ExamQuestionSelectVo radio : radios) {
if (radio.getChecked()) {
radioIdsStr += radio.getQuestionId() + "-";
radioCnt++;
}
}
// 移除最后一个多余的"-"
radioIdsStr = replaceLastSeparator(radioIdsStr);
// 遍历多选题目列表拼接被选中的题目ID
for (ExamQuestionSelectVo check : checks) {
if (check.getChecked()) {
checkIdsStr += check.getQuestionId() + "-";
checkCnt++;
}
}
// 移除最后一个多余的"-"
checkIdsStr = replaceLastSeparator(checkIdsStr);
// 遍历判断题目列表拼接被选中的题目ID
for (ExamQuestionSelectVo judge : judges) {
if (judge.getChecked()) {
judgeIdsStr += judge.getQuestionId() + "-";
judgeCnt++;
}
}
// 移除最后一个多余的"-"
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
// 设置考试的题目ID字符串包括单选、多选和判断题
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
// 设置各个题目的id
// 分别设置各类题目的ID字符串
exam.setExamQuestionIdsRadio(radioIdsStr);
exam.setExamQuestionIdsCheck(checkIdsStr);
exam.setExamQuestionIdsJudge(judgeIdsStr);
// 计算总分数
// 计算考试的总分数
int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
exam.setExamScore(examScore);
// 保存更新后的考试信息
examRepository.save(exam);
// 返回更新后的考试信息
return exam;
}
// 重写getExamCardList方法用于获取所有考试的信息列表
@Override
public List<ExamCardVo> getExamCardList() {
// 从数据库中获取所有的考试信息
List<Exam> examList = examRepository.findAll();
// 创建一个新的列表,用于存储转换后的考试卡片信息
List<ExamCardVo> examCardVoList = new ArrayList<>();
// 遍历考试信息列表,将每个考试信息转换为考试卡片信息
for (Exam exam : examList) {
ExamCardVo examCardVo = new ExamCardVo();
BeanUtils.copyProperties(exam, examCardVo);
// 将转换后的考试卡片信息添加到列表中
examCardVoList.add(examCardVo);
}
// 返回考试卡片信息列表
return examCardVoList;
}
// 重写getExamDetail方法用于获取指定考试的详细信息
@Override
public ExamDetailVo getExamDetail(String id) {
// 根据ID从数据库中获取考试信息
Exam exam = examRepository.findById(id).orElse(null);
// 创建一个新的考试详细信息对象
ExamDetailVo examDetailVo = new ExamDetailVo();
// 设置考试信息
examDetailVo.setExam(exam);
// 确保考试信息不为空这里使用assert进行断言但通常建议使用更优雅的异常处理
assert exam != null;
// 将考试的各类题目ID字符串分割为数组并设置到考试详细信息对象中
examDetailVo.setRadioIds(exam.getExamQuestionIdsRadio().split("-"));
examDetailVo.setCheckIds(exam.getExamQuestionIdsCheck().split("-"));
examDetailVo.setJudgeIds(exam.getExamQuestionIdsJudge().split("-"));
// 返回考试详细信息对象
return examDetailVo;
}
// 重写judge方法用于判断用户考试的得分情况
@Override
public ExamRecord judge(String userId, String examId, HashMap<String, List<String>> answersMap) {
// 开始考试判分~~~
// 1.首先获取考试对象和选项数组
ExamDetailVo examDetailVo = getExamDetail(examId);
Exam exam = examDetailVo.getExam();
// 2.然后获取该考试下所有题目信息
List<String> questionIds = new ArrayList<>();
// 2.1 题目id的数组
List<String> radioIdList = Arrays.asList(examDetailVo.getRadioIds());
List<String> checkIdList = Arrays.asList(examDetailVo.getCheckIds());
List<String> judgeIdList = Arrays.asList(examDetailVo.getJudgeIds());
questionIds.addAll(radioIdList);
questionIds.addAll(checkIdList);
questionIds.addAll(judgeIdList);
// 2.2 每种题目的分数
int radioScore = exam.getExamScoreRadio();
int checkScore = exam.getExamScoreCheck();
int judgeScore = exam.getExamScoreJudge();
// 2.3 根据问题id的数组拿到所有的问题对象供下面步骤用
List<Question> questionList = questionRepository.findAllById(questionIds);
Map<String, Question> questionMap = new HashMap<>();
// 开始考试判分~~~
// 1. 获取考试详情对象和考试对象的选项数组
ExamDetailVo examDetailVo = getExamDetail(examId);// 获取考试详情
Exam exam = examDetailVo.getExam();// 获取考试对象
// 2.然后获取该考试下所有题目信息
List<String> questionIds = new ArrayList<>();// 存储题目ID的列表
// 2.1 获取各种题型的题目ID列表
List<String> radioIdList = Arrays.asList(examDetailVo.getRadioIds());// 单选题ID列表
List<String> checkIdList = Arrays.asList(examDetailVo.getCheckIds());// 多选题ID列表
List<String> judgeIdList = Arrays.asList(examDetailVo.getJudgeIds());// 判断题ID列表
questionIds.addAll(radioIdList);// 将单选题ID添加到总列表
questionIds.addAll(checkIdList);// 将多选题ID添加到总列表
questionIds.addAll(judgeIdList);// 将判断题ID添加到总列表
// 2.2 获取每种题型的分数
int radioScore = exam.getExamScoreRadio();// 单选题每题分数
int checkScore = exam.getExamScoreCheck();// 多选题每题分数
int judgeScore = exam.getExamScoreJudge();// 判断题每题分数
// 2.3 根据题目ID获取所有题目对象便于后续操作
List<Question> questionList = questionRepository.findAllById(questionIds);// 查询所有题目
Map<String, Question> questionMap = new HashMap<>();// 使用Map存储题目ID和题目对象的映射
for (Question question : questionList) {
questionMap.put(question.getQuestionId(), question);
questionMap.put(question.getQuestionId(), question);// 将题目添加到Map中
}
// 3.根据正确答案和用户作答信息进行判分
Set<String> questionIdsAnswer = answersMap.keySet();
// 存储当前考试每个题目的得分情况
// 3. 根据正确答案,用户作答信息进行判分
Set<String> questionIdsAnswer = answersMap.keySet();// 获取用户作答的题目ID集合
// 存储每个题目的得分情况
Map<String, Integer> judgeMap = new HashMap<>();
// 考生作答地每个题目的选项(题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔),用于查看考试详情
// 例子题目1的id_作答选项1-作答选项2&题目2的id_作答选项1&题目3_作答选项1-作答选项2-作答选项3
// 用于构建用户作答选项的字符串,用于查看考试详情
StringBuilder answerOptionIdsSb = new StringBuilder();
// 用户此次考试的总分
// 用户考试的总分
int totalScore = 0;
// 遍历用户作答的题目ID
for (String questionId : questionIdsAnswer) {
// 获取用户作答地这个题的答案信息
// 获取题目对象
Question question = questionMap.get(questionId);
// 获取答案选项
// 获取题目正确答案选项ID并处理格式
String questionAnswerOptionIds = replaceLastSeparator(question.getQuestionAnswerOptionIds());
List<String> questionAnswerOptionIdList = Arrays.asList(questionAnswerOptionIds.split("-"));
Collections.sort(questionAnswerOptionIdList);
String answerStr = listConcat(questionAnswerOptionIdList);
// 获取用户作答
Collections.sort(questionAnswerOptionIdList);// 对选项进行排序
String answerStr = listConcat(questionAnswerOptionIdList);// 将选项列表转换为字符串
// 获取用户作答的选项ID列表并处理格式
List<String> questionUserOptionIdList = answersMap.get(questionId);
Collections.sort(questionUserOptionIdList);
String userStr = listConcat(questionUserOptionIdList);
// 判断questionAnswerOptionIds和answersMap里面的答案是否相等
Collections.sort(questionUserOptionIdList);// 对选项进行排序
String userStr = listConcat(questionUserOptionIdList);// 将选项列表转换为字符串
// 判断用户作答是否正确
if (answerStr.equals(userStr)) {
// 说明题目作答正确,下面根据题型给分
// 作答正确,根据题型设置分数
int score = 0;
if (radioIdList.contains(questionId)) {
score = radioScore;
score = radioScore;// 单选题分数
}
if (checkIdList.contains(questionId)) {
score = checkScore;
score = checkScore;// 多选题分数
}
if (judgeIdList.contains(questionId)) {
score = judgeScore;
score = judgeScore;// 判断题分数
}
// 累计本次考试得
// 累加总
totalScore += score;
// True代表题目答对
// 记录答案正确及用户作答选项
answerOptionIdsSb.append(questionId + "@True_" + userStr + "$");
judgeMap.put(questionId, score);
judgeMap.put(questionId, score);// 记录题目得分
} else {
// 说明题目作答错误,直接判零分,False代表题目答错
// 作答错误记0分
answerOptionIdsSb.append(questionId + "@False_" + userStr + "$");
judgeMap.put(questionId, 0);
judgeMap.put(questionId, 0);// 记录题目得分为0
}
}
// 4.计算得分记录本次考试结果存到ExamRecord
ExamRecord examRecord = new ExamRecord();
examRecord.setExamRecordId(IdUtil.simpleUUID());
examRecord.setExamId(examId);
// 注意去掉最后可能有的&_-
// 4. 计算得分,记录考试结果,并保存到数据库
ExamRecord examRecord = new ExamRecord();// 创建考试记录对象
examRecord.setExamRecordId(IdUtil.simpleUUID());// 设置考试记录ID
examRecord.setExamId(examId);// 设置考试ID
// 设置用户作答选项字符串,注意处理格式
examRecord.setAnswerOptionIds(replaceLastSeparator(answerOptionIdsSb.toString()));
examRecord.setExamJoinerId(userId);
examRecord.setExamJoinDate(new Date());
examRecord.setExamJoinScore(totalScore);
examRecordRepository.save(examRecord);
return examRecord;
examRecord.setExamJoinerId(userId);// 设置考生ID
examRecord.setExamJoinDate(new Date());// 设置考试日期
examRecord.setExamJoinScore(totalScore);// 设置考试总分
examRecordRepository.save(examRecord);// 保存考试记录到数据库
return examRecord;// 返回考试记录对象
}
// 重写方法,用于获取指定用户的考试记录列表
@Override
public List<ExamRecordVo> getExamRecordList(String userId) {
// 获取指定用户下的考试记录列表
// 通过用户ID从数据库中查询该用户参与的考试记录按考试加入日期降序排列
List<ExamRecord> examRecordList = examRecordRepository.findByExamJoinerIdOrderByExamJoinDateDesc(userId);
// 创建一个用于存放转换后的考试记录视图对象的列表
List<ExamRecordVo> examRecordVoList = new ArrayList<>();
// 遍历查询到的考试记录
for (ExamRecord examRecord : examRecordList) {
ExamRecordVo examRecordVo = new ExamRecordVo();
ExamRecordVo examRecordVo = new ExamRecordVo();// 创建一个新的考试记录视图对象
// 根据考试记录中的考试ID查询考试详情
Exam exam = examRepository.findById(examRecord.getExamId()).orElse(null);
// 设置考试详情到视图对象中
examRecordVo.setExam(exam);
// 根据用户ID查询用户详情
User user = userRepository.findById(userId).orElse(null);
// 设置用户详情到视图对象中
examRecordVo.setUser(user);
// 设置考试记录到视图对象中
examRecordVo.setExamRecord(examRecord);
// 将视图对象添加到列表中
examRecordVoList.add(examRecordVo);
}
return examRecordVoList;
}
// 重写方法,用于获取特定考试记录的详细信息
@Override
public RecordDetailVo getRecordDetail(String recordId) {
// 获取考试详情的封装对象
// 通过记录ID从数据库中查询考试记录
ExamRecord record = examRecordRepository.findById(recordId).orElse(null);
// 创建一个新的记录详情视图对象
RecordDetailVo recordDetailVo = new RecordDetailVo();
// 设置考试记录到视图对象中
recordDetailVo.setExamRecord(record);
// 用户的答案,需要解析
// 初始化用于存储用户答案和结果的哈希映射
HashMap<String, List<String>> answersMap = new HashMap<>();
HashMap<String, String> resultsMap = new HashMap<>();
// 确保查询到的考试记录不为空
assert record != null;
// 获取用户答案字符串,以$分隔每个题目的答案
String answersStr = record.getAnswerOptionIds();
// $分隔题目,因为$在正则中有特殊用途(行尾),所以需要括起来
// 按$分割字符串,得到每个题目的答案
String[] questionArr = answersStr.split("[$]");
// 遍历每个题目的答案
for (String questionStr : questionArr) {
System.out.println(questionStr);
// 区分开题目标题和选项
// 题目字符串格式为:题目标题@结果_选项1-选项2-...,分割得到题目和选项
String[] questionTitleResultAndOption = questionStr.split("_");
String[] questionTitleAndResult = questionTitleResultAndOption[0].split("@");
String[] questionOptions = questionTitleResultAndOption[1].split("-");
// 题目:答案选项
// 将题目和答案选项存入answersMap
answersMap.put(questionTitleAndResult[0], Arrays.asList(questionOptions));
// 题目True / False
// 将题目和结果True/False存入resultsMap
resultsMap.put(questionTitleAndResult[0], questionTitleAndResult[1]);
}
// 设置答案和结果映射到视图对象中
recordDetailVo.setAnswersMap(answersMap);
recordDetailVo.setResultsMap(resultsMap);
// 下面再计算正确答案的map
// 获取考试详情用于获取所有题目的ID
ExamDetailVo examDetailVo = getExamDetail(record.getExamId());
// 初始化一个列表用于存放所有题目的ID
List<String> questionIdList = new ArrayList<>();
// 将单选、多选、判断题的ID添加到列表中
questionIdList.addAll(Arrays.asList(examDetailVo.getRadioIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getCheckIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getJudgeIds()));
// 获取所有的问题对象
// 根据题目ID列表从数据库中查询所有题目
List<Question> questionList = questionRepository.findAllById(questionIdList);
// 初始化一个哈希映射用于存储正确答案
HashMap<String, List<String>> answersRightMap = new HashMap<>();
// 遍历所有题目,获取每个题目的正确答案
for (Question question : questionList) {
// 记得去掉最后可能出现的特殊字符
// 去除答案字符串末尾可能出现的特殊字符(如多余的"-"
String questionAnswerOptionIdsStr = replaceLastSeparator(question.getQuestionAnswerOptionIds());
// 分割字符串得到正确答案
String[] questionAnswerOptionIds = questionAnswerOptionIdsStr.split("-");
// 将题目ID和正确答案存入answersRightMap
answersRightMap.put(question.getQuestionId(), Arrays.asList(questionAnswerOptionIds));
}
// 设置正确答案映射到视图对象中
recordDetailVo.setAnswersRightMap(answersRightMap);
// 返回记录详情视图对象
return recordDetailVo;
}
/**
* -
* -_$
*
* @param str
* @return -
* @param str
* @return
*/
private String replaceLastSeparator(String str) {
// 获取字符串的最后一个字符。
String lastChar = str.substring(str.length() - 1);
// 题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔
// 检查最后一个字符是否是分隔符(-、_或$)。
if ("-".equals(lastChar) || "_".equals(lastChar) || "$".equals(lastChar)) {
// 如果最后一个字符是分隔符则使用StrUtil.sub方法移除它。
str = StrUtil.sub(str, 0, str.length() - 1);
}
// 返回处理后的字符串。
return str;
}
/**
* -
* --
*
* @param strList
* @return -
* @param strList
* @return -
*/
private String listConcat(List<String> strList) {
// 创建一个StringBuilder对象用于高效地构建最终的字符串
StringBuilder sb = new StringBuilder();
// 遍历字符串列表。
for (String str : strList) {
// 将当前字符串添加到StringBuilder中。
sb.append(str);
// 在当前字符串后添加一个-作为分隔符。
sb.append("-");
}
// 调用replaceLastSeparator方法移除StringBuilder中拼接字符串的最后一个-。
return replaceLastSeparator(sb.toString());
}
}

Loading…
Cancel
Save