浏览代码

练习接口

xiatian 5 年之前
父节点
当前提交
de64120d5a

+ 73 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/PracticeController.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.oe.admin.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.admin.service.PracticeService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeCourseInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeDetailInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeRecordInfo;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月7日 上午11:12:28
+ * @company 	QMTH
+ * @description PracticeController.java
+ */
+@Api(tags = "练习相关接口")
+@RestController
+@RequestMapping("${$rmp.ctr.oe}/practice")
+public class PracticeController extends ControllerSupport{
+
+	@Autowired
+	private PracticeService practiceService;
+	
+	/**
+	 * 练习课程列表
+	 * @param examId
+	 * @param studentId
+	 * @return
+	 */
+	@ApiOperation(value = "练习课程列表")
+	@GetMapping("/queryPracticeCourseList")
+	public List<PracticeCourseInfo> queryPracticeCourseList(@RequestParam Long examId){
+		Check.isNull(examId, "examId不能为空");
+		User user = getAccessUser();
+		return practiceService.queryPracticeCourseList(examId, user.getUserId());
+	}
+	
+	/**
+	 * 课程练习记录详情
+	 * @param examStudentId
+	 * @return
+	 */
+	@ApiOperation(value = "课程练习记录详情")
+	@GetMapping("/queryPracticeRecordList")
+	public List<PracticeRecordInfo> queryPracticeRecordList(@RequestParam Long examStudentId){
+		Check.isNull(examStudentId, "examStudentId不能为空");
+		return practiceService.queryPracticeRecordList(examStudentId);
+	}
+	
+	/**
+	 * 单次练习答题情况统计
+	 * @return
+	 */
+	@ApiOperation(value = "单次练习答题情况统计")
+	@GetMapping("/getPracticeDetailInfo")
+	public PracticeDetailInfo getPracticeDetailInfo(@RequestParam Long examRecordDataId){
+		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+		return practiceService.getPracticeDetailInfo(examRecordDataId);
+	}
+	
+}

+ 38 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/PracticeService.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeCourseInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeDetailInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeRecordInfo;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月7日 上午11:04:59
+ * @company 	QMTH
+ * @description PracticeService.java
+ */
+public interface PracticeService {
+
+	/**
+	 * 练习课程列表
+	 * @param examId
+	 * @param studentId
+	 * @return
+	 */
+	public List<PracticeCourseInfo> queryPracticeCourseList(Long examId,Long studentId);
+	
+	/**
+	 * 课程练习记录详情
+	 * @param examStudentId
+	 * @return
+	 */
+	public List<PracticeRecordInfo> queryPracticeRecordList(Long examStudentId);
+	
+	/**
+	 * 单次练习答题情况统计
+	 * @return
+	 */
+	public PracticeDetailInfo getPracticeDetailInfo(Long examRecordDataId);
+}

+ 143 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/PaperStructInfo.java

@@ -0,0 +1,143 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.QuesStructType;
+
+
+/**
+ * 试卷结构。一套试卷结构包含多条数据,即多个大题
+ * Created by zhengmin on 2016/8/18.
+ */
+public class PaperStructInfo implements JsonSerializable{
+
+    private static final long serialVersionUID = 3386140600282845440L;
+
+    /**
+     * 序号
+     */
+    private int index;
+
+    /**
+     * 试题类型
+     */
+    private QuesStructType questionType;
+
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 总分
+     */
+    private double totalScore;
+
+    /**
+     * 题量,套题则算1题
+     */
+    private int count;
+
+    /**
+     * 试卷结构下的题目总量,套题和套题下的子题都算
+     */
+    private int questionCount;
+
+    /**
+     * 每小题分
+     */
+    private String score;
+    
+	/**
+	 * 答对数量
+	 */
+	private int succQuestionNum;
+	/**
+	 * 答错数量
+	 */
+	private int failQuestionNum;
+	/**
+	 * 未作答
+	 */
+	private Integer notAnsweredCount;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public String getScore() {
+        return score;
+    }
+
+    public void setScore(String score) {
+        this.score = score;
+    }
+
+    public QuesStructType getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(QuesStructType questionType) {
+        this.questionType = questionType;
+    }
+
+    public int getQuestionCount() {
+        return questionCount;
+    }
+
+    public void setQuestionCount(int questionCount) {
+        this.questionCount = questionCount;
+    }
+
+	public int getSuccQuestionNum() {
+		return succQuestionNum;
+	}
+
+	public void setSuccQuestionNum(int succQuestionNum) {
+		this.succQuestionNum = succQuestionNum;
+	}
+
+	public int getFailQuestionNum() {
+		return failQuestionNum;
+	}
+
+	public void setFailQuestionNum(int failQuestionNum) {
+		this.failQuestionNum = failQuestionNum;
+	}
+
+	public Integer getNotAnsweredCount() {
+		return notAnsweredCount;
+	}
+
+	public void setNotAnsweredCount(Integer notAnsweredCount) {
+		this.notAnsweredCount = notAnsweredCount;
+	}
+    
+}

