wangliang 4 vuotta sitten
vanhempi
commit
e835913652

+ 7 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/ExamRecordCacheUtil.java

@@ -369,4 +369,11 @@ public class ExamRecordCacheUtil {
             examRecordService.dataUpdateMq(recordId, ExamRecordFieldEnum.force_finish.name(), forceFinish);
         }
     }
+
+    public static void setLastPrepareTime(Long recordId, Date date, boolean update) {
+        redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), ExamRecordFieldEnum.last_prepare_time.getCode(), date);
+        if (update) {
+            examRecordService.dataUpdateMq(recordId, ExamRecordFieldEnum.last_prepare_time.name(), date, 1);
+        }
+    }
 }

+ 2 - 2
themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java

@@ -124,7 +124,7 @@ public class SystemConstant {
      */
     public static final int CONSUME_MESSAGE_BATCH_MAX_SIZE = 10;
     public static final int MAXRECONSUMETIMES = 3;
-//    public static final String MQDTO_OBJ = "mqDtoObj";
+    //    public static final String MQDTO_OBJ = "mqDtoObj";
     public static final long MESSAGE_TIMEOUT = 3000L;
     public static List<String> mqDelayLevelList = null;
     public static final String delayLevel = "1s,5s,10s,30s,1m,2m,3m,4m,5m,6m,7m,8m,9m,10m,20m,30m,1h,2h";
@@ -151,7 +151,7 @@ public class SystemConstant {
     public static final String WEBSOCKET_OE_ONLINE_COUNT = "websocket:oe:online:count";
     public static final String WEBSOCKET_ADMIN_ONLINE_COUNT = "websocket:admin:online:count";
     public static final String GET = "get";
-        public static final long WEBSOCKET_MAX_TIME_OUT = 1 * 60 * 1000;
+    public static final long WEBSOCKET_MAX_TIME_OUT = 1 * 60 * 1000;
     public static final String ACK_MESSAGE = "ackMessage";
     /**
      * 缓存配置

+ 76 - 11
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java

@@ -89,10 +89,13 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
 
     @Resource
     private TBTaskHistoryService tbTaskHistoryService;
-    
+
     @Resource
     TEExamPaperService examPaperService;
 
+    @Resource
+    TEExamService teExamService;
+
     /**
      * 查询考试批次
      *
@@ -188,7 +191,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
             throw new BusinessException("考生Id和当前登录用户不一致");
         }
         ExamCacheBean examCache = getExamCacheBean(es.getExamId());
-        Long unFinishedRecordId=ExamingDataCacheUtil.getUnFinishedRecordId(studentId);
+        Long unFinishedRecordId = ExamingDataCacheUtil.getUnFinishedRecordId(studentId);
         if (unFinishedRecordId != null) {
             Long recordId = unFinishedRecordId;
             if (examStudentId.equals(ExamRecordCacheUtil.getExamStudentId(recordId))) {//当前考生已存在候考的考试记录
@@ -293,7 +296,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         redisUtil.set(RedisKeyHelper.examStudentCacheKey(examStudentId), es);
         //更新场次-考试记录缓存
         ExamActivityRecordCacheUtil.setExamRecordStatus(activityId, recordId, ExamRecordCacheUtil.getStatus(recordId));
-        ExamingDataCacheUtil.setUnFinishedRecordId(studentId,recordId);
+        ExamingDataCacheUtil.setUnFinishedRecordId(studentId, recordId);
         //mq发送消息start
         TEStudentCacheDto teStudentCacheDto = (TEStudentCacheDto) redisUtil.getStudent(studentId);
         MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.STUDENT.name(), SystemOperationEnum.FIRST_PREPARE, MqTagEnum.STUDENT, String.valueOf(teStudentCacheDto.getId()), teStudentCacheDto.getIdentity());
@@ -384,6 +387,46 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
             throw new BusinessException("未找到试卷");
         }
 
+        WebsocketStatusEnum websocketStatusEnum = ExamRecordCacheUtil.getClientWebsocketStatus(recordId);
+        if (Objects.isNull(websocketStatusEnum) || Objects.equals(WebsocketStatusEnum.OFF_LINE, websocketStatusEnum)) {
+            throw new BusinessException("客户端网络离线");
+        }
+        MonitorStatusSourceEnum clientCameraStatus = ExamRecordCacheUtil.getMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_CAMERA.name());
+        if (Objects.nonNull(clientCameraStatus) || Objects.equals(MonitorStatusSourceEnum.STOP, clientCameraStatus)) {
+            throw new BusinessException("客户端摄像头离线");
+        }
+        MonitorStatusSourceEnum clientScreenStatus = ExamRecordCacheUtil.getMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_SCREEN.name());
+        if (Objects.nonNull(clientScreenStatus) || Objects.equals(MonitorStatusSourceEnum.STOP, clientScreenStatus)) {
+            throw new BusinessException("客户端屏幕离线");
+        }
+        MonitorStatusSourceEnum mobileFirstStatus = ExamRecordCacheUtil.getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_FIRST.name());
+        if (Objects.nonNull(mobileFirstStatus) || Objects.equals(MonitorStatusSourceEnum.STOP, mobileFirstStatus)) {
+            throw new BusinessException("移动端第一机位离线");
+        }
+        MonitorStatusSourceEnum mobileSecondStatus = ExamRecordCacheUtil.getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_SECOND.name());
+        if (Objects.nonNull(mobileSecondStatus) || Objects.equals(MonitorStatusSourceEnum.STOP, mobileSecondStatus)) {
+            throw new BusinessException("移动端第二机位离线");
+        }
+
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
+        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+        Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
+        //获取断点时间
+        Date lastBreakTime = ExamRecordCacheUtil.getLastBreakTime(recordId);
+        //获取剩余断点次数
+        Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ? 0 : ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+        Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
+        Integer breakExpireSeconds = Objects.isNull(ec.getBreakExpireSeconds()) ? 0 : ec.getBreakExpireSeconds();
+        //如果断点时间大于整体断点时间,则强制交卷
+        if (Objects.nonNull(lastBreakTime) && (System.currentTimeMillis() - lastBreakTime.getTime()) / 1000 > breakExpireSeconds) {
+            teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+        } else {
+            //如果断点次数小于0,也强制交卷
+            if (leftBreakResumeCount < 0) {
+                teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+            }
+        }
         ret = new ExamStartBean();
         ret.setPaperDecryptSecret(ep.getDecryptSecret());
         ret.setPaperDecryptVector(ep.getDecryptVector());
@@ -392,8 +435,10 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         Date firstStartTime = new Date();
         ExamRecordCacheUtil.setFirstStartTime(recordId, firstStartTime, false);
         ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.ANSWERING, false);
-        String[] columns = new String[]{ExamRecordFieldEnum.first_start_time.name(), ExamRecordFieldEnum.status.name()};
-        Object[] values = new Object[]{firstStartTime, ExamRecordStatusEnum.ANSWERING};
+        Date lastStartTime = new Date();
+        ExamRecordCacheUtil.setLastStartTime(recordId, lastStartTime, false);
+        String[] columns = new String[]{ExamRecordFieldEnum.first_start_time.name(), ExamRecordFieldEnum.status.name(), ExamRecordFieldEnum.last_start_time.name()};
+        Object[] values = new Object[]{firstStartTime, ExamRecordStatusEnum.ANSWERING, lastStartTime};
         toeExamRecordService.dataUpdatesMq(recordId, columns, values);
         //更新场次-考试记录缓存
         ExamActivityRecordCacheUtil.setExamRecordStatus(activityId, recordId, ExamRecordCacheUtil.getStatus(recordId));
@@ -468,8 +513,8 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
             answerCache.setAnswer(answer);
             answerCache.setVersion(version);
             answerCache.setDurationSeconds(durationSeconds);
-        	answerCache.setObjective(checkIsObjective(ExamRecordCacheUtil.getPaperId(recordId), mainNumber, subNumber, subIndex));
-            	
+            answerCache.setObjective(checkIsObjective(ExamRecordCacheUtil.getPaperId(recordId), mainNumber, subNumber, subIndex));
+
         } else {
             if (version.longValue() > answerCache.getVersion().longValue()) {
                 answerCache.setAnswer(answer);
@@ -483,18 +528,18 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         redisUtil.set(RedisKeyHelper.examAnswerKey(recordId),
                 RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex), answerCache);
         // 如果是客观题,重置考试记录客观题得分
-        if(answerCache.getObjective()) {
-        	ExamRecordCacheUtil.setObjectiveScore(recordId, null, false);
+        if (answerCache.getObjective()) {
+            ExamRecordCacheUtil.setObjectiveScore(recordId, null, false);
             // 发消息计算客观分
             calculateObjectiveScore(recordId, mainNumber, subNumber, subIndex);
         }
-        
+
         AnswerSubmitBean ret = new AnswerSubmitBean();
         ret.setVersion(version);
 
         return ret;
     }
-    
+
     private boolean checkIsObjective(Long paperId, Integer mainNumber, Integer subNumber, Integer subIndex) {
         Map<String, ObjectiveAnswerCacheBean> map = examPaperService.getObjectiveAnswerCacheBean(paperId);
         if (map == null || map.size() == 0) {
@@ -634,6 +679,25 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         if (ExamRecordStatusEnum.FINISHED.equals(sta) || ExamRecordStatusEnum.PERSISTED.equals(sta)) {
             throw new BusinessException("该考试已结束");
         }
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
+        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+        Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
+        //获取断点时间
+        Date lastBreakTime = ExamRecordCacheUtil.getLastBreakTime(recordId);
+        //获取剩余断点次数
+        Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ? 0 : ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+        Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
+        Integer breakExpireSeconds = Objects.isNull(ec.getBreakExpireSeconds()) ? 0 : ec.getBreakExpireSeconds();
+        //如果断点时间大于整体断点时间,则强制交卷
+        if (Objects.nonNull(lastBreakTime) && (System.currentTimeMillis() - lastBreakTime.getTime()) / 1000 > breakExpireSeconds) {
+            teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+        } else {
+            //如果断点次数小于0,也强制交卷
+            if (leftBreakResumeCount < 0) {
+                teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+            }
+        }
 
         ExamResumeBean ret = new ExamResumeBean();
         ret.setDurationSeconds(ExamRecordCacheUtil.getDurationSeconds(recordId));
@@ -660,6 +724,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         if (audioLeftPlayCounts != null && audioLeftPlayCounts.size() > 0) {
             ret.setAudioLeftPlayCount(audioLeftPlayCounts);
         }
+        ExamRecordCacheUtil.setLastPrepareTime(recordId, new Date(), true);
         return ret;
     }
 

+ 1 - 0
themis-exam/src/main/java/com/qmth/themis/exam/websocket/WebSocketOeServer.java

@@ -182,6 +182,7 @@ public class WebSocketOeServer implements Concurrently {
                 mqDtoService.assembleSendAsyncDelayMsg(mqDto);
                 //发送延时mq消息end
             } else {
+                log.info("正常退出");
                 Long ecExamId = ExamRecordCacheUtil.getExamId(recordId);
                 Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
                 TEExamStudentService teExamStudentService = SpringContextHolder.getBean(TEExamStudentService.class);