xiangpei
2024-06-17 501ecc84678a081aad3ad598251a8a80e03d4116
开始考试:todo 随机试卷题目
7个文件已修改
2个文件已添加
305 ■■■■ 已修改文件
src/main/java/com/ycl/jxkg/controller/student/StudentExamController.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/vo/DoQuestionVO.java 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/vo/ExamSubmitVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/vo/FinishedQuestion.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/domain/vo/PaperFixQuestionVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/mapper/ExamMapper.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/ExamService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/service/impl/ExamServiceImpl.java 133 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ExamMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ycl/jxkg/controller/student/StudentExamController.java
@@ -1,14 +1,12 @@
package com.ycl.jxkg.controller.student;
import com.ycl.jxkg.base.Result;
import com.ycl.jxkg.domain.query.ExamQuery;
import com.ycl.jxkg.domain.vo.ExamSubmitVO;
import com.ycl.jxkg.service.ExamService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
/**
 * @author:xp
@@ -22,6 +20,28 @@
    private final ExamService examService;
    /**
     * 学员端考试分页
     *
     * @param query
     * @return
     */
    @PostMapping("/page")
    public Result examList(ExamQuery query) {
        return examService.studentPage(query);
    }
    /**
     * 开始考试
     *
     * @param id
     * @return
     */
    @PostMapping("/start/{id}")
    public Result start(@PathVariable("id") Integer id) {
        return examService.start(id);
    }
    /**
     * 主动提交试卷
     *
     * @param submitData
src/main/java/com/ycl/jxkg/domain/vo/DoQuestionVO.java
@@ -1,8 +1,10 @@
package com.ycl.jxkg.domain.vo;
import com.ycl.jxkg.domain.base.AbsVo;
import com.ycl.jxkg.domain.question.QuestionItemObject;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -10,25 +12,40 @@
 * @date:2024/6/13 15:27
 */
@Data
public class DoQuestionVO extends AbsVo {
public class DoQuestionVO extends FinishedQuestion {
    private Integer id;
    /** 题目类型 */
    private Integer questionType;
    /** 题干 */
    private String titleContent;
    private String title;
    /** 学员答案 */
    private String answer;
    /** 学员多选题答案 */
    private List<String> answerList;
    /** 题目答案 */
    private String questionAnswer;
    /** 选项json,后端使用 */
    /** 题目解析,老师阅卷使用 */
    private String analyze;
    /** 选项json */
    private String content;
    /** 正确与否,后端判断后填充该值 */
    /** 题目难度 */
    private Integer difficult;
    /** 针对单选、判断、多选给出正确与否 */
    private Boolean right;
    /** 题目分数 */
    private BigDecimal questionScore;
    /** 语音题音频 */
    private String audioFile;
    /** 音频原始名 */
    private String originalFile;
    /** 题目选项 */
    private List<QuestionItemVO> questionItemList;
    private List<QuestionItemObject> questionItemList;
}
src/main/java/com/ycl/jxkg/domain/vo/ExamSubmitVO.java
@@ -19,5 +19,5 @@
    private Integer doTime;
    /** 题目 */
    private List<DoQuestionVO> questionList;
    private List<PaperFixQuestionVO> paperQuestionList;
}
src/main/java/com/ycl/jxkg/domain/vo/FinishedQuestion.java
New file
@@ -0,0 +1,24 @@
package com.ycl.jxkg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
 * @author:xp
 * @date:2024/6/17 16:53
 */
@Data
public class FinishedQuestion {
    /** 学员答案 */
    private String answer;
    /** 学员多选题答案 */
    private List<String> answerList;
    /** 得分 */
    private BigDecimal score;
}
src/main/java/com/ycl/jxkg/domain/vo/PaperFixQuestionVO.java
New file
@@ -0,0 +1,26 @@
package com.ycl.jxkg.domain.vo;
import com.ycl.jxkg.domain.exam.PaperQuestion;
import lombok.Data;
import java.util.List;
/**
 * 大标题包裹题目列表
 *
 * @author:xp
 * @date:2024/6/17 16:35
 */