+ 164 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/PracticeCourseInfo.java

@@ -0,0 +1,164 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class PracticeCourseInfo implements JsonSerializable{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3648502707781974505L;
+
+	private Long examId;
+	    
+    private String examName;
+    
+    private String examType;
+
+    private Long examStudentId;
+
+    private String courseName;
+
+    private String courseCode;
+
+    private String studentCode;
+
+    private String studentName;
+
+    /**
+     * 练习次数
+     */
+    private long practiceCount;
+    /**
+     * 最高正确率
+     */
+    private double maxObjectiveAccuracy;
+    /**
+     * 平均正确率
+     */
+    private double aveObjectiveAccuracy;
+
+    /**
+     * 最近正确率
+     */
+    private double recentObjectiveAccuracy;
+
+    private Date startTime;//考试时间范围开始时间
+
+    private Date endTime; //考试时间范围结束时间
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public String getExamName() {
+		return examName;
+	}
+
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
+	public String getStudentName() {
+		return studentName;
+	}
+
+	public void setStudentName(String studentName) {
+		this.studentName = studentName;
+	}
+
+	public long getPracticeCount() {
+		return practiceCount;
+	}
+
+	public void setPracticeCount(long practiceCount) {
+		this.practiceCount = practiceCount;
+	}
+
+	public double getMaxObjectiveAccuracy() {
+		return maxObjectiveAccuracy;
+	}
+
+	public void setMaxObjectiveAccuracy(double maxObjectiveAccuracy) {
+		this.maxObjectiveAccuracy = maxObjectiveAccuracy;
+	}
+
+	public double getAveObjectiveAccuracy() {
+		return aveObjectiveAccuracy;
+	}
+
+	public void setAveObjectiveAccuracy(double aveObjectiveAccuracy) {
+		this.aveObjectiveAccuracy = aveObjectiveAccuracy;
+	}
+
+	public double getRecentObjectiveAccuracy() {
+		return recentObjectiveAccuracy;
+	}
+
+	public void setRecentObjectiveAccuracy(double recentObjectiveAccuracy) {
+		this.recentObjectiveAccuracy = recentObjectiveAccuracy;
+	}
+
+	public Date getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(Date startTime) {
+		this.startTime = startTime;
+	}
+
+	public Date getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(Date endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getExamType() {
+		return examType;
+	}
+
+	public void setExamType(String examType) {
+		this.examType = examType;
+	}
+
+}

+ 73 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/PracticeDetailInfo.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月7日 上午10:23:19
+ * @company 	QMTH
+ * @description 答题情况统计
+ */
+public class PracticeDetailInfo implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1712307616138306152L;
+
+	private Long id;
+
+	private String courseName;
+
+	private String courseCode;
+	/**
+	 * 客观题正确率
+	 */
+	private double objectiveAccuracy;
+	
+	private List<PaperStructInfo> paperStructInfos;
+	
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public double getObjectiveAccuracy() {
+		return objectiveAccuracy;
+	}
+
+	public void setObjectiveAccuracy(double objectiveAccuracy) {
+		this.objectiveAccuracy = objectiveAccuracy;
+	}
+
+	public List<PaperStructInfo> getPaperStructInfos() {
+		return paperStructInfos;
+	}
+
+	public void setPaperStructInfos(List<PaperStructInfo> paperStructInfos) {
+		this.paperStructInfos = paperStructInfos;
+	}
+
+}

