Sfoglia il codice sorgente

交卷后续处理,代码重构及bug fix

lideyin 5 anni fa
parent
commit
bfbb00fd29

+ 30 - 0
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/api/provider/ExamRecordDataCloudServiceProvider.java

@@ -1,7 +1,12 @@
 package cn.com.qmth.examcloud.core.oe.student.api.provider;
 
+import java.util.Date;
 import java.util.List;
 
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcExamScoreReq;
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcFaceBiopsyResultReq;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcExamScoreResp;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcFaceBiopsyResultResp;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -50,5 +55,30 @@ public class ExamRecordDataCloudServiceProvider extends ControllerSupport implem
         return res;
     }
 
+	/**
+	 * 计算活体检测结果
+	 *
+	 * @param req
+	 * @return
+	 */
+	@ApiOperation(value = "计算活体检测结果")
+	@PostMapping("/calcFaceBiopsyResult")
+	@Override
+	public CalcFaceBiopsyResultResp calcFaceBiopsyResult(@RequestBody CalcFaceBiopsyResultReq req) {
+		return examRecordDataService.calcFaceBiopsyResult(req);
+	}
+
+	/**
+	 * 计算考试分数
+	 *
+	 * @param req
+	 * @return
+	 */
+	@ApiOperation(value = "计算考试分数")
+	@PostMapping("/calcExamScore")
+	@Override
+	public CalcExamScoreResp calcExamScore(@RequestBody CalcExamScoreReq req) {
+		return examRecordDataService.calcExamScore(req);
+	}
 
 }

+ 25 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamRecordDataEntity.java

@@ -196,6 +196,16 @@ public class ExamRecordDataEntity extends JpaEntity {
      */
     private Double succPercent;
 
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+
     /**
      * 数据同步状态
      */
@@ -483,4 +493,19 @@ public class ExamRecordDataEntity extends JpaEntity {
         this.batchNum = batchNum;
     }
 
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public Double getBaiduFaceLivenessSuccessPercent() {
+        return baiduFaceLivenessSuccessPercent;
+    }
+
+    public void setBaiduFaceLivenessSuccessPercent(Double baiduFaceLivenessSuccessPercent) {
+        this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+    }
 }

+ 23 - 10
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamRecordDataService.java

@@ -2,8 +2,12 @@ package cn.com.qmth.examcloud.core.oe.student.service;
 
 import java.util.List;
 
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcExamScoreReq;
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcFaceBiopsyResultReq;
 import cn.com.qmth.examcloud.core.oe.student.api.request.GetExamRecordDataIdsReq;
 import cn.com.qmth.examcloud.core.oe.student.api.request.UpdateExamRecordDataBatchNumReq;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcExamScoreResp;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcFaceBiopsyResultResp;
 import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
 import cn.com.qmth.examcloud.support.cache.bean.ExamSettingsCacheBean;
 import cn.com.qmth.examcloud.support.examing.ExamRecordData;
@@ -30,30 +34,39 @@ public interface ExamRecordDataService {
      *            是否全客观题
      * @return
      */
-    public ExamRecordData createExamRecordData(ExamingSession examingSession, ExamSettingsCacheBean examBean, CourseCacheBean courseBean,
-                                               String basePaperId);
-    
+    ExamRecordData createExamRecordData(ExamingSession examingSession, ExamSettingsCacheBean examBean, CourseCacheBean courseBean,
+                                        String basePaperId);
+
     /**
-     * 保存
-     * @param timeout   秒
+     *
+     * @param examRecordDataId
+     * @param data
      */
-    public void saveExamRecordDataCache(Long examRecordDataId,ExamRecordData data);
+    void saveExamRecordDataCache(Long examRecordDataId, ExamRecordData data);
 
     /**
      * 获取
      * @param examRecordDataId
      * @return
      */
-    public ExamRecordData getExamRecordDataCache(Long examRecordDataId);
+    ExamRecordData getExamRecordDataCache(Long examRecordDataId);
 
     /**
      * 删除
      * @param examRecordDataId
      */
-    public void deleteExamRecordDataCache(Long examRecordDataId);
+    void deleteExamRecordDataCache(Long examRecordDataId);
     
-    public List<Long> getExamRecordDataIds(GetExamRecordDataIdsReq req);
+    List<Long> getExamRecordDataIds(GetExamRecordDataIdsReq req);
     
-    public void updateExamRecordDataBatchNum(UpdateExamRecordDataBatchNumReq req);
+    void updateExamRecordDataBatchNum(UpdateExamRecordDataBatchNumReq req);
+
+    /**
+     * 计算活体检测结果
+     * @param req
+     * @return
+     */
+    CalcFaceBiopsyResultResp calcFaceBiopsyResult(CalcFaceBiopsyResultReq req);
 
+    CalcExamScoreResp calcExamScore(CalcExamScoreReq req);
 }