@Data
public class PaperFixQuestionVO {
    /** 大标题 */
    private String title;
    /** 题型 */
    private Integer questionType;
    /** 题目列表 */
    private List<DoQuestionVO> questionList;
}
src/main/java/com/ycl/jxkg/mapper/ExamMapper.java
@@ -29,6 +29,13 @@
    /**
    *  分页
    */
    IPage getPage(IPage page, @Param("query") ExamQuery query);
    IPage getPage(IPage page, @Param("query") ExamQuery query, @Param("userId") Integer userId);
    /**
     * 学生端分页
     *
     * @param page
     * @param query
     */
    IPage studentPage(IPage<ExamVO> page, @Param("query") ExamQuery query, @Param("userId") Integer userId);
}
src/main/java/com/ycl/jxkg/service/ExamService.java
@@ -88,4 +88,20 @@
     * @return
     */
    Result getMarkPaperInfo(Integer id);
    /**
     * 学生端分页
     *
     * @param query
     * @return
     */
    Result studentPage(ExamQuery query);
    /**
     * 学生端开始考试
     *
     * @param id
     * @return
     */
    Result start(Integer id);
}
src/main/java/com/ycl/jxkg/service/impl/ExamServiceImpl.java
@@ -5,11 +5,15 @@
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ycl.jxkg.context.WebContext;
import com.ycl.jxkg.domain.entity.*;
import com.ycl.jxkg.domain.exam.PaperFixQuestionDTO;
import com.ycl.jxkg.domain.vo.*;
import com.ycl.jxkg.domain.vo.admin.exam.ExamPaperEditRequestVO;
import com.ycl.jxkg.enums.ExamPaperTypeEnum;
import com.ycl.jxkg.enums.QuestionTypeEnum;
import com.ycl.jxkg.enums.general.ExamStatusEnum;
import com.ycl.jxkg.enums.general.ExamSubmitTempStatusEnum;
import com.ycl.jxkg.mapper.*;
import com.ycl.jxkg.service.ExamPaperService;
import com.ycl.jxkg.service.ExamService;
import com.ycl.jxkg.base.Result;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -22,10 +26,7 @@
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -44,6 +45,7 @@
    private final ExamSubmitTempMapper examSubmitTempMapper;
    private final ClassesUserMapper classesUserMapper;
    private final ExamPaperMapper examPaperMapper;
    private final ExamPaperService examPaperService;
    /**
     * 添加
@@ -110,7 +112,7 @@
    @Override
    public Result page(ExamQuery query) {
        IPage<ExamVO> page = PageUtil.getPage(query, ExamVO.class);
        baseMapper.getPage(page, query);
        baseMapper.getPage(page, query, webContext.getCurrentUser().getId());
        page.getRecords().stream().forEach(item -> {
            if (! StringUtils.hasText(item.getClassName())) {
                item.setClassName("班级不存在或被删除");
@@ -122,6 +124,76 @@
            }
        });
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
    @Override
    public Result studentPage(ExamQuery query) {
        IPage<ExamVO> page = PageUtil.getPage(query, ExamVO.class);
        baseMapper.studentPage(page, query, webContext.getCurrentUser().getId());
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
    @Override
    public Result start(Integer id) {
        Exam exam = baseMapper.selectById(id);
        if (Objects.isNull(exam)) {
            throw new RuntimeException("该考试不存在");
        }
        if (Objects.isNull(exam.getExamPaperId())) {
            throw new RuntimeException("考试未绑定试卷");
        }
        // 查出考试试卷
        ExamPaper examPaper = examPaperMapper.selectById(exam.getExamPaperId());
        if (Objects.isNull(examPaper)) {
            throw new RuntimeException("试卷不存在");
        }
        if (StringUtils.hasText(examPaper.getContent())) {
            throw new RuntimeException("试卷题目为空");
        }
        // 将题目转换为可临时保存的题目结构
        if (ExamPaperTypeEnum.Fixed.getCode().equals(examPaper.getPaperType())
                || ExamPaperTypeEnum.RandomOrder.getCode().equals(examPaper.getPaperType())) {
            // 转换
            List<PaperFixQuestionVO> data = this.coverTo(examPaper);
            return Result.ok().data(data);
        } else if (ExamPaperTypeEnum.Random.getCode().equals(examPaper.getPaperType())) {
            // todo 随机题目生成
        }
        return null;
    }
    /**
     * 将数据库存储的题目,转为可临时保存的题目结构
     *
     * @param examPaper 试卷
     * @return
     */
    private List<PaperFixQuestionVO> coverTo(ExamPaper examPaper) {
        List<PaperFixQuestionDTO> questionWarpList = JSON.parseArray(examPaper.getContent(), PaperFixQuestionDTO.class);
        return questionWarpList.stream().map(item -> {
            PaperFixQuestionVO vo = new PaperFixQuestionVO();
            vo.setTitle(item.getTitle());
            vo.setQuestionType(item.getQuestionType());
            List<DoQuestionVO> doQuestionVOS = item.getQuestionList().stream().map(question -> {
                DoQuestionVO doQuestionVO = new DoQuestionVO();
                doQuestionVO.setTitle(question.getTitle());
                doQuestionVO.setQuestionType(item.getQuestionType());
                doQuestionVO.setQuestionItemList(question.getItems());
                doQuestionVO.setId(question.getId());
                doQuestionVO.setOriginalFile(question.getOriginalFile());
                doQuestionVO.setAudioFile(question.getAudioFile());
                return doQuestionVO;
            }).collect(Collectors.toList());
            if (ExamPaperTypeEnum.RandomOrder.getCode().equals(examPaper.getPaperType())) {
                // 随序试卷打乱顺序
                Collections.shuffle(doQuestionVOS);
            }
            vo.setQuestionList(doQuestionVOS);
            return vo;
        }).collect(Collectors.toList());
    }
    /**
@@ -158,27 +230,31 @@
        if (Objects.isNull(exam)) {
            throw new RuntimeException("该考试不存在");
        }
        // 判断单选、多选、判断题对错
        List<Integer> questionIds = submitData.getQuestionList().stream().map(DoQuestionVO::getId).collect(Collectors.toList());
        List<Question> questionList = questionMapper.getAnswerInfo(questionIds);
        Map<Integer, Question> answerMap = questionList.stream().collect(Collectors.toMap(Question::getId, entity -> entity));
        submitData.getQuestionList().stream().forEach(item -> {
            Question question = answerMap.get(item.getId());
            if (Objects.nonNull(question)
                    && (QuestionTypeEnum.SingleChoice.getCode().equals(question.getQuestionType())
                    || QuestionTypeEnum.MultipleChoice.getCode().equals(question.getQuestionType())
                    || QuestionTypeEnum.TrueFalse.getCode().equals(question.getQuestionType())
            )) {
                String correct = question.getCorrect();
                if (QuestionTypeEnum.MultipleChoice.getCode().equals(question.getQuestionType())) {
                    // 如果是选择题,那么将答案转为list
                    List<String> answerList = JSON.parseArray(correct, String.class);
                    item.setRight(answerList.containsAll(item.getAnswerList()));
                } else {
                    item.setRight(question.getCorrect().equals(item.getAnswer()));
                }
            }
        });
//        // 判断单选、多选、判断题对错
//        List<Integer> questionIds = new ArrayList<>(24);
//        submitData.getPaperQuestionList().forEach(item -> {
//            List<Integer> ids = item.getQuestionList().stream().map(DoQuestionVO::getId).collect(Collectors.toList());
//            questionIds.addAll(ids);
//        });
//        List<Question> questionList = questionMapper.getAnswerInfo(questionIds);
//        Map<Integer, Question> answerMap = questionList.stream().collect(Collectors.toMap(Question::getId, entity -> entity));
//        submitData.getQuestionList().stream().forEach(item -> {
//            Question question = answerMap.get(item.getId());
//            if (Objects.nonNull(question)
//                    && (QuestionTypeEnum.SingleChoice.getCode().equals(question.getQuestionType())
//                    || QuestionTypeEnum.MultipleChoice.getCode().equals(question.getQuestionType())
//                    || QuestionTypeEnum.TrueFalse.getCode().equals(question.getQuestionType())
//            )) {
//                String correct = question.getCorrect();
//                if (QuestionTypeEnum.MultipleChoice.getCode().equals(question.getQuestionType())) {
//                    // 如果是选择题,那么将答案转为list
//                    List<String> answerList = JSON.parseArray(correct, String.class);
//                    item.setRight(answerList.containsAll(item.getAnswerList()));
//                } else {
//                    item.setRight(question.getCorrect().equals(item.getAnswer()));
//                }
//            }
//        });
        // 阅卷后才往exam_paper_answer保存考试成绩、以及保存到exam_paper_customer_answer
        // 现在只需要保存到一张临时表
        // 该接口是主动提交,所以状态都设置为完成,以便后续老师阅卷
@@ -208,7 +284,7 @@
                return;
            }
            one.setDoTime(submitData.getDoTime());
            one.setExamSubmit(JSON.toJSONString(submitData.getQuestionList()));
            one.setExamSubmit(JSON.toJSONString(submitData.getPaperQuestionList()));
            one.setCreateTime(new Date());
            one.setStatus(status);
            examSubmitTempMapper.updateById(one);
@@ -218,7 +294,7 @@
            examSubmitTemp.setDoTime(submitData.getDoTime());
            examSubmitTemp.setStatus(status);
            examSubmitTemp.setUserId(webContext.getCurrentUser().getId());
            examSubmitTemp.setExamSubmit(JSON.toJSONString(submitData.getQuestionList()));
            examSubmitTemp.setExamSubmit(JSON.toJSONString(submitData.getPaperQuestionList()));
            examSubmitTemp.setMarkPaperStatus(ExamSubmitTempStatusEnum.TEMP);
            examSubmitTempMapper.insert(examSubmitTemp);
        }
@@ -264,4 +340,5 @@
        markPaperVO.setSuggestTime(examPaper.getSuggestTime());
        return Result.ok().data(markPaperVO);
    }
}
src/main/resources/mapper/ExamMapper.xml
@@ -61,7 +61,7 @@
            TEP.name
        FROM
            t_exam TE
                INNER JOIN t_user TU ON TU.id = TE.teacher_id AND TU.deleted = 0
                INNER JOIN t_user TU ON TU.id = TE.teacher_id AND TU.deleted = 0 AND TU.id = #{userId}
                LEFT JOIN t_classes TC ON TC.id = TE.classes_id AND TC.deleted = 0
                LEFT JOIN t_exam_paper TEP ON TEP.id = TE.exam_paper_id AND TEP.deleted = 0
        WHERE
@@ -74,4 +74,34 @@
            </if>
    </select>
    <select id="studentPage" resultMap="BaseResultMap">
        SELECT
            TE.exam_name,
            TE.exam_paper_id,
            TE.classes_id,
            TE.exam_paper_type,
            TE.exam_place,
            TE.status,
            IF(TE.status != 'not_start', IF(TE.status == 'ing', 1, 2), 0) as orderc,
            TE.start_time,
            TE.end_time,
            TE.create_time,
            TE.teacher_id,
            TE.id,
            TC.class_name,
            TEP.name
        FROM
            t_exam TE
        INNER JOIN t_classes TC ON TC.id = TE.classes_id AND TC.deleted = 0
        INNER JOIN t_classes_user TCU ON TC.id = TCU.classes_id AND TC.deleted = 0 AND TCU.user_id = #{userId}
        INNER JOIN t_exam_paper TEP ON TEP.id = TE.exam_paper_id AND TEP.deleted = 0
        WHERE
            TE.deleted = 0
            <if test="query.examName != null and query.examName != ''">
                AND TE.exam_name like concat('%', #{query.examName}, '%')
            </if>
        ORDER BY orderc DESC
    </select>
</mapper>