luohairen
2024-11-07 027cde89bbd242f88c7d71ff602a12747b477ac5
src/main/java/com/ycl/jxkg/service/impl/ExamServiceImpl.java
@@ -2,17 +2,24 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycl.jxkg.base.Result;
import com.ycl.jxkg.base.SystemCode;
import com.ycl.jxkg.constants.ExamScoreConstant;
import com.ycl.jxkg.context.WebContext;
import com.ycl.jxkg.domain.base.AbsVo;
import com.ycl.jxkg.domain.entity.*;
import com.ycl.jxkg.domain.exam.PaperFixQuestionDTO;
import com.ycl.jxkg.domain.exam.PaperQuestion;
import com.ycl.jxkg.domain.exam.PaperQuestionSettingDTO;
import com.ycl.jxkg.domain.exam.PaperSettingItem;
import com.ycl.jxkg.domain.form.AddTimeForm;
import com.ycl.jxkg.domain.form.ExamForm;
import com.ycl.jxkg.domain.form.ForceSubmitForm;
@@ -26,13 +33,17 @@
import com.ycl.jxkg.enums.ExamPaperTypeEnum;
import com.ycl.jxkg.enums.QuestionTypeEnum;
import com.ycl.jxkg.enums.WebsocketCommendEnum;
import com.ycl.jxkg.enums.general.ClassesStatusEnum;
import com.ycl.jxkg.enums.general.ExamStatusEnum;
import com.ycl.jxkg.enums.general.ExamSubmitTempStatusEnum;
import com.ycl.jxkg.mapper.*;
import com.ycl.jxkg.rabbitmq.msg.ExamStatusMsg;
import com.ycl.jxkg.rabbitmq.product.Producer;
import com.ycl.jxkg.server.WebsocketServer;
import com.ycl.jxkg.service.ExamPaperScoreService;
import com.ycl.jxkg.service.ExamPaperService;
import com.ycl.jxkg.service.ExamService;
import com.ycl.jxkg.utils.DateTimeUtil;
import com.ycl.jxkg.utils.PageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
@@ -56,6 +67,8 @@
@RequiredArgsConstructor
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {
    private static final String ANSWER_SPLIT = ",";
    private final ExamMapper examMapper;
    private final WebContext webContext;
    private final QuestionMapper questionMapper;
@@ -67,7 +80,9 @@
    private final UserMapper userMapper;
    private final ExamPaperScoreMapper examPaperScoreMapper;
    private final ExamPaperScoreService examPaperScoreService;
    private static final String ANSWER_SPLIT = ",";
    private final Producer producer;
    /**
     * 添加
     *
@@ -79,7 +94,21 @@
        Exam entity = ExamForm.getEntityByForm(form, null);
        entity.setStatus(ExamStatusEnum.getStatusByTime(form.getStartTime(), form.getEndTime(), null));
        entity.setTeacherId(webContext.getCurrentUser().getId());
        baseMapper.insert(entity);
        // 查出考试试卷
        ExamPaper examPaper = examPaperMapper.selectById(entity.getExamPaperId());
        // 校验能否生成一张试卷
        try {
            this.checkGenExam(examPaper);
            // 设置乐观锁版本
            entity.setUpdateVersion(0);
            if (baseMapper.insert(entity) > 0) {
                this.sendMQ(entity, 0);
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
            return Result.fail(500, e.getMessage());
        }
        return Result.ok("添加成功");
    }
@@ -95,13 +124,70 @@
        // 为空抛IllegalArgumentException,做全局异常处理
        Assert.notNull(entity, "记录不存在");
        // 判断考试状态
        if (! ExamStatusEnum.NOT_START.equals(entity.getStatus())) {
        if (!ExamStatusEnum.NOT_START.equals(entity.getStatus())) {
            throw new RuntimeException("只能修改还未开始的考试");
        }
        BeanUtils.copyProperties(form, entity);
        entity.setStatus(ExamStatusEnum.getStatusByTime(form.getStartTime(), form.getEndTime(), null));
        baseMapper.updateById(entity);
        entity.setStatus(ExamStatusEnum.getStatusByTime(form.getStartTime(), form.getEndTime(), new Date()));
        // 查出考试试卷
        ExamPaper examPaper = examPaperMapper.selectById(entity.getExamPaperId());
        try {
            this.checkGenExam(examPaper);
            // 如果修改成功发送mq消息
            if (baseMapper.updateById(entity) > 0) {
                this.sendMQ(entity, entity.getUpdateVersion());
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
            return Result.fail(500, e.getMessage());
        }
        return Result.ok("修改成功");
    }
    /**
     * 发送mq消息
     *
     * @param entity  考试实体类
     * @param version 乐观锁版本
     */
    public void sendMQ(Exam entity, Integer version) {
        // 如果当前状态为未开始,则发送两条mq消息,一条设置状态为进行中,一条设置状态为已结束
        if (ExamStatusEnum.NOT_START.equals(entity.getStatus())) {
            // 进行状态消息
            ExamStatusMsg ingMsg = new ExamStatusMsg();
            ingMsg.setVersion(version);
            ingMsg.setExamId(entity.getId());
            ingMsg.setTargetStatus(ExamStatusEnum.ING);
            producer.examMsg(entity.getId(), JSON.toJSONString(ingMsg), DateTimeUtil.getTwoTimeDiffMS(entity.getStartTime(), new Date()));
            // 结束状态消息
            ExamStatusMsg finishedMsg = new ExamStatusMsg();
            finishedMsg.setVersion(version);
            finishedMsg.setExamId(entity.getId());
            finishedMsg.setTargetStatus(ExamStatusEnum.FINISHED);
            producer.examMsg(entity.getId(), JSON.toJSONString(finishedMsg), DateTimeUtil.getTwoTimeDiffMS(entity.getEndTime(), new Date()));
        } else if (ExamStatusEnum.ING.equals(entity.getStatus())) { // 当前是进行中状态则只需发送结束消息
            // 结束状态消息
            ExamStatusMsg finishedMsg = new ExamStatusMsg();
            finishedMsg.setVersion(0);
            finishedMsg.setExamId(entity.getId());
            finishedMsg.setTargetStatus(ExamStatusEnum.FINISHED);
            producer.examMsg(entity.getId(), JSON.toJSONString(finishedMsg), DateTimeUtil.getTwoTimeDiffMS(entity.getEndTime(), new Date()));
        }
    }
    /**
     * 根据考试的当前状态,得到下一个状态
     *
     * @param currentStatus
     * @return
     */
    public ExamStatusEnum getNextStatus(ExamStatusEnum currentStatus) {
        if (ExamStatusEnum.NOT_START.equals(currentStatus)) {
            return ExamStatusEnum.ING;
        } else {
            return ExamStatusEnum.FINISHED;
        }
    }
    /**
@@ -148,6 +234,7 @@
                item.setExamPaperId(null);
            }
        });
        //
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
@@ -156,9 +243,103 @@
    public Result studentPage(ExamQuery query) {
        IPage<ExamVO> page = PageUtil.getPage(query, ExamVO.class);
        baseMapper.studentPage(page, query, webContext.getCurrentUser().getId());
        for (ExamVO record : page.getRecords()) {
            ExamSubmitTemp one = new LambdaQueryChainWrapper<>(examSubmitTempMapper)
                    .eq(ExamSubmitTemp::getExamId, record.getId())
                    .eq(ExamSubmitTemp::getUserId, webContext.getCurrentUser().getId())
                    .one();
            record.setIsContinue(Objects.isNull(one) || ExamSubmitTempStatusEnum.temp.equals(one.getExamSubmit()));
        }
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
    /**
     * 测试试卷配置能否生成试卷
     *
     * @param examPaper
     */
    public void checkGenExam(ExamPaper examPaper) throws RuntimeException {
        // 试卷内容
        List<PaperFixQuestionVO> examData = new ArrayList<>();
        // 拿到题目副本数据
        List<QuestionAnswerCopyVO> questionAnswerCopyVOList = new ArrayList<>(32);
        if (ExamPaperTypeEnum.Fixed.getCode().equals(examPaper.getPaperType())
                || ExamPaperTypeEnum.RandomOrder.getCode().equals(examPaper.getPaperType())) {
            if (!StringUtils.hasText(examPaper.getContent())) {
                throw new RuntimeException("试卷题目为空");
            }
            // 转换
            examData = this.coverTo(examPaper, questionAnswerCopyVOList);
        } else if (ExamPaperTypeEnum.Random.getCode().equals(examPaper.getPaperType())) {
            // 根据随机试卷的配置,随机生成对应题目
            if (!StringUtils.hasText(examPaper.getContent())) {
                throw new RuntimeException("试卷配置异常,请联系老师");
            }
            List<PaperQuestionSettingDTO> paperSettingList = JSON.parseArray(examPaper.getContent(), PaperQuestionSettingDTO.class);
            examData = new ArrayList<>(8);
            for (PaperQuestionSettingDTO paperSetting : paperSettingList) {
                PaperFixQuestionVO paperFixQuestionVO = new PaperFixQuestionVO();
                paperFixQuestionVO.setTitle(paperSetting.getTitle());
                paperFixQuestionVO.setQuestionType(paperSetting.getQuestionType());
                //一个类型的题目list
                List<DoQuestionVO> childQuestionList = new ArrayList<>();
                List<PaperSettingItem> settingList = paperSetting.getSettingList();
                for (PaperSettingItem settingItem : settingList) {
                    Integer num = settingItem.getNum();
                    Integer difficult = settingItem.getDifficult();
                    if(0 == difficult) {
                        difficult = null;
                    }
                    //需要配置的题目数量为0则跳过
                    if (num == null || num == 0) continue;
                    List<Question> questions = questionMapper.getRandomQuestion(settingItem.getSubjectId(), paperSetting.getQuestionType(), difficult, settingItem.getNum());
                    if (org.springframework.util.CollectionUtils.isEmpty(questions) || settingItem.getNum() > questions.size()) {
                        throw new RuntimeException("试卷配置的题目数不足以生成试卷");
                    }
                    // 拿到题目后组装为可临时保存的题目结构
                    List<DoQuestionVO> childQuestions = questions.stream().map(item -> {
                        DoQuestionVO doQuestionVO = new DoQuestionVO();
                        doQuestionVO.setQuestionType(item.getQuestionType());
                        //从配置里拿题目分数
                        doQuestionVO.setQuestionScore(settingItem.getScore());
                        if (StringUtils.hasText(item.getContent())) {
                            QuestionObject questionObject = JSON.parseObject(item.getContent(), QuestionObject.class);
                            doQuestionVO.setQuestionItemList(questionObject.getQuestionItemObjects());
                            doQuestionVO.setTitle(questionObject.getTitleContent());
                        }
                        doQuestionVO.setId(item.getId());
                        doQuestionVO.setOriginalFile(item.getOriginalFile());
                        doQuestionVO.setAudioFile(item.getAudioFile());
                        // 题目副本
                        QuestionAnswerCopyVO copy = new QuestionAnswerCopyVO();
                        copy.setId(item.getId());
                        copy.setDifficult(item.getDifficult());
                        copy.setAnalyze(JSON.parseObject(item.getContent(), PaperQuestion.class).getAnalyze());
                        //填空的答案在Json里
                        if (QuestionTypeEnum.GapFilling.getCode().equals(item.getQuestionType())) {
                            List<String> gapAnswer = new ArrayList<>();
                            for (QuestionItemObject questionItemObject : doQuestionVO.getQuestionItemList()) {
                                gapAnswer.add(questionItemObject.getContent());
                            }
                            copy.setCorrect(String.join(ANSWER_SPLIT, gapAnswer));
                        } else {
                            copy.setCorrect(item.getCorrect());
                        }
                        questionAnswerCopyVOList.add(copy);
                        return doQuestionVO;
                    }).collect(Collectors.toList());
                    //添加到这个类型的list中
                    childQuestionList.addAll(childQuestions);
                }
                paperFixQuestionVO.setQuestionList(childQuestionList);
                if (! CollectionUtils.isEmpty(childQuestionList)) {
                    examData.add(paperFixQuestionVO);
                }
            }
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -187,6 +368,10 @@
                .eq(ExamSubmitTemp::getUserId, webContext.getCurrentUser().getId())
                .one();
        if (Objects.nonNull(hasJoin)) {
            // 允许提交后继续作答
            if(ExamSubmitTempStatusEnum.finish.equals(hasJoin.getStatus())){
                throw new RuntimeException("您已提交试卷,请勿重复作答");
            }
            StartExamVO startExamVO = new StartExamVO();
            startExamVO.setExamName(exam.getExamName());
            startExamVO.setId(hasJoin.getExamId());
@@ -223,50 +408,63 @@
                PaperFixQuestionVO paperFixQuestionVO = new PaperFixQuestionVO();
                paperFixQuestionVO.setTitle(paperSetting.getTitle());
                paperFixQuestionVO.setQuestionType(paperSetting.getQuestionType());
                // 拿到课目下某类题型的x道随机题
                List<Question> questions = questionMapper.getRandomQuestion(examPaper.getSubjectId(), paperSetting.getQuestionType(), paperSetting.getNum());
                if (paperSetting.getNum() > questions.size()) {
                    throw new RuntimeException("配置的题目数不足以生成试卷");
                }
                // 拿到题目后组装为可临时保存的题目结构
                List<DoQuestionVO> childQuestions = questions.stream().map(item -> {
                    DoQuestionVO doQuestionVO = new DoQuestionVO();
                    doQuestionVO.setTitle(item.getTitle());
                    doQuestionVO.setQuestionType(item.getQuestionType());
                    //从配置里拿题目分数
                    doQuestionVO.setQuestionScore(paperSetting.getScore());
                    if (StringUtils.hasText(item.getContent())) {
                        QuestionObject questionObject = JSON.parseObject(item.getContent(), QuestionObject.class);
                        doQuestionVO.setQuestionItemList(questionObject.getQuestionItemObjects());
                //一个类型的题目list
                List<DoQuestionVO> childQuestionList = new ArrayList<>();
                List<PaperSettingItem> settingList = paperSetting.getSettingList();
                for (PaperSettingItem settingItem : settingList) {
                    Integer num = settingItem.getNum();
                    Integer difficult = settingItem.getDifficult();
                    if(0 == difficult){
                        difficult = null;
                    }
                    doQuestionVO.setId(item.getId());
                    doQuestionVO.setOriginalFile(item.getOriginalFile());
                    doQuestionVO.setAudioFile(item.getAudioFile());
                    // 题目副本
                    QuestionAnswerCopyVO copy = new QuestionAnswerCopyVO();
                    copy.setId(item.getId());
                    copy.setDifficult(item.getDifficult());
                    copy.setAnalyze(JSON.parseObject(item.getContent(), PaperQuestion.class).getAnalyze());
                    //填空的答案在Json里
                    if (QuestionTypeEnum.GapFilling.getCode().equals(item.getQuestionType())) {
                        List<String> gapAnswer = new ArrayList<>();
                        for (QuestionItemObject questionItemObject : doQuestionVO.getQuestionItemList()) {
                            gapAnswer.add(questionItemObject.getContent());
                    //需要配置的题目数量为0则跳过
                    if (num == null || num == 0) continue;
                    List<Question> questions = questionMapper.getRandomQuestion(settingItem.getSubjectId(), paperSetting.getQuestionType(), difficult, settingItem.getNum());
                    if (org.springframework.util.CollectionUtils.isEmpty(questions) || settingItem.getNum() > questions.size()) {
                        throw new RuntimeException("配置的题目数不足以生成试卷");
                    }
                    // 拿到题目后组装为可临时保存的题目结构
                    List<DoQuestionVO> childQuestions = questions.stream().map(item -> {
                        DoQuestionVO doQuestionVO = new DoQuestionVO();
                        doQuestionVO.setQuestionType(item.getQuestionType());
                        //从配置里拿题目分数
                        doQuestionVO.setQuestionScore(settingItem.getScore());
                        if (StringUtils.hasText(item.getContent())) {
                            QuestionObject questionObject = JSON.parseObject(item.getContent(), QuestionObject.class);
                            doQuestionVO.setQuestionItemList(questionObject.getQuestionItemObjects());
                            doQuestionVO.setTitle(questionObject.getTitleContent());
                        }
                        copy.setCorrect(String.join(ANSWER_SPLIT, gapAnswer));
                    } else {
                        copy.setCorrect(item.getCorrect());
                    }
                    questionAnswerCopyVOList.add(copy);
                        doQuestionVO.setId(item.getId());
                        doQuestionVO.setOriginalFile(item.getOriginalFile());
                        doQuestionVO.setAudioFile(item.getAudioFile());
                    return doQuestionVO;
                }).collect(Collectors.toList());
                paperFixQuestionVO.setQuestionList(childQuestions);
                examData.add(paperFixQuestionVO);
                        // 题目副本
                        QuestionAnswerCopyVO copy = new QuestionAnswerCopyVO();
                        copy.setId(item.getId());
                        copy.setDifficult(item.getDifficult());
                        copy.setAnalyze(JSON.parseObject(item.getContent(), PaperQuestion.class).getAnalyze());
                        //填空的答案在Json里
                        if (QuestionTypeEnum.GapFilling.getCode().equals(item.getQuestionType())) {
                            List<String> gapAnswer = new ArrayList<>();
                            for (QuestionItemObject questionItemObject : doQuestionVO.getQuestionItemList()) {
                                gapAnswer.add(questionItemObject.getContent());
                            }
                            copy.setCorrect(String.join(ANSWER_SPLIT, gapAnswer));
                        } else {
                            copy.setCorrect(item.getCorrect());
                        }
                        questionAnswerCopyVOList.add(copy);
                        return doQuestionVO;
                    }).collect(Collectors.toList());
                    //添加到这个类型的list中
                    childQuestionList.addAll(childQuestions);
                }
                paperFixQuestionVO.setQuestionList(childQuestionList);
                if (! CollectionUtils.isEmpty(childQuestionList)) {
                    examData.add(paperFixQuestionVO);
                }
            }
            return Result.ok(startExamVO);
        }
        ExamSubmitTemp examSubmitTemp = new ExamSubmitTemp();
        examSubmitTemp.setExamId(id);
@@ -303,6 +501,23 @@
                doQuestionVO.setQuestionType(item.getQuestionType());
                //增加题目分数
                doQuestionVO.setQuestionScore(question.getScore());
                // 题目副本
                QuestionAnswerCopyVO copy = new QuestionAnswerCopyVO();
                copy.setId(question.getId());
                copy.setAnalyze(question.getAnalyze());
                copy.setDifficult(question.getDifficult());
                //填空的答案在Json里
                if (QuestionTypeEnum.GapFilling.getCode().equals(item.getQuestionType())) {
                    List<String> gapAnswer = new ArrayList<>();
                    for (QuestionItemObject questionItemObject : question.getItems()) {
                        gapAnswer.add(questionItemObject.getContent());
                    }
                    copy.setCorrect(String.join(ANSWER_SPLIT, gapAnswer));
                } else {
                    copy.setCorrect(question.getCorrect());
                }
                questionAnswerCopyVOList.add(copy);
                // 填空题需要抹除content(因为是答案)
                if (QuestionTypeEnum.GapFilling.getCode().equals(doQuestionVO.getQuestionType())) {
                    question.getItems().stream().forEach(option -> {
@@ -313,24 +528,6 @@
                doQuestionVO.setId(question.getId());
                doQuestionVO.setOriginalFile(question.getOriginalFile());
                doQuestionVO.setAudioFile(question.getAudioFile());
                // 题目副本
                QuestionAnswerCopyVO copy = new QuestionAnswerCopyVO();
                copy.setId(question.getId());
                copy.setAnalyze(question.getAnalyze());
                copy.setDifficult(question.getDifficult());
                //填空的答案在Json里
                if (QuestionTypeEnum.GapFilling.getCode().equals(item.getQuestionType())) {
                    List<String> gapAnswer = new ArrayList<>();
                    for (QuestionItemObject questionItemObject : doQuestionVO.getQuestionItemList()) {
                        gapAnswer.add(questionItemObject.getContent());
                    }
                    copy.setCorrect(String.join(ANSWER_SPLIT, gapAnswer));
                } else {
                    copy.setCorrect(question.getCorrect());
                }
                questionAnswerCopyVOList.add(copy);
                return doQuestionVO;
            }).collect(Collectors.toList());
            if (ExamPaperTypeEnum.RandomOrder.getCode().equals(examPaper.getPaperType())) {
@@ -361,10 +558,21 @@
     * @return
     */
    @Override
    public Result all() {
        List<Exam> entities = baseMapper.selectList(null);
    public Result all(ExamQuery query) {
        List<Exam> entities;
        // 判断如果examName为空或空字符串,则查询所有
        if (query.getExamName() == null || query.getExamName().isEmpty()) {
            entities = baseMapper.selectList(null);
        }else {
            entities = baseMapper.selectList(new LambdaQueryWrapper<>(Exam.class).like(Exam::getExamName,query.getExamName()));
        }
        List<ExamVO> vos = entities.stream()
                .map(entity -> ExamVO.getVoByEntity(entity, null))
                .map(entity -> {
                    ExamVO vo = new ExamVO();
                    vo = ExamVO.getVoByEntity(entity, vo);
                    vo.setStatus(entity.getStatus().getDesc());
                    return vo;
                })
                .collect(Collectors.toList());
        return Result.ok().data(vos);
    }
@@ -411,9 +619,6 @@
        // 现在只需要保存到一张临时表
        // 该接口是主动提交,所以状态都设置为完成,以便后续老师阅卷
        saveTempExam(submitData, ExamSubmitTempStatusEnum.finish);
        //TODO:考试状态设定为结束
        return Result.ok();
    }
@@ -439,7 +644,6 @@
        ExamSubmitTemp one = new LambdaQueryChainWrapper<>(examSubmitTempMapper)
                .eq(ExamSubmitTemp::getExamId, submitData.getId())
                .eq(ExamSubmitTemp::getUserId, webContext.getCurrentUser().getId())
                .eq(ExamSubmitTemp::getDeleted, 0)
                .one();
        if (Objects.nonNull(one)) {
@@ -449,9 +653,8 @@
            Date now = new Date();
            one.setExamSubmit(JSON.toJSONString(submitData.getTitleList()));
            one.setUpdateTime(now);
            long doTimeL = now.getTime() - one.getCreateTime().getTime();
            Integer doTime = (int) doTimeL;
            one.setDoTime(doTime);
            int doTimeInSeconds = (int) (now.getTime() - one.getCreateTime().getTime()) / 1000;
            one.setDoTime(doTimeInSeconds);
            one.setStatus(status);
            examSubmitTempMapper.updateById(one);
        } else {
@@ -477,7 +680,7 @@
            throw new RuntimeException("考试试卷不存在");
        }
        List<ExamSubmitTemp> examSubmitTempList = new LambdaQueryChainWrapper<>(examSubmitTempMapper)
                .eq(ExamSubmitTemp::getDeleted,0)
                .eq(ExamSubmitTemp::getDeleted, 0)
                .eq(ExamSubmitTemp::getExamId, id)
                .list();
        // 参考人数
@@ -488,12 +691,27 @@
        List<StudentExamInfoVO> studentExamList = classesUserMapper.getClassesUserList(exam.getClassesId());
        // 应考人数
        Integer shouldUserNum = studentExamList.size();
        studentExamList.stream().forEach(item -> {
            if (StringUtils.hasText(item.getExamSubmit())) {
                item.setQuestionList(JSON.parseArray(item.getExamSubmit(), DoQuestionVO.class));
        for (StudentExamInfoVO studentExamInfoVO : studentExamList) {
            Integer userId = studentExamInfoVO.getUserId();
            Optional<ExamSubmitTemp> first = examSubmitTempList.stream().filter(examSubmitTemp -> examSubmitTemp.getUserId().equals(userId)).findFirst();
            if (first.isPresent()) {
                ExamSubmitTemp examSubmitTemp = first.get();
                studentExamInfoVO.setMarkPaperStatus(examSubmitTemp.getMarkPaperStatus());
                studentExamInfoVO.setStatus(examSubmitTemp.getStatus());
                studentExamInfoVO.setDoTime(examSubmitTemp.getDoTime());
            } else {
                //不存在考试记录
                studentExamInfoVO.setStatus(ExamSubmitTempStatusEnum.temp);
                //根据Score表判断
                ExamPaperScore paperScore = examPaperScoreMapper.getByExamIdUserId(exam.getId(), userId);
                if(paperScore==null) {
                    studentExamInfoVO.setMarkPaperStatus(ExamSubmitTempStatusEnum.temp);
                }else {
                    studentExamInfoVO.setMarkPaperStatus(ExamSubmitTempStatusEnum.finish);
                }
                studentExamInfoVO.setDoTime(0);
            }
        });
        }
        MarkPaperVO markPaperVO = new MarkPaperVO();
        markPaperVO.setExamName(exam.getExamName());
@@ -511,20 +729,22 @@
    @Override
    public Result getMarkPaperInfo(Integer examId, Integer userId) {
        User student = userMapper.getUserById(userId);
        //如果已经阅过卷了,查成绩表
        Result<ExamPaperMarkVO> paperMarkVO1 = checkHasJudge(examId, student);
        if (paperMarkVO1 != null) return paperMarkVO1;
        ExamVO exam = examMapper.getById(examId);
        //学生答题表
        ExamSubmitTemp userExam = new LambdaQueryChainWrapper<>(examSubmitTempMapper)
                .eq(ExamSubmitTemp::getExamId, examId)
                .eq(ExamSubmitTemp::getUserId, userId)
                .one();
        if (Objects.isNull(userExam)) {
            throw new RuntimeException("该学员考试记录不存在");
            //缺考,学生没有做题信息
            ExamPaperMarkVO paperMarkVO = createVO(null, exam, student);
            return Result.ok(paperMarkVO);
        }
        //如果已经阅过卷了,查成绩表
        Result<ExamPaperMarkVO> paperMarkVO1 = checkHasJudge(examId, userId, userExam);
        if (paperMarkVO1 != null) return paperMarkVO1;
        User student = userMapper.getUserById(userId);
        ExamVO exam = examMapper.getById(examId);
        //封装阅卷基本数据
        ExamPaperMarkVO paperMarkVO = createVO(userExam, exam, student);
        List<QuestionAnswerCopyVO> answerList = JSONArray.parseArray(userExam.getQuestionAnswerCopy(), QuestionAnswerCopyVO.class);
@@ -543,28 +763,35 @@
        for (PaperFixQuestionVO titleItem : titleItems) {
            for (DoQuestionVO doQuestionVO : titleItem.getQuestionList()) {
                Integer questionId = doQuestionVO.getId();
                Optional<QuestionAnswerCopyVO> first = answerList.stream().filter(answer -> questionId.equals(answer.getId())).findFirst();
                if (first.isPresent()) {
                    QuestionAnswerCopyVO answerCopyVO = first.get();
                    doQuestionVO.setQuestionAnswer(answerCopyVO.getCorrect());
                    doQuestionVO.setAnalyze(answerCopyVO.getAnalyze());
                    doQuestionVO.setDifficult(answerCopyVO.getDifficult());
                if(questionId!=null) {
                    Optional<QuestionAnswerCopyVO> first = answerList.stream().filter(answer -> questionId.equals(answer.getId())).findFirst();
                    if (first.isPresent()) {
                        QuestionAnswerCopyVO answerCopyVO = first.get();
                        doQuestionVO.setQuestionAnswer(answerCopyVO.getCorrect());
                        doQuestionVO.setAnalyze(answerCopyVO.getAnalyze());
                        doQuestionVO.setDifficult(answerCopyVO.getDifficult());
                    }
                }
            }
        }
    }
    //检查是否阅卷
    private Result<ExamPaperMarkVO> checkHasJudge(Integer examId, Integer userId, ExamSubmitTemp userExam) {
        if (ExamSubmitTempStatusEnum.finish.equals(userExam.getMarkPaperStatus())) {
            ExamPaperScore examPaperScore = examPaperScoreMapper.getByExamIdUserId(examId, userId);
    private Result<ExamPaperMarkVO> checkHasJudge(Integer examId, User student) {
        ExamPaperScore examPaperScore = examPaperScoreMapper.getByExamIdUserId(examId, student.getId());
        if (examPaperScore != null) {
            ExamPaperMarkVO paperMarkVO = new ExamPaperMarkVO();
            BeanUtils.copyProperties(examPaperScore, paperMarkVO);
            paperMarkVO.setUserName(student.getRealName());
            paperMarkVO.setTotalScore(examPaperScore.getTotalScore() + "");
            paperMarkVO.setScore(examPaperScore.getScore() + "");
            List<PaperFixQuestionVO> paperFixQuestionVOS = JSONArray.parseArray(examPaperScore.getPaperContent(), PaperFixQuestionVO.class);
            paperMarkVO.setTitleItems(paperFixQuestionVOS);
            paperMarkVO.setNavbar(JSONArray.parseArray(examPaperScore.getNavbar(), ExamPaperMarkNavbarVO.class));
            if (!StringUtils.isEmpty(examPaperScore.getPaperContent())) {
                List<PaperFixQuestionVO> paperFixQuestionVOS = JSONArray.parseArray(examPaperScore.getPaperContent(), PaperFixQuestionVO.class);
                paperMarkVO.setTitleItems(paperFixQuestionVOS);
            }
            if (!StringUtils.isEmpty(examPaperScore.getNavbar())) {
                paperMarkVO.setNavbar(JSONArray.parseArray(examPaperScore.getNavbar(), ExamPaperMarkNavbarVO.class));
            }
            return Result.ok(paperMarkVO);
        }
        return null;
@@ -600,7 +827,7 @@
                    if (StringUtils.isEmpty(doQuestionVO.getQuestionAnswer())) {
                        return Result.fail(SystemCode.InnerError.getCode(), "题目id为:" + doQuestionVO.getId() + "的题目缺少答案,请先完善");
                    }
                    trueOrFalse(score,doQuestionVO, navbarVO, doQuestionVO.getQuestionAnswer().equals(doQuestionVO.getAnswer()));
                    score = trueOrFalse(score, doQuestionVO, navbarVO, doQuestionVO.getQuestionAnswer().equals(doQuestionVO.getAnswer()));
                }
                /* 如果是多选 */
                else if (QuestionTypeEnum.MultipleChoice.getCode().equals(questionType)) {
@@ -616,7 +843,7 @@
                    List<String> questionAnswerList = Arrays.asList(questionAnswer.split(","));
                    //学生答案为空,判断为错
                    if (CollectionUtils.isEmpty(answerList)) {
                        trueOrFalse(score,doQuestionVO, navbarVO, false);
                        score = trueOrFalse(score, doQuestionVO, navbarVO, false);
                        num++;
                        navbar.add(navbarVO);
                        continue;
@@ -627,7 +854,7 @@
                    Set<String> set2 = new HashSet<>(questionAnswerList);
                    //答案完全一致,满分
                    if (set1.equals(set2)) {
                        trueOrFalse(score,doQuestionVO, navbarVO, true);
                        score = trueOrFalse(score, doQuestionVO, navbarVO, true);
                        num++;
                        navbar.add(navbarVO);
                        continue;
@@ -637,20 +864,20 @@
                    }
                    //如果多选得分类型为 答错不得分
                    if (Integer.valueOf(DeductTypeEnum.AllCorrect.getCode()).equals(paperMarkVO.getDeductType())) {
                        trueOrFalse(score,doQuestionVO, navbarVO, false);
                        score = trueOrFalse(score, doQuestionVO, navbarVO, false);
                    }
                    //如果多选得分类型为 漏选得固定分值,包含错误选项不得分
                    else if (Integer.valueOf(DeductTypeEnum.PartCorrect.getCode()).equals(paperMarkVO.getDeductType())) {
                        //学生答案移除所有正确答案,如果还有元素说明包含错误选项
                        answerList.removeAll(questionAnswerList);
                        if (!CollectionUtils.isEmpty(answerList)) {
                            trueOrFalse(score,doQuestionVO, navbarVO, false);
                            score = trueOrFalse(score, doQuestionVO, navbarVO, false);
                        } else {
                            navbarVO.setRight(false);
                            doQuestionVO.setRight(false);
                            //漏选得固定分
                            score = score.add(paperMarkVO.getDeductScore());
                            doQuestionVO.setScore(paperMarkVO.getDeductScore());
                            score = score.add(doQuestionVO.getScore());
                        }
                    }
                    //如果多选得分类型为 每对一题得相应分值,包含错误选项不得分
@@ -658,12 +885,13 @@
                        //学生答案移除所有正确答案,如果还有元素说明包含错误选项
                        answerList.removeAll(questionAnswerList);
                        if (!CollectionUtils.isEmpty(answerList)) {
                            trueOrFalse(score,doQuestionVO, navbarVO, false);
                            score = trueOrFalse(score, doQuestionVO, navbarVO, false);
                        } else {
                            navbarVO.setRight(false);
                            doQuestionVO.setRight(false);
                            //漏选得分
                            doQuestionVO.setScore(paperMarkVO.getDeductScore().multiply(new BigDecimal(answerCount)));
                            score = score.add(doQuestionVO.getScore());
                        }
                    }
                }
@@ -681,7 +909,7 @@
                    List<String> questionAnswerList = Arrays.asList(questionAnswer.split(","));
                    //学生答案为空,判断为错
                    if (CollectionUtils.isEmpty(answerList)) {
                        trueOrFalse(score,doQuestionVO, navbarVO, false);
                        score = trueOrFalse(score, doQuestionVO, navbarVO, false);
                        num++;
                        navbar.add(navbarVO);
                        continue;
@@ -690,7 +918,7 @@
                    int questionAnswerCount = questionAnswerList.size();
                    //答案完全一致,满分
                    if (answerList.equals(questionAnswerList)) {
                        trueOrFalse(score,doQuestionVO, navbarVO, true);
                        score = trueOrFalse(score, doQuestionVO, navbarVO, true);
                    } else {
                        navbarVO.setRight(false);
                        doQuestionVO.setRight(false);
@@ -708,6 +936,7 @@
                        //填空得分
                        BigDecimal gapScore = scoreEach.multiply(new BigDecimal(count));
                        doQuestionVO.setScore(gapScore);
                        score = score.add(doQuestionVO.getScore());
                    }
                }
@@ -717,11 +946,12 @@
        }
        paperMarkVO.setTitleItems(titleItems);
        paperMarkVO.setNavbar(navbar);
        paperMarkVO.setScore(score + "");
        return null;
    }
    //设置全对或全错
    private void trueOrFalse(BigDecimal score,DoQuestionVO doQuestionVO, ExamPaperMarkNavbarVO orderVO, Boolean isCorrect) {
    private BigDecimal trueOrFalse(BigDecimal score, DoQuestionVO doQuestionVO, ExamPaperMarkNavbarVO orderVO, Boolean isCorrect) {
        if (isCorrect) {
            //正确
            orderVO.setRight(isCorrect);
@@ -734,20 +964,28 @@
            doQuestionVO.setRight(isCorrect);
            doQuestionVO.setScore(BigDecimal.ZERO);
        }
        return score;
    }
    //封装阅卷返回数据
    private ExamPaperMarkVO createVO(ExamSubmitTemp userExam, ExamVO exam, User student) {
        Integer paperId = exam.getExamPaperId();
        ExamPaper examPaper = examPaperMapper.selectById(paperId);
        ExamPaperMarkVO paperMarkVO = new ExamPaperMarkVO();
        BeanUtils.copyProperties(userExam, paperMarkVO);
        paperMarkVO.setPaperId(exam.getExamPaperId());
        paperMarkVO.setExamName(exam.getExamName());
        paperMarkVO.setPaperType(exam.getExamPaperType());
        paperMarkVO.setSubmitTime(userExam.getUpdateTime());
        if (userExam != null) {
            BeanUtils.copyProperties(userExam, paperMarkVO);
            paperMarkVO.setSubmitTime(userExam.getUpdateTime());
            paperMarkVO.setTitleItems(JSON.parseArray(userExam.getExamSubmit(), PaperFixQuestionVO.class));
        } else {
            //缺考,学生没有做题信息
            paperMarkVO.setExamId(exam.getId());
            paperMarkVO.setUserId(student.getId());
            paperMarkVO.setScore(BigDecimal.ZERO + "");
            paperMarkVO.setDoTime(0);
        }
        paperMarkVO.setUserName(student.getRealName());
        paperMarkVO.setTitleItems(JSON.parseArray(userExam.getExamSubmit(), PaperFixQuestionVO.class));
        paperMarkVO.setExamName(exam.getExamName());
        paperMarkVO.setPaperId(exam.getExamPaperId());
        paperMarkVO.setPaperType(exam.getExamPaperType());
        ExamPaper examPaper = examPaperMapper.selectById(exam.getExamPaperId());
        paperMarkVO.setTotalScore(examPaper.getScore() + "");
        paperMarkVO.setDeductType(examPaper.getDeductType());
        paperMarkVO.setDeductScore(examPaper.getDeductTypeScore());
@@ -766,13 +1004,24 @@
        examPaperScore.setTotalScore(new BigDecimal(examPaperMark.getTotalScore()));
        examPaperScore.setJudgeUser(userId);
        examPaperScore.setJudgeTime(new Date());
        examPaperScore.setPaperContent(JSON.toJSONString(examPaperMark.getTitleItems()));
        examPaperScore.setNavbar(JSON.toJSONString(examPaperMark.getNavbar()));
        if (!StringUtils.isEmpty(examPaperMark.getTitleItems())) {
            examPaperScore.setPaperContent(JSON.toJSONString(examPaperMark.getTitleItems()));
        }
        if (!StringUtils.isEmpty(examPaperMark.getNavbar())) {
            examPaperScore.setNavbar(JSON.toJSONString(examPaperMark.getNavbar()));
        }
        long questionCorrect = 0;
        long questionCount = 0;
        if (!CollectionUtils.isEmpty(examPaperMark.getNavbar())) {
            questionCorrect = examPaperMark.getNavbar().stream().filter(ExamPaperMarkNavbarVO::getRight).count();
            examPaperScore.setStatus(ExamScoreConstant.PRESENT);
            questionCorrect = examPaperMark.getNavbar().stream().filter(vo -> vo.getRight() != null && vo.getRight()).count();
            questionCount = examPaperMark.getNavbar().size();
        } else {
            //缺考查试卷配置
            Integer paperId = examPaperMark.getPaperId();
            ExamPaper examPaper = examPaperMapper.selectById(paperId);
            questionCount = examPaper.getNum();
            examPaperScore.setStatus(ExamScoreConstant.ABSENT);
        }
        examPaperScore.setQuestionCorrect(Integer.valueOf(questionCorrect + ""));
        examPaperScore.setQuestionCount(Integer.valueOf(questionCount + ""));
@@ -783,6 +1032,15 @@
            examPaperScoreMapper.updateById(examPaperScore);
        } else {
            examPaperScoreMapper.insert(examPaperScore);
            //修改考试里试卷状态为已阅卷
            ExamSubmitTemp userExam = new LambdaQueryChainWrapper<>(examSubmitTempMapper)
                    .eq(ExamSubmitTemp::getExamId, examPaperMark.getExamId())
                    .eq(ExamSubmitTemp::getUserId, examPaperMark.getUserId())
                    .one();
            if (userExam != null) {
                userExam.setMarkPaperStatus(ExamSubmitTempStatusEnum.finish);
                examSubmitTempMapper.updateById(userExam);
            }
        }
        return Result.ok();
    }
@@ -791,26 +1049,73 @@
    @Override
    public Result monitorList(ExamQuery query) {
        IPage<ExamSubmitTempVO> page = PageUtil.getPage(query, ExamSubmitTempVO.class);
        return Result.ok((examSubmitTempMapper.monitorList(page, query)));
        IPage<ExamSubmitTempVO> vo = examSubmitTempMapper.monitorList(page, query);
        return Result.ok(vo);
    }
    @Override
    public Result addTime(AddTimeForm form) {
        if (!websocketServer.checkUserOnline(form.getUserId())) {
            throw new RuntimeException("该学员不在线,无法执行该操作");
        }
        WebsocketDataVO websocket = new WebsocketDataVO();
        websocket.setCommend(WebsocketCommendEnum.DELAYED.getCommend());
        websocket.setCommend(WebsocketCommendEnum.DELAYED.getCommand());
        BigDecimal sed = BigDecimal.valueOf(60).multiply(form.getAddTimeM());
        form.setAddTimeM(sed);
        websocket.setData(form);
        // 发送websocket消息
        websocketServer.sendOneMessage(form.getUserId(), JSON.toJSONString(form));
        websocketServer.sendOneMessage(form.getUserId(), JSON.toJSONString(websocket));
        return Result.ok("操作成功");
    }
    @Override
    public Result forceSubmit(ForceSubmitForm form) {
        if (!websocketServer.checkUserOnline(form.getUserId())) {
            throw new RuntimeException("该学员不在线,无法执行该操作");
        }
        WebsocketDataVO websocket = new WebsocketDataVO();
        websocket.setCommend(WebsocketCommendEnum.FORCE_SUBMIT.getCommend());
        websocket.setCommend(WebsocketCommendEnum.FORCE_SUBMIT.getCommand());
        websocket.setData(form);
        // 发送websocket消息
        websocketServer.sendOneMessage(form.getUserId(), JSON.toJSONString(form));
        websocketServer.sendOneMessage(form.getUserId(), JSON.toJSONString(websocket));
        return Result.ok("操作成功");
    }
    /**
     * 作废
     *
     * @param id
     * @return {@link Result }
     * @author
     */
    @Override
    public Result cancel(Integer id) {
        new LambdaUpdateChainWrapper<>(examMapper)
                .eq(Exam::getId, id)
                .set(Exam::getStatus, ExamStatusEnum.CANCEL)
                .update();
        return Result.ok("作废成功");
    }
    @Override
    public Result recover(Integer id) {
        // 先查询当前考试记录的详细信息
        Exam examInfo = new LambdaQueryChainWrapper<>(examMapper)
                .eq(Exam::getId, id)
                .one();
        // 确定恢复后当前考试记录的状态
        Date currentTime = new Date();
        Date startTime = examInfo.getStartTime();
        Date endTime = examInfo.getEndTime();
        ExamStatusEnum statusByTime = ExamStatusEnum.getStatusByTime(startTime, endTime, currentTime);
        examInfo.setStatus(statusByTime);
        // 修改当前的考试状态
        new LambdaUpdateChainWrapper<>(examMapper)
                .eq(Exam::getId, id)
                .set(Exam::getStatus, examInfo.getStatus())
                .update();
        // 还原班级的考试信息
        return Result.ok("考试记录已经恢复正常");
    }
}