+ 135 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/PracticeRecordInfo.java

@@ -0,0 +1,135 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class PracticeRecordInfo implements JsonSerializable{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5420413503946583744L;
+
+	private Long id;
+
+	private String examName;
+
+	private String courseName;
+
+	private String courseCode;
+
+	private Date startTime;
+
+	private Date endTime;
+
+	/**
+	 * 考生考试使用的考试时长
+	 */
+	private Long usedExamTime;
+
+	/**
+	 * 题目数量
+	 */
+	private Integer totalQuestionCount;
+	/**
+	 * 答对数量
+	 */
+	private Integer succQuestionNum;
+	/**
+	 * 答错数量
+	 */
+	private Integer failQuestionNum;
+	/**
+	 * 未作答
+	 */
+	private Integer notAnsweredCount;
+	/**
+	 * 客观题正确率
+	 */
+	private Double objectiveAccuracy;
+	/**
+	 * 客观题数量
+	 */
+	private Integer objectiveNum;
+	
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	public Date getStartTime() {
+		return startTime;
+	}
+	public void setStartTime(Date startTime) {
+		this.startTime = startTime;
+	}
+	public Date getEndTime() {
+		return endTime;
+	}
+	public void setEndTime(Date endTime) {
+		this.endTime = endTime;
+	}
+	public Long getUsedExamTime() {
+		return usedExamTime;
+	}
+	public void setUsedExamTime(Long usedExamTime) {
+		this.usedExamTime = usedExamTime;
+	}
+	public Integer getTotalQuestionCount() {
+		return totalQuestionCount;
+	}
+	public void setTotalQuestionCount(Integer totalQuestionCount) {
+		this.totalQuestionCount = totalQuestionCount;
+	}
+	public Integer getSuccQuestionNum() {
+		return succQuestionNum;
+	}
+	public void setSuccQuestionNum(Integer succQuestionNum) {
+		this.succQuestionNum = succQuestionNum;
+	}
+	public Integer getFailQuestionNum() {
+		return failQuestionNum;
+	}
+	public void setFailQuestionNum(Integer failQuestionNum) {
+		this.failQuestionNum = failQuestionNum;
+	}
+	public Integer getNotAnsweredCount() {
+		return notAnsweredCount;
+	}
+	public void setNotAnsweredCount(Integer notAnsweredCount) {
+		this.notAnsweredCount = notAnsweredCount;
+	}
+	public Double getObjectiveAccuracy() {
+		return objectiveAccuracy;
+	}
+	public void setObjectiveAccuracy(Double objectiveAccuracy) {
+		this.objectiveAccuracy = objectiveAccuracy;
+	}
+	public Integer getObjectiveNum() {
+		return objectiveNum;
+	}
+	public void setObjectiveNum(Integer objectiveNum) {
+		this.objectiveNum = objectiveNum;
+	}
+	
+}

+ 266 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/PracticeServiceImpl.java