+ 185 - 23
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamRecordDataServiceImpl.java

@@ -1,9 +1,32 @@
 package cn.com.qmth.examcloud.core.oe.student.service.impl;
 
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcExamScoreReq;
+import cn.com.qmth.examcloud.core.oe.student.api.request.CalcFaceBiopsyResultReq;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcExamScoreResp;
+import cn.com.qmth.examcloud.core.oe.student.api.response.CalcFaceBiopsyResultResp;
+import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamQuestion;
+import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamRecordQuestions;
+import cn.com.qmth.examcloud.core.oe.student.base.utils.QuestionTypeUtil;
+import cn.com.qmth.examcloud.core.oe.student.dao.ExamFaceLivenessVerifyRepo;
+import cn.com.qmth.examcloud.core.oe.student.dao.FaceBiopsyRepo;
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamFaceLivenessVerifyEntity;
+import cn.com.qmth.examcloud.core.oe.student.dao.entity.FaceBiopsyEntity;
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceVerifyResult;
+import cn.com.qmth.examcloud.core.oe.student.service.ExamRecordQuestionsService;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.QuestionAnswerCacheBean;
+import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
+import cn.com.qmth.examcloud.support.enums.IsSuccess;
+import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.assertj.core.internal.bytebuddy.asm.Advice;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -39,6 +62,18 @@ public class ExamRecordDataServiceImpl implements ExamRecordDataService {
     @Autowired
     private ExamRecordDataRepo examRecordDataRepo;
 
+    @Autowired
+    private FaceBiopsyRepo faceBiopsyRepo;
+
+    @Autowired
+    private ExamFaceLivenessVerifyRepo examFaceLivenessVerifyRepo;
+
+    @Autowired
+    private ExamRecordQuestionsService examRecordQuestionsService;
+
+    @Autowired
+    private ExamRecordDataService examRecordDataService;
+
     @Transactional
     @Override
     public ExamRecordData createExamRecordData(ExamingSession examingSession, ExamSettingsCacheBean examBean,
@@ -76,19 +111,19 @@ public class ExamRecordDataServiceImpl implements ExamRecordDataService {
     @Override
     public void saveExamRecordDataCache(Long examRecordDataId, ExamRecordData data) {
         String key = RedisKeyHelper.getBuilder().examRecordDataKey(examRecordDataId);
-        redisClient.set(key + examRecordDataId, data, -1);
+        redisClient.set(key, data, -1);
     }
 
     @Override
     public ExamRecordData getExamRecordDataCache(Long examRecordDataId) {
         String key = RedisKeyHelper.getBuilder().examRecordDataKey(examRecordDataId);
-        return redisClient.get(key + examRecordDataId, ExamRecordData.class);
+        return redisClient.get(key, ExamRecordData.class);
     }
 
     @Override
     public void deleteExamRecordDataCache(Long examRecordDataId) {
         String key = RedisKeyHelper.getBuilder().examRecordDataKey(examRecordDataId);
-        redisClient.delete(key + examRecordDataId);
+        redisClient.delete(key);
     }
 
     private ExamRecordData of(ExamRecordDataEntity et) {
@@ -135,39 +170,166 @@ public class ExamRecordDataServiceImpl implements ExamRecordDataService {
 
     @Override
     public List<Long> getExamRecordDataIds(GetExamRecordDataIdsReq req) {
-        Long startId=req.getStartId();
-        Long batchNum=req.getBatchNum();
-        Integer size=req.getSize();
-        if(startId==null) {
-            throw new StatusException("1001", "startId 不能为空"); 
+        Long startId = req.getStartId();
+        Long batchNum = req.getBatchNum();
+        Integer size = req.getSize();
+        if (startId == null) {
+            throw new StatusException("1001", "startId 不能为空");
         }
-        if(batchNum==null) {
-            throw new StatusException("1002", "batchNum 不能为空"); 
+        if (batchNum == null) {
+            throw new StatusException("1002", "batchNum 不能为空");
         }
-        if(size==null) {
-            throw new StatusException("1003", "size 不能为空"); 
+        if (size == null) {
+            throw new StatusException("1003", "size 不能为空");
         }
-        if(size>200) {
-            throw new StatusException("1004", "size 最大为200"); 
+        if (size > 200) {
+            throw new StatusException("1004", "size 最大为200");
         }
-        List<ExamRecordDataEntity> list= examRecordDataRepo.getLimitExamRecordDataList(batchNum, startId, size);
-        List<Long> ids=list.stream().map(et->et.getId()).collect(Collectors.toList());
+        List<ExamRecordDataEntity> list = examRecordDataRepo.getLimitExamRecordDataList(batchNum, startId, size);
+        List<Long> ids = list.stream().map(et -> et.getId()).collect(Collectors.toList());
         return ids;
     }
 
     @Transactional
     @Override
     public void updateExamRecordDataBatchNum(UpdateExamRecordDataBatchNumReq req) {
-        List<Long> ids=req.getIds();
-        Long batchNum=req.getBatchNum();
-        if(ids==null) {
-            throw new StatusException("2001", "ids 不能为空"); 
+        List<Long> ids = req.getIds();
+        Long batchNum = req.getBatchNum();
+        if (ids == null) {
+            throw new StatusException("2001", "ids 不能为空");
         }
-        if(batchNum==null) {
-            throw new StatusException("2002", "batchNum 不能为空"); 
+        if (batchNum == null) {
+            throw new StatusException("2002", "batchNum 不能为空");
         }
-        for(Long id:ids) {
+        for (Long id : ids) {
             examRecordDataRepo.updateBatchNumById(batchNum, id);
         }
     }
+
+    /**
+     * 计算活体检测结果
+     *
+     * @param req
+     * @return
+     */
+    @Override
+    public CalcFaceBiopsyResultResp calcFaceBiopsyResult(CalcFaceBiopsyResultReq req) {
+        ExamRecordData examRecordData = this.getExamRecordDataCache(req.getExamRecordDataId());
+
+        //如果活检结果不为空,且活检失败,则判定为违纪有警告
+        if (null != examRecordData.getFaceVerifyResult()) {
+            if (IsSuccess.FAILED == examRecordData.getFaceVerifyResult()) {
+                return new CalcFaceBiopsyResultResp(true, true, IsSuccess.FAILED);
+            }
+            return new CalcFaceBiopsyResultResp();
+        }
+
+        //如果考试记录中的活检记录为空,需要再次计算一次,并更新到考试记录中
+        else {
+            Long examId = examRecordData.getExamId();
+            Long rootOrgId = examRecordData.getRootOrgId();
+            Long studentId = examRecordData.getStudentId();
+
+            //是否进行活体检测
+            if (FaceBiopsyHelper.isFaceVerify(rootOrgId, examId, studentId)) {
+                FaceBiopsyScheme faceBiopsyScheme = FaceBiopsyHelper.getFaceBiopsyScheme(rootOrgId);
+                //新活体检测方案
+                if (faceBiopsyScheme == FaceBiopsyScheme.NEW) {
+                    FaceBiopsyEntity faceBiopsy = faceBiopsyRepo.findByExamRecordDataId(examRecordData.getId());
+
+                    //如果活检结果最终为true,只需要更新活检状态即可
+                    if (faceBiopsy != null && faceBiopsy.getResult() != null && faceBiopsy.getResult()) {
+                        return new CalcFaceBiopsyResultResp(IsSuccess.SUCCESS);
+                    }
+
+                    return new CalcFaceBiopsyResultResp(true, true, IsSuccess.FAILED);
+                }
+                //旧活体检测方案
+                else {
+                    List<ExamFaceLivenessVerifyEntity> faceVerifies =
+                            examFaceLivenessVerifyRepo.findByExamRecordDataIdOrderById(examRecordData.getId());
+
+                    if (null != faceVerifies && faceVerifies.size() > 0) {
+                        ExamFaceLivenessVerifyEntity latestFaceVerify = faceVerifies.get(faceVerifies.size() - 1);
+                        //最后一次活检成功,则认为成功,并更新考试记录相关属性
+                        if (latestFaceVerify != null && latestFaceVerify.getVerifyResult() != null &&
+                                latestFaceVerify.getVerifyResult() == FaceVerifyResult.VERIFY_SUCCESS) {
+
+                            return new CalcFaceBiopsyResultResp(IsSuccess.SUCCESS);
+                        }
+                    }
+
+                    return new CalcFaceBiopsyResultResp(true, true, IsSuccess.FAILED);
+                }
+            }
+            return new CalcFaceBiopsyResultResp();
+        }
+    }
+
+    @Override
+    public CalcExamScoreResp calcExamScore(CalcExamScoreReq req) {
+
+        Long examRecordDataId = req.getExamRecordDataId();
+        //FIXME this is a bug .to be fix
+        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.getExamRecordQuestions(examRecordDataId,0);
+        // 考生作答记录明细
+        List<ExamQuestion> examQuestionList = examRecordQuestions.getExamQuestions();
+
+        double studentObjectiveScoreTotal = 0d;//客观题总分
+        int questionSuccessNum = 0;//答题正确数
+        int objectiveQuestionsNum = 0;//客观题总数
+
+        //计算客观题总得分和答题正确数
+        for (int i = 0; i < examQuestionList.size(); i++) {
+            ExamQuestion examQuestion = examQuestionList.get(i);
+
+            if (QuestionTypeUtil.isObjectiveQuestion(examQuestion.getQuestionType())) {
+                objectiveQuestionsNum++;
+
+                //如果标准答案为空,则更新标准答案
+                if (StringUtils.isEmpty(examQuestion.getCorrectAnswer())) {
+                    updateCorrectAnswer(examRecordDataId, examQuestion);
+                }
+
+                //如果学生作答正确,则客观分累加,答题数累加
+                if (examQuestion.getStudentAnswer() != null
+                        && examQuestion.getCorrectAnswer() != null
+                        && examQuestion.getStudentAnswer().equals(examQuestion.getCorrectAnswer())) {
+                    double questionScore = examQuestion.getQuestionScore().doubleValue();
+                    BigDecimal bigDecimalQuestionScore = new BigDecimal(Double.toString(questionScore));
+                    BigDecimal bigDecimalObjectiveScoreTotal = new BigDecimal(Double.toString(studentObjectiveScoreTotal));
+
+                    studentObjectiveScoreTotal = bigDecimalQuestionScore.add(bigDecimalObjectiveScoreTotal).doubleValue();
+                    questionSuccessNum++;
+                }
+            }
+        }
+
+        //计算客观题答题正确率
+        double objectiveAccuracy = 0;
+        if (questionSuccessNum > 0 && objectiveQuestionsNum > 0) {
+            objectiveAccuracy = Double.valueOf(new DecimalFormat("#.00").format(questionSuccessNum * 100D / objectiveQuestionsNum));
+        }
+
+        CalcExamScoreResp resp = new CalcExamScoreResp();
+        resp.setObjectiveScore(studentObjectiveScoreTotal);
+        resp.setObjectiveAccuracy(objectiveAccuracy);
+        resp.setTotalScore(studentObjectiveScoreTotal);//交卷时,总分=客观分得分
+
+        return resp;
+    }
+
+    /**
+     * 更新客观题答案
+     *
+     * @param examRecordDataId
+     * @param examQuestion
+     */
+    private void updateCorrectAnswer(Long examRecordDataId, ExamQuestion examQuestion) {
+        QuestionAnswerCacheBean questionAnswerCache = CacheHelper.getQuestionAnswer(examQuestion.getQuestionId());
+        List<String> rightAnswerList = questionAnswerCache.getRightAnswers();
+        examQuestion.setCorrectAnswer(rightAnswerList.get(examQuestion.getOrder() - 1));
+
+        examRecordQuestionsService.saveExamQuestion(examRecordDataId, examQuestion.getOrder(), examQuestion);
+    }
 }

+ 3 - 3
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamRecordPaperStructServiceImpl.java

@@ -18,18 +18,18 @@ public class ExamRecordPaperStructServiceImpl implements ExamRecordPaperStructSe
     @Override
     public void saveExamRecordPaperStruct(Long examRecordDataId, ExamRecordPaperStruct paper) {
         String key = RedisKeyHelper.getBuilder().studentPaperKey(examRecordDataId);
-        redisClient.set(key + examRecordDataId, paper,-1);
+        redisClient.set(key , paper,-1);
     }
 
     @Override
     public ExamRecordPaperStruct getExamRecordPaperStruct(Long examRecordDataId) {
         String key = RedisKeyHelper.getBuilder().studentPaperKey(examRecordDataId);
-        return redisClient.get(key + examRecordDataId, ExamRecordPaperStruct.class);
+        return redisClient.get(key , ExamRecordPaperStruct.class);
     }
 
     @Override
     public void deleteExamRecordPaperStruct(Long examRecordDataId) {
         String key = RedisKeyHelper.getBuilder().studentPaperKey(examRecordDataId);
-        redisClient.delete(key + examRecordDataId);
+        redisClient.delete(key );
     }
 }

+ 3 - 3
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamRecordQuestionsServiceImpl.java

@@ -60,19 +60,19 @@ public class ExamRecordQuestionsServiceImpl implements ExamRecordQuestionsServic
     @Override
     public void saveExamQuestion(Long examRecordDataId, Integer order, ExamQuestion question) {
         String key = RedisKeyHelper.getBuilder().studentAnswerKey(examRecordDataId, order);
-        redisClient.set(key + examRecordDataId, question, -1);
+        redisClient.set(key , question, -1);
     }
 
     @Override
     public ExamQuestion getExamQuestion(Long examRecordDataId, Integer order) {
         String key = RedisKeyHelper.getBuilder().studentAnswerKey(examRecordDataId, order);
-        return redisClient.get(key + examRecordDataId, ExamQuestion.class);
+        return redisClient.get(key , ExamQuestion.class);
     }
 
     @Override
     public void deleteExamQuestion(Long examRecordDataId, Integer order) {
         String key = RedisKeyHelper.getBuilder().studentAnswerKey(examRecordDataId, order);
-        redisClient.delete(key + examRecordDataId);
+        redisClient.delete(key );
     }
 
     @Override

+ 2 - 2
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamingSessionServiceImpl.java

@@ -29,12 +29,12 @@ public class ExamingSessionServiceImpl implements ExamingSessionService {
     @Override
     public ExamingSession getExamingSession(Long studentId) {
         String key = RedisKeyHelper.getBuilder().examingSessionKey(studentId);
-        return redisClient.get(key+studentId,ExamingSession.class);
+        return redisClient.get(key,ExamingSession.class);
     }
 
     @Override
     public void deleteExamingSession(Long studentId) {
         String key = RedisKeyHelper.getBuilder().examingSessionKey(studentId);
-        redisClient.delete(key+studentId);
+        redisClient.delete(key);
     }
 }