@@ -0,0 +1,266 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.base.utils.QuestionTypeUtil;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordQuestionsRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamScoreRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamQuestionEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordPaperStructEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordQuestionsEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamScoreEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordPaperStructService;
+import cn.com.qmth.examcloud.core.oe.admin.service.PracticeService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PaperStructInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeCourseInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeDetailInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.PracticeRecordInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.others.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
+import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionGroup;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+
+/**
+ * @author chenken
+ * @date 2018年9月7日 上午11:05:18
+ * @company QMTH
+ * @description PracticeServiceImpl.java
+ */
+@Service("practiceService")
+public class PracticeServiceImpl implements PracticeService {
+
+    @Autowired
+    private ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
+    @Autowired
+    private ExamScoreRepo examScoreRepo;
+
+    @Autowired
+    private ExamRecordQuestionsRepo examRecordQuestionsRepo;
+
+    @Autowired
+    private ExamRecordPaperStructService examRecordPaperStructService;
+
+    @Override
+    public List<PracticeCourseInfo> queryPracticeCourseList(Long examId, Long studentId) {
+        if (examId == null || studentId == null) {
+            return null;
+        }
+        List<ExamStudentEntity> examStudentList = examStudentRepo.findByExamIdAndStudentId(examId, studentId);
+        if (examStudentList == null || examStudentList.size() == 0) {
+            return null;
+        }
+        List<PracticeCourseInfo> practiceCourseInfos = new ArrayList<PracticeCourseInfo>();
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examId,studentId);
+        for (ExamStudentEntity examStudent : examStudentList) {
+            practiceCourseInfos.add(buildPracticeCourseInfo(examStudent, examBean));
+        }
+        return practiceCourseInfos;
+    }
+
+
+    private PracticeCourseInfo buildPracticeCourseInfo(ExamStudentEntity examStudent, ExamBean examBean) {
+        PracticeCourseInfo practiceCourseInfo = new PracticeCourseInfo();
+        practiceCourseInfo.setExamStudentId(examStudent.getExamStudentId());
+        practiceCourseInfo.setCourseCode(examStudent.getCourseCode());
+
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudent.getCourseId());
+        practiceCourseInfo.setCourseName(courseBean.getName());
+        practiceCourseInfo.setStudentCode(examStudent.getStudentCode());
+        practiceCourseInfo.setStudentName(examStudent.getStudentName());
+
+        practiceCourseInfo.setExamId(examBean.getId());
+        practiceCourseInfo.setExamName(examBean.getName());
+        practiceCourseInfo.setExamType(examBean.getExamType());
+        practiceCourseInfo.setStartTime(examBean.getBeginTime());
+        practiceCourseInfo.setEndTime(examBean.getEndTime());
+
+        List<ExamRecordDataEntity> examRecordEntities = examRecordDataRepo.findByExamStudentId(examStudent.getExamStudentId());
+
+        int allExamRecordNums = 0;
+        List<ExamScoreEntity> examScoreEntities = new ArrayList<ExamScoreEntity>();
+        for (ExamRecordDataEntity examRecordData : examRecordEntities) {
+            if (examRecordData.getExamRecordStatus() != ExamRecordStatus.EXAM_ING) {
+                allExamRecordNums++;
+                ExamScoreEntity examScore = examScoreRepo.findByExamRecordDataId(examRecordData.getId());
+                examScoreEntities.add(examScore);
+            }
+        }
+        double allObjectiveAccuracy = 0;//总正确率
+        double maxObjectiveAccuracy = 0;//最高客观题正确率
+        long recentObjectiveAccuracyId = 0;//最近ID
+        double recentObjectiveAccuracy = 0;//最近客观题正确率
+
+        for (ExamScoreEntity examScore : examScoreEntities) {
+            if (examScore != null) {
+                if (examScore.getObjectiveAccuracy() > maxObjectiveAccuracy) {
+                    maxObjectiveAccuracy = examScore.getObjectiveAccuracy();
+                }
+                if (examScore.getId() > recentObjectiveAccuracyId) {//判断是否为最近的
+                    recentObjectiveAccuracyId = examScore.getId();
+                    recentObjectiveAccuracy = examScore.getObjectiveAccuracy();
+                }
+                allObjectiveAccuracy = allObjectiveAccuracy + examScore.getObjectiveAccuracy();
+            }
+        }
+        //平均正确率
+        double aveObjectiveAccuracy = 0D;
+        if (allExamRecordNums > 0) {
+            aveObjectiveAccuracy = Double.valueOf(new DecimalFormat("#.00").format(allObjectiveAccuracy / allExamRecordNums));
+        }
+        practiceCourseInfo.setAveObjectiveAccuracy(aveObjectiveAccuracy);
+        practiceCourseInfo.setMaxObjectiveAccuracy(maxObjectiveAccuracy);
+        practiceCourseInfo.setRecentObjectiveAccuracy(recentObjectiveAccuracy);
+        practiceCourseInfo.setPracticeCount(allExamRecordNums);//练习总次数
+        return practiceCourseInfo;
+    }
+
+    @Override
+    public List<PracticeRecordInfo> queryPracticeRecordList(Long examStudentId) {
+        if (examStudentId == null) {
+            return null;
+        }
+        List<ExamRecordDataEntity> examRecordDataList = examRecordDataRepo.findByExamStudentId(examStudentId);
+        List<PracticeRecordInfo> practiceRecords = new ArrayList<PracticeRecordInfo>();
+        ExamStudentEntity examStudent = examStudentRepo.findByExamStudentId(examStudentId);
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudent.getCourseId());
+        for (ExamRecordDataEntity examRecordData : examRecordDataList) {
+            if (examRecordData.getExamRecordStatus() != ExamRecordStatus.EXAM_ING) {
+                practiceRecords.add(buildPracticeRecord(examRecordData, courseBean));
+            }
+        }
+        return practiceRecords;
+    }
+
+    private PracticeRecordInfo buildPracticeRecord(ExamRecordDataEntity examRecordData, CourseBean courseBean) {
+        PracticeRecordInfo practiceRecordInfo = new PracticeRecordInfo();
+
+        practiceRecordInfo.setId(examRecordData.getId());
+        practiceRecordInfo.setCourseCode(courseBean.getCode());
+        practiceRecordInfo.setCourseName(courseBean.getName());
+        practiceRecordInfo.setStartTime(examRecordData.getStartTime());
+        practiceRecordInfo.setEndTime(examRecordData.getEndTime());
+        practiceRecordInfo.setUsedExamTime(examRecordData.getUsedExamTime());
+
+        ExamScoreEntity examScoreEntity = examScoreRepo.findByExamRecordDataId(examRecordData.getId());
+        practiceRecordInfo.setObjectiveAccuracy(examScoreEntity.getObjectiveAccuracy());//客观题答对的比率
+        ExamRecordQuestionsEntity examRecordQuestions = examRecordQuestionsRepo.findByExamRecordDataId(examRecordData.getId());
+        List<ExamQuestionEntity> examQuestionEntities = examRecordQuestions.getExamQuestionEntities();
+        practiceRecordInfo = calculationExamQuestionSituationInfo(practiceRecordInfo, examQuestionEntities);
+
+        return practiceRecordInfo;
+    }
+
+    /**
+     * 计算答题情况
+     *
+     * @param practiceRecordInfo
+     * @param examQuestionEntities
+     * @return
+     */
+    private PracticeRecordInfo calculationExamQuestionSituationInfo(PracticeRecordInfo practiceRecordInfo, List<ExamQuestionEntity> examQuestionEntities) {
+        if (practiceRecordInfo == null) {
+            practiceRecordInfo = new PracticeRecordInfo();
+        }
+        int succQuestionNum = 0;//正确答题数
+        int failQuestionNum = 0;//错误答题数
+        int notAnsweredCount = 0;//未作答题数
+        for (ExamQuestionEntity examQuestionEntity : examQuestionEntities) {
+            if (examQuestionEntity.getIsAnswer() == null || !examQuestionEntity.getIsAnswer()) {
+                notAnsweredCount++;
+            } else {
+                //客观题判断正确错误
+                if (QuestionTypeUtil.isObjectiveQuestion(examQuestionEntity.getQuestionType())) {
+                    if (examQuestionEntity.getCorrectAnswer() != null
+                            && examQuestionEntity.getStudentAnswer() != null
+                            && examQuestionEntity.getCorrectAnswer().equals(examQuestionEntity.getStudentAnswer())) {
+                        succQuestionNum++;
+                    } else {
+                        failQuestionNum++;
+                    }
+                }
+            }
+        }
+        int objectiveQuestionsNum = 0;//客观题总数
+        for (ExamQuestionEntity examQuestionEntity : examQuestionEntities) {
+            if (QuestionTypeUtil.isObjectiveQuestion(examQuestionEntity.getQuestionType())) {
+                objectiveQuestionsNum++;
+            }
+        }
+        practiceRecordInfo.setObjectiveNum(objectiveQuestionsNum);
+        practiceRecordInfo.setSuccQuestionNum(succQuestionNum);
+        practiceRecordInfo.setFailQuestionNum(failQuestionNum);
+        practiceRecordInfo.setNotAnsweredCount(notAnsweredCount);
+        practiceRecordInfo.setTotalQuestionCount(examQuestionEntities.size());
+        return practiceRecordInfo;
+    }
+
+    @Override
+    public PracticeDetailInfo getPracticeDetailInfo(Long examRecordDataId) {
+        if (examRecordDataId == null) {
+            return null;
+        }
+        PracticeDetailInfo practiceDetailInfo = new PracticeDetailInfo();
+        //取出试卷结构
+        ExamRecordPaperStructEntity examRecordPaperStruct = examRecordPaperStructService.getExamRecordPaperStruct(examRecordDataId);
+        DefaultPaper defaultPaper = examRecordPaperStruct.getDefaultPaper();
+        List<PaperStructInfo> paperStructInfos = new ArrayList<PaperStructInfo>();
+        List<DefaultQuestionGroup> questionGroupList = defaultPaper.getQuestionGroupList();
+        //取出作答记录
+        ExamRecordQuestionsEntity examRecordQuestions = examRecordQuestionsRepo.findByExamRecordDataId(examRecordDataId);
+        List<ExamQuestionEntity> examQuestionEntities = examRecordQuestions.getExamQuestionEntities();
+        //遍历大题
+        int objectiveNum = 0;//各个大题中的客观题总数
+        int objectSuccessNum = 0;//各个大题中的客观题答对的总数量
+        for (int i = 0; i < questionGroupList.size(); i++) {
+            DefaultQuestionGroup defaultQuestionGroup = questionGroupList.get(i);
+            PaperStructInfo paperStructInfo = new PaperStructInfo();
+            paperStructInfo.setTitle(defaultQuestionGroup.getGroupName());//大题名称
+            paperStructInfo.setIndex(i + 1);//大题号
+            //使用大题号过滤
+            List<ExamQuestionEntity> examQuestionEntitiesOfMainQuestion = examQuestionEntities.stream().filter(o1 -> o1.getMainNumber().intValue() == paperStructInfo.getIndex()).collect(Collectors.toList());
+            //计算出作答情况
+            PracticeRecordInfo practiceRecordInfo = calculationExamQuestionSituationInfo(null, examQuestionEntitiesOfMainQuestion);
+
+            objectiveNum += practiceRecordInfo.getObjectiveNum();
+            objectSuccessNum += practiceRecordInfo.getSuccQuestionNum();
+
+            paperStructInfo.setQuestionCount(practiceRecordInfo.getTotalQuestionCount());
+            paperStructInfo.setSuccQuestionNum(practiceRecordInfo.getSuccQuestionNum());
+            paperStructInfo.setFailQuestionNum(practiceRecordInfo.getFailQuestionNum());
+            paperStructInfo.setNotAnsweredCount(practiceRecordInfo.getNotAnsweredCount());
+            paperStructInfos.add(paperStructInfo);
+        }
+        practiceDetailInfo.setPaperStructInfos(paperStructInfos);
+        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class);
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examRecordData.getCourseId());
+        practiceDetailInfo.setCourseCode(courseBean.getCode());
+        practiceDetailInfo.setCourseName(courseBean.getName());
+        practiceDetailInfo.setId(examRecordDataId);
+
+        double objectiveAccuracy = 0;//客观题答题正确率
+        if (objectiveNum > 0) {
+            objectiveAccuracy = Double.valueOf(new DecimalFormat("#.00").format(objectSuccessNum * 100D / objectiveNum));
+        }
+        practiceDetailInfo.setObjectiveAccuracy(objectiveAccuracy);
+        return practiceDetailInfo;
+    }
+
+}