Jelajahi Sumber

断点逻辑修改,加入examStatus锁,加入全局异常错误信息表

wangliang 4 tahun lalu
induk
melakukan
574f3482ae

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

@@ -125,6 +125,8 @@ public class SystemConstant {
 
     public static final int MAX_EXPORT_SIZE = 500;
 
+    public static final int MAX_EXAM_STATUS_COUNT = 20;
+
     public static final String EXCEL_PREFIX = ".xlsx";
 
     public static final String TXT_PREFIX = ".txt";

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java

@@ -409,7 +409,7 @@ public interface TOeExamRecordService extends IService<TOeExamRecord> {
      *
      * @param recordId
      */
-    Boolean setExamBreak(Long recordId, Integer count);
+    Boolean setExamBreak(Long recordId);
 
     /**
      * 发送断点信息

+ 135 - 84
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java

@@ -20,10 +20,7 @@ import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
 import com.qmth.themis.business.dto.response.TEExamActivityDto;
 import com.qmth.themis.business.dto.response.TEExamDto;
 import com.qmth.themis.business.dto.response.TEExamQueryDto;
-import com.qmth.themis.business.entity.TBSession;
-import com.qmth.themis.business.entity.TBTaskHistory;
-import com.qmth.themis.business.entity.TEExam;
-import com.qmth.themis.business.entity.TOeExamRecord;
+import com.qmth.themis.business.entity.*;
 import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.*;
 import com.qmth.themis.business.util.*;
@@ -100,6 +97,9 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
     @Resource
     TEExamPaperService examPaperService;
 
+    @Resource
+    TGErrorService tgErrorService;
+
     /**
      * 查询考试批次
      *
@@ -116,7 +116,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      */
     @Override
     public IPage<TEExamQueryDto> examQuery(IPage<Map> iPage, Long userId, Long id, String code, String name,
-            String mode, Integer enable, Long orgId, String type) {
+                                           String mode, Integer enable, Long orgId, String type) {
         return teExamMapper.examQuery(iPage, userId, id, code, name, mode, enable, orgId, type);
     }
 
@@ -280,12 +280,12 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
                 ExamRecordCacheUtil.setMinDurationSeconds(recordId, examCache.getMinDurationSeconds(), false);
                 ExamRecordCacheUtil.setMaxDurationSeconds(recordId, ac.getMaxDurationSeconds(), false);
                 ExamRecordCacheUtil.setForceFinish(recordId, examCache.getForceFinish(), false);
-                String[] columns = new String[] { ExamRecordFieldEnum.start_time.name(),
+                String[] columns = new String[]{ExamRecordFieldEnum.start_time.name(),
                         ExamRecordFieldEnum.end_time.name(), ExamRecordFieldEnum.opening_seconds.name(),
                         ExamRecordFieldEnum.min_duration_seconds.name(),
-                        ExamRecordFieldEnum.max_duration_seconds.name(), ExamRecordFieldEnum.force_finish.name() };
-                Object[] values = new Object[] { ac.getStartTime(), ac.getFinishTime(), ac.getOpeningSeconds(),
-                        examCache.getMinDurationSeconds(), ac.getMaxDurationSeconds(), examCache.getForceFinish() };
+                        ExamRecordFieldEnum.max_duration_seconds.name(), ExamRecordFieldEnum.force_finish.name()};
+                Object[] values = new Object[]{ac.getStartTime(), ac.getFinishTime(), ac.getOpeningSeconds(),
+                        examCache.getMinDurationSeconds(), ac.getMaxDurationSeconds(), examCache.getForceFinish()};
                 toeExamRecordService.dataUpdatesMq(recordId, columns, values);
                 return prepare;
             }
@@ -492,14 +492,14 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
                 if (Objects.isNull(clientCameraStatus) || Objects
                         .equals(MonitorStatusSourceEnum.STOP, clientCameraStatus)) {
                     switch (strs[i]) {
-                    case "CLIENT_CAMERA":
-                        throw new BusinessException(ExceptionResultEnum.CLIENT_CAMERA_OFFLINE);
-                    case "CLIENT_SCREEN":
-                        throw new BusinessException(ExceptionResultEnum.CLIENT_SCREEN_OFFLINE);
-                    case "MOBILE_FIRST":
-                        throw new BusinessException(ExceptionResultEnum.MOBILE_FIRST_OFFLINE);
-                    default:
-                        throw new BusinessException(ExceptionResultEnum.MOBILE_SECOND_OFFLINE);
+                        case "CLIENT_CAMERA":
+                            throw new BusinessException(ExceptionResultEnum.CLIENT_CAMERA_OFFLINE);
+                        case "CLIENT_SCREEN":
+                            throw new BusinessException(ExceptionResultEnum.CLIENT_SCREEN_OFFLINE);
+                        case "MOBILE_FIRST":
+                            throw new BusinessException(ExceptionResultEnum.MOBILE_FIRST_OFFLINE);
+                        default:
+                            throw new BusinessException(ExceptionResultEnum.MOBILE_SECOND_OFFLINE);
                     }
                 }
             }
@@ -508,30 +508,47 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         ret.setPaperDecryptSecret(ep.getDecryptSecret());
         ret.setPaperDecryptVector(ep.getDecryptVector());
 
-        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
-                SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
-        if (lock) {
-            try {
-                // 更新考试记录缓存
-                Long firstStartTime = System.currentTimeMillis();
-                ExamRecordCacheUtil.setFirstStartTime(recordId, firstStartTime, false);
-                ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.ANSWERING, false);
-                Long lastStartTime = System.currentTimeMillis();
-                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,
-                        new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
-                                ExamRecordCacheUtil.getStatus(recordId)));
-            } finally {
-                if (Objects.nonNull(recordId)) {
-                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+        boolean lock = false;
+        for (int i = 0; i < SystemConstant.MAX_EXAM_STATUS_COUNT; i++) {
+            lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
+                    SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
+            if (lock) {
+                try {
+                    // 更新考试记录缓存
+                    Long firstStartTime = System.currentTimeMillis();
+                    ExamRecordCacheUtil.setFirstStartTime(recordId, firstStartTime, false);
+                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.ANSWERING, false);
+                    Long lastStartTime = System.currentTimeMillis();
+                    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,
+                            new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
+                                    ExamRecordCacheUtil.getStatus(recordId)));
+                    break;
+                } finally {
+                    if (Objects.nonNull(recordId)) {
+                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+                    }
+                }
+            } else {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
                 }
             }
-        } else {
+        }
+        if (!lock) {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("recordId", recordId);
+            jsonObject.put("method", "start");
+            jsonObject.put("examStatus", ExamRecordCacheUtil.getStatus(recordId));
+            TGError tgError = new TGError(jsonObject.toJSONString(), System.currentTimeMillis());
+            tgErrorService.save(tgError);
             throw new BusinessException(ExceptionResultEnum.EXAM_STATUS_UPDATE_ERROR);
         }
 
@@ -614,7 +631,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      */
     @Override
     public AnswerSubmitBean answerSubmit(Long studentId, Long recordId, Integer mainNumber, Integer subNumber,
-            Integer subIndex, String answer, Long version, Integer durationSeconds) {
+                                         Integer subIndex, String answer, Long version, Integer durationSeconds) {
 
         // 校验当前登录用户和参数一致性
         if (ExamRecordCacheUtil.getId(recordId) == null) {
@@ -710,7 +727,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      */
     @Override
     public AudioLeftPlayCountSubmitBean audioLeftPlayCountSubmit(Long studentId, Long recordId, String key,
-            Integer count) {
+                                                                 Integer count) {
 
         // 校验当前登录用户和参数一致性
         if (ExamRecordCacheUtil.getId(recordId) == null) {
@@ -856,27 +873,44 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
             ret.setAudioLeftPlayCount(audioLeftPlayCounts);
         }
 
-        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
-                SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
-        if (lock) {
-            try {
-                Long lastPrepareTime = System.currentTimeMillis();
-                ExamRecordCacheUtil.setLastPrepareTime(recordId, lastPrepareTime, false);
-                ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.RESUME_PREPARE, false);
-                String[] columns = new String[]{ExamRecordFieldEnum.last_prepare_time.name(),
-                        ExamRecordFieldEnum.status.name()};
-                Object[] values = new Object[]{lastPrepareTime, ExamRecordStatusEnum.RESUME_PREPARE};
-                toeExamRecordService.dataUpdatesMq(recordId, columns, values);
-                //更新场次-考试记录缓存
-                ExamActivityRecordCacheUtil.setExamRecordStatus(es.getExamActivityId(), recordId,
-                        new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
-                                ExamRecordCacheUtil.getStatus(recordId)));
-            } finally {
-                if (Objects.nonNull(recordId)) {
-                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+        boolean lock = false;
+        for (int i = 0; i < SystemConstant.MAX_EXAM_STATUS_COUNT; i++) {
+            lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
+                    SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
+            if (lock) {
+                try {
+                    Long lastPrepareTime = System.currentTimeMillis();
+                    ExamRecordCacheUtil.setLastPrepareTime(recordId, lastPrepareTime, false);
+                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.RESUME_PREPARE, false);
+                    String[] columns = new String[]{ExamRecordFieldEnum.last_prepare_time.name(),
+                            ExamRecordFieldEnum.status.name()};
+                    Object[] values = new Object[]{lastPrepareTime, ExamRecordStatusEnum.RESUME_PREPARE};
+                    toeExamRecordService.dataUpdatesMq(recordId, columns, values);
+                    //更新场次-考试记录缓存
+                    ExamActivityRecordCacheUtil.setExamRecordStatus(es.getExamActivityId(), recordId,
+                            new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
+                                    ExamRecordCacheUtil.getStatus(recordId)));
+                    break;
+                } finally {
+                    if (Objects.nonNull(recordId)) {
+                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+                    }
+                }
+            } else {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
                 }
             }
-        } else {
+        }
+        if (!lock) {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("recordId", recordId);
+            jsonObject.put("method", "resume");
+            jsonObject.put("examStatus", ExamRecordCacheUtil.getStatus(recordId));
+            TGError tgError = new TGError(jsonObject.toJSONString(), System.currentTimeMillis());
+            tgErrorService.save(tgError);
             throw new BusinessException(ExceptionResultEnum.EXAM_STATUS_UPDATE_ERROR);
         }
         return ret;
@@ -1018,31 +1052,48 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         } else {
             ret.setStatus(FinishExamResultEnum.NORMAL);
         }
-        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
-                SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
-        if (lock) {
-            try {
-                Long finishTime = System.currentTimeMillis();
-                ExamRecordCacheUtil.setFinishTime(recordId, finishTime, false);
-                ExamRecordCacheUtil.setDurationSeconds(recordId, durationSeconds, false);
-                ExamRecordCacheUtil.setFinishType(recordId, FinishTypeEnum.valueOf(type), false);
-                ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.FINISHED, false);
-                String[] columns = new String[]{ExamRecordFieldEnum.finish_time.name(),
-                        ExamRecordFieldEnum.duration_seconds.name(), ExamRecordFieldEnum.finish_type.name(),
-                        ExamRecordFieldEnum.status.name()};
-                Object[] values = new Object[]{finishTime, durationSeconds, FinishTypeEnum.valueOf(type),
-                        ExamRecordStatusEnum.FINISHED};
-                toeExamRecordService.dataUpdatesMq(recordId, columns, values);
-                //更新场次-考试记录缓存
-                ExamActivityRecordCacheUtil.setExamRecordStatus(es.getExamActivityId(), recordId,
-                        new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
-                                ExamRecordCacheUtil.getStatus(recordId)));
-            } finally {
-                if (Objects.nonNull(recordId)) {
-                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+        boolean lock = false;
+        for (int i = 0; i < SystemConstant.MAX_EXAM_STATUS_COUNT; i++) {
+            lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
+                    SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
+            if (lock) {
+                try {
+                    Long finishTime = System.currentTimeMillis();
+                    ExamRecordCacheUtil.setFinishTime(recordId, finishTime, false);
+                    ExamRecordCacheUtil.setDurationSeconds(recordId, durationSeconds, false);
+                    ExamRecordCacheUtil.setFinishType(recordId, FinishTypeEnum.valueOf(type), false);
+                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.FINISHED, false);
+                    String[] columns = new String[]{ExamRecordFieldEnum.finish_time.name(),
+                            ExamRecordFieldEnum.duration_seconds.name(), ExamRecordFieldEnum.finish_type.name(),
+                            ExamRecordFieldEnum.status.name()};
+                    Object[] values = new Object[]{finishTime, durationSeconds, FinishTypeEnum.valueOf(type),
+                            ExamRecordStatusEnum.FINISHED};
+                    toeExamRecordService.dataUpdatesMq(recordId, columns, values);
+                    //更新场次-考试记录缓存
+                    ExamActivityRecordCacheUtil.setExamRecordStatus(es.getExamActivityId(), recordId,
+                            new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
+                                    ExamRecordCacheUtil.getStatus(recordId)));
+                    break;
+                } finally {
+                    if (Objects.nonNull(recordId)) {
+                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
+                    }
+                }
+            } else {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
                 }
             }
-        } else {
+        }
+        if (!lock) {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("recordId", recordId);
+            jsonObject.put("method", "diposeFinish");
+            jsonObject.put("examStatus", ExamRecordCacheUtil.getStatus(recordId));
+            TGError tgError = new TGError(jsonObject.toJSONString(), System.currentTimeMillis());
+            tgErrorService.save(tgError);
             throw new BusinessException(ExceptionResultEnum.EXAM_STATUS_UPDATE_ERROR);
         }
         //更新未完成考试记录id
@@ -1212,7 +1263,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      */
     @Override
     public void sendOeLogMessage(SystemOperationEnum systemOperationEnum, Long examStudentId, Long recordId,
-            MqDto mqDto) {
+                                 MqDto mqDto) {
         //mq发送消息start
         Map<String, Object> properties = new HashMap<>();
         properties.put("remark", systemOperationEnum.getCode());

+ 133 - 134
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java

@@ -143,7 +143,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
     @Transactional
     @Override
     public Long saveByPrepare(Long examId, Long examActivityId, Long examStudentId, Long paperId,
-            Integer serialNumber) {
+                              Integer serialNumber) {
         ExamActivityCacheBean ac = examActivityService.getExamActivityCacheBean(examActivityId);
         ExamCacheBean exam = examService.getExamCacheBean(examId);
         TOeExamRecord er = new TOeExamRecord();
@@ -443,13 +443,13 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
 
     @Override
     public void saveFaceVerify(ExamTypeEnum type, Long recordId, Long entryAuthenticationId,
-            VerifyExceptionEnum entryAuthenticationResult) {
+                               VerifyExceptionEnum entryAuthenticationResult) {
         if (ExamTypeEnum.FIRST_START.equals(type)) {
             ExamRecordCacheUtil.setEntryAuthenticationId(recordId, entryAuthenticationId, false);
             ExamRecordCacheUtil.setEntryAuthenticationResult(recordId, entryAuthenticationResult, false);
-            String[] columns = new String[] { ExamRecordFieldEnum.entry_authentication_id.name(),
-                    ExamRecordFieldEnum.entry_authentication_result.name() };
-            Object[] values = new Object[] { entryAuthenticationId, entryAuthenticationResult };
+            String[] columns = new String[]{ExamRecordFieldEnum.entry_authentication_id.name(),
+                    ExamRecordFieldEnum.entry_authentication_result.name()};
+            Object[] values = new Object[]{entryAuthenticationId, entryAuthenticationResult};
             this.dataUpdatesMq(recordId, columns, values);
         } else if (ExamTypeEnum.IN_PROCESS.equals(type)) {
 
@@ -458,13 +458,13 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
 
     @Override
     public void saveLivenessVerify(LivenessTypeEnum type, Long recordId, Long entryAuthenticationId,
-            VerifyExceptionEnum entryAuthenticationResult) {
+                                   VerifyExceptionEnum entryAuthenticationResult) {
         if (LivenessTypeEnum.FIRST_START.equals(type)) {
             ExamRecordCacheUtil.setEntryAuthenticationId(recordId, entryAuthenticationId, false);
             ExamRecordCacheUtil.setEntryAuthenticationResult(recordId, entryAuthenticationResult, false);
-            String[] columns = new String[] { ExamRecordFieldEnum.entry_authentication_id.name(),
-                    ExamRecordFieldEnum.entry_authentication_result.name() };
-            Object[] values = new Object[] { entryAuthenticationId, entryAuthenticationResult };
+            String[] columns = new String[]{ExamRecordFieldEnum.entry_authentication_id.name(),
+                    ExamRecordFieldEnum.entry_authentication_result.name()};
+            Object[] values = new Object[]{entryAuthenticationId, entryAuthenticationResult};
             this.dataUpdatesMq(recordId, columns, values);
         } else if (LivenessTypeEnum.IN_PROCESS.equals(type)) {
             Integer count = ExamRecordCacheUtil.getInProcessLivenessVerifyCount(recordId);
@@ -571,9 +571,9 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListBean> invigilatePageList(IPage<Map> iPage, Long examId, Long examActivityId,
-            String roomCode, Integer paperDownload, String status, String name, String identity,
-            Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, String monitorStatusSource,
-            Long userId) {
+                                                        String roomCode, Integer paperDownload, String status, String name, String identity,
+                                                        Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, String monitorStatusSource,
+                                                        Long userId) {
         return tOeExamRecordMapper
                 .invigilatePageList(iPage, examId, examActivityId, roomCode, paperDownload, status, name, identity,
                         minWarningCount, maxWarningCount, clientWebsocketStatus, monitorStatusSource, userId);
@@ -599,8 +599,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListVideoBean> invigilatePageListVideo(IPage<Map> iPage, Long examId, Long examActivityId,
-            String roomCode, Integer paperDownload, String status, String name, String identity,
-            Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, Long userId, Long orgId) {
+                                                                  String roomCode, Integer paperDownload, String status, String name, String identity,
+                                                                  Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePageListVideo(iPage, examId, examActivityId, roomCode, paperDownload, status, name, identity,
                         minWarningCount, maxWarningCount, clientWebsocketStatus, userId, orgId);
@@ -617,7 +617,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public List<InvigilateListVideoBean> invigilatePageListVideoRandom(Long examId, Long userId, Integer randomNum,
-            Long orgId) {
+                                                                       Long orgId) {
         return tOeExamRecordMapper.invigilatePageListVideoRandom(examId, userId, randomNum, orgId);
     }
 
@@ -644,9 +644,9 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListPatrolBean> invigilatePagePatrolList(IPage<Map> iPage, Long examId, Long examActivityId,
-            String roomCode, String status, String name, String identity, Integer minMultipleFaceCount,
-            Integer maxMultipleFaceCount, Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount,
-            Integer maxWarningCount, String clientWebsocketStatus, Long userId, Long orgId) {
+                                                                    String roomCode, String status, String name, String identity, Integer minMultipleFaceCount,
+                                                                    Integer maxMultipleFaceCount, Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount,
+                                                                    Integer maxWarningCount, String clientWebsocketStatus, Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePagePatrolList(iPage, examId, examActivityId, roomCode, status, name, identity,
                         minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount, maxExceptionCount,
@@ -675,9 +675,9 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListWarningBean> invigilatePageWarningList(IPage<Map> iPage, Long examId,
-            Long examActivityId, String roomCode, Integer approveStatus, String name, String identity,
-            Integer minMultipleFaceCount, Integer maxMultipleFaceCount, Integer minExceptionCount,
-            Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount, Long userId, Long orgId) {
+                                                                      Long examActivityId, String roomCode, Integer approveStatus, String name, String identity,
+                                                                      Integer minMultipleFaceCount, Integer maxMultipleFaceCount, Integer minExceptionCount,
+                                                                      Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount, Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePageWarningList(iPage, examId, examActivityId, roomCode, approveStatus, name, identity,
                         minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount, maxExceptionCount,
@@ -705,9 +705,9 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public Integer approveStatusListUpdate(Long examId, Long examActivityId, String roomCode, Integer approveStatus,
-            String name, String identity, Integer minMultipleFaceCount, Integer maxMultipleFaceCount,
-            Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount,
-            Long userId, Long orgId) {
+                                           String name, String identity, Integer minMultipleFaceCount, Integer maxMultipleFaceCount,
+                                           Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount,
+                                           Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .approveStatusListUpdate(examId, examActivityId, roomCode, approveStatus, name, identity,
                         minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount, maxExceptionCount,
@@ -729,8 +729,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListProgressBean> invigilatePageProgressList(IPage<Map> iPage, Long examId,
-            Long examActivityId, String roomCode, String courseCode, String name, String identity, Long userId,
-            Long orgId) {
+                                                                        Long examActivityId, String roomCode, String courseCode, String name, String identity, Long userId,
+                                                                        Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePageProgressList(iPage, examId, examActivityId, roomCode, courseCode, name, identity, userId,
                         orgId);
@@ -751,7 +751,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public List<InvigilateListProgressExcelBean> invigilatePageProgressListExport(Long examId, Long examActivityId,
-            String roomCode, String courseCode, String name, String identity, Long userId, Long orgId) {
+                                                                                  String roomCode, String courseCode, String name, String identity, Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePageProgressListExport(examId, examActivityId, roomCode, courseCode, name, identity, userId,
                         orgId);
@@ -782,10 +782,10 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public IPage<InvigilateListHistoryBean> invigilatePageListHistory(IPage<Map> iPage, Long examId,
-            Long examActivityId, String roomCode, String courseCode, String status, Integer breachStatus,
-            String finishType, String name, String identity, Integer minMultipleFaceCount, Integer maxMultipleFaceCount,
-            Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount,
-            Long userId, Long orgId) {
+                                                                      Long examActivityId, String roomCode, String courseCode, String status, Integer breachStatus,
+                                                                      String finishType, String name, String identity, Integer minMultipleFaceCount, Integer maxMultipleFaceCount,
+                                                                      Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount, Integer maxWarningCount,
+                                                                      Long userId, Long orgId) {
         return tOeExamRecordMapper
                 .invigilatePageListHistory(iPage, examId, examActivityId, roomCode, courseCode, status, breachStatus,
                         finishType, name, identity, minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount,
@@ -947,7 +947,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      */
     @Override
     public TOeExamRecord findMarkResult(MarkResultDto markResultDto,
-            RecordSelectStrategyEnum recordSelectStrategyEnum) {
+                                        RecordSelectStrategyEnum recordSelectStrategyEnum) {
         TOeExamRecord tOeExamRecord = null;
         Double sumScore = null;
         //客观分最高
@@ -1012,121 +1012,120 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * 设置断点信息
      *
      * @param recordId
-     * @param count
      */
     @Override
     @Transactional
-    public Boolean setExamBreak(Long recordId, Integer count) {
-        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
-                SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
-        if (lock) {
-            try {
-                Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ?
-                        0 :
-                        ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
-                alreadyBreakCount++;
-                Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
-                Long breakId = uidUtil.getId();
-                ExamRecordCacheUtil.setLastBreakId(recordId, breakId, false);
-                ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF, false);
-                Long lastBreakTimeNow = System.currentTimeMillis();
-                ExamRecordCacheUtil.setLastBreakTime(recordId, lastBreakTimeNow, false);
-                ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount, false);
-                Long lastStartTime = System.currentTimeMillis();
-                ExamRecordCacheUtil.setLastStartTime(recordId, lastStartTime, false);
-                String[] columns = new String[]{ExamRecordFieldEnum.last_break_id.name(),
-                        ExamRecordFieldEnum.status.name(), ExamRecordFieldEnum.last_break_time.name(),
-                        ExamRecordFieldEnum.already_break_count.name(), ExamRecordFieldEnum.last_start_time.name()};
-                Object[] values = new Object[]{breakId, ExamRecordStatusEnum.BREAK_OFF, lastBreakTimeNow,
-                        alreadyBreakCount, lastStartTime};
-                TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
-                tOeExamRecordService.dataUpdatesMq(recordId, columns, values);
-                //考试断点异常原因 发送mq start
-                MqDto mqDtoBreak = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_BREAK.name(),
-                        ExceptionEnum.NET_TIME_OUT, MqTagEnum.EXAM_BREAK, String.valueOf(recordId),
-                        String.valueOf(recordId));
-                MqDtoService mqDtoService = SpringContextHolder.getBean(MqDtoService.class);
-                mqDtoService.assembleSendOneWayMsg(mqDtoBreak);
-                //考试断点异常原因 发送mq end
-
-                Long examId = ExamRecordCacheUtil.getExamId(recordId);
-                ExamCacheBean ec = examService.getExamCacheBean(examId);//考试缓存
-                Integer breakExpireSeconds = Objects.isNull(ec.getBreakExpireSeconds()) ?
-                        0 :
-                        ec.getBreakExpireSeconds();
-                if (breakExpireSeconds.intValue() > 0) {
-                    List<String> list = SystemConstant.mqDelayLevelList.subList(5, 15);
-                    String level = null;
-                    if (breakExpireSeconds.intValue() <= 60) {
-                        level = "1m";
-                    } else {
-                        Integer time = breakExpireSeconds.intValue() / 60;
-                        if (time.intValue() >= 30) {
-                            level = "30m";
+    public Boolean setExamBreak(Long recordId) {
+        boolean lock = false;
+        for (int i = 0; i < SystemConstant.MAX_EXAM_STATUS_COUNT; i++) {
+            lock = redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId,
+                    SystemConstant.REDIS_LOCK_EXAM_STATUS_TIME_OUT);
+            if (lock) {
+                try {
+                    Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ?
+                            0 :
+                            ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+                    alreadyBreakCount++;
+                    Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
+                    Long breakId = uidUtil.getId();
+                    ExamRecordCacheUtil.setLastBreakId(recordId, breakId, false);
+                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF, false);
+                    Long lastBreakTimeNow = System.currentTimeMillis();
+                    ExamRecordCacheUtil.setLastBreakTime(recordId, lastBreakTimeNow, false);
+                    ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount, false);
+                    Long lastStartTime = System.currentTimeMillis();
+                    ExamRecordCacheUtil.setLastStartTime(recordId, lastStartTime, false);
+                    String[] columns = new String[]{ExamRecordFieldEnum.last_break_id.name(),
+                            ExamRecordFieldEnum.status.name(), ExamRecordFieldEnum.last_break_time.name(),
+                            ExamRecordFieldEnum.already_break_count.name(), ExamRecordFieldEnum.last_start_time.name()};
+                    Object[] values = new Object[]{breakId, ExamRecordStatusEnum.BREAK_OFF, lastBreakTimeNow,
+                            alreadyBreakCount, lastStartTime};
+                    TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
+                    tOeExamRecordService.dataUpdatesMq(recordId, columns, values);
+                    //考试断点异常原因 发送mq start
+                    MqDto mqDtoBreak = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_BREAK.name(),
+                            ExceptionEnum.NET_TIME_OUT, MqTagEnum.EXAM_BREAK, String.valueOf(recordId),
+                            String.valueOf(recordId));
+                    MqDtoService mqDtoService = SpringContextHolder.getBean(MqDtoService.class);
+                    mqDtoService.assembleSendOneWayMsg(mqDtoBreak);
+                    //考试断点异常原因 发送mq end
+
+                    Long examId = ExamRecordCacheUtil.getExamId(recordId);
+                    ExamCacheBean ec = examService.getExamCacheBean(examId);//考试缓存
+                    Integer breakExpireSeconds = Objects.isNull(ec.getBreakExpireSeconds()) ?
+                            0 :
+                            ec.getBreakExpireSeconds();
+                    if (breakExpireSeconds.intValue() > 0) {
+                        List<String> list = SystemConstant.mqDelayLevelList.subList(5, 15);
+                        String level = null;
+                        if (breakExpireSeconds.intValue() <= 60) {
+                            level = "1m";
                         } else {
-                            for (String s : list) {
-                                Integer value = Integer.parseInt(s.substring(0, s.length() - 1));
-                                if (time.intValue() <= value.intValue()) {
-                                    level = value + "m";
-                                    break;
+                            Integer time = breakExpireSeconds.intValue() / 60;
+                            if (time.intValue() >= 30) {
+                                level = "30m";
+                            } else {
+                                for (String s : list) {
+                                    Integer value = Integer.parseInt(s.substring(0, s.length() - 1));
+                                    if (time.intValue() <= value.intValue()) {
+                                        level = value + "m";
+                                        break;
+                                    }
                                 }
                             }
                         }
+                        Map<String, Object> tranMap = new HashMap<>();
+                        Integer time = SystemConstant.mqDelayLevel.get(level);
+                        LocalDateTime dt = LocalDateTime.now();
+                        if (level.contains("m")) {
+                            dt = dt.plusMinutes(Long.parseLong(level.replace("m", "")));
+                        } else {
+                            dt = dt.plusSeconds(Long.parseLong(level.replace("s", "")));
+                        }
+                        tranMap.put("recordId", recordId);
+                        tranMap.put("timeOut", time);
+                        tranMap.put("mqExecTime", dt.toInstant(ZoneOffset.of("+8")).toEpochMilli());
+                        //考试断点延时消息 发送mq start
+                        MqDto mqDtoBreakDelay = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(),
+                                MqTagEnum.EXAM_BREAK_DELAY.name(), MqTagEnum.EXAM_BREAK_DELAY, MqTagEnum.EXAM_BREAK_DELAY,
+                                String.valueOf(recordId), tranMap, String.valueOf(recordId));
+                        mqDtoService.assembleSendAsyncDelayMsg(mqDtoBreakDelay);
+                        //考试断点延时消息 发送mq end
                     }
-                    Map<String, Object> tranMap = new HashMap<>();
-                    Integer time = SystemConstant.mqDelayLevel.get(level);
-                    LocalDateTime dt = LocalDateTime.now();
-                    if (level.contains("m")) {
-                        dt = dt.plusMinutes(Long.parseLong(level.replace("m", "")));
+                    //更新场次-考试记录缓存
+                    ExamActivityRecordCacheUtil.setExamRecordStatus(examActivityId, recordId,
+                            new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
+                                    ExamRecordCacheUtil.getStatus(recordId)));
+                    break;
+                } catch (Exception e) {
+                    log.error("请求出错", e);
+                    if (e instanceof BusinessException) {
+                        throw new BusinessException(e.getMessage());
                     } else {
-                        dt = dt.plusSeconds(Long.parseLong(level.replace("s", "")));
+                        throw new RuntimeException(e);
+                    }
+                } finally {
+                    if (Objects.nonNull(recordId)) {
+                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
                     }
-                    tranMap.put("recordId", recordId);
-                    tranMap.put("timeOut", time);
-                    tranMap.put("mqExecTime", dt.toInstant(ZoneOffset.of("+8")).toEpochMilli());
-                    //考试断点延时消息 发送mq start
-                    MqDto mqDtoBreakDelay = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(),
-                            MqTagEnum.EXAM_BREAK_DELAY.name(), MqTagEnum.EXAM_BREAK_DELAY, MqTagEnum.EXAM_BREAK_DELAY,
-                            String.valueOf(recordId), tranMap, String.valueOf(recordId));
-                    mqDtoService.assembleSendAsyncDelayMsg(mqDtoBreakDelay);
-                    //考试断点延时消息 发送mq end
-                }
-                //更新场次-考试记录缓存
-                ExamActivityRecordCacheUtil.setExamRecordStatus(examActivityId, recordId,
-                        new ExamActivityRecordCacheBean(ExamRecordCacheUtil.getExamStudentId(recordId),
-                                ExamRecordCacheUtil.getStatus(recordId)));
-            } catch (Exception e) {
-                log.error("请求出错", e);
-                if (e instanceof BusinessException) {
-                    throw new BusinessException(e.getMessage());
-                } else {
-                    throw new RuntimeException(e);
-                }
-            } finally {
-                if (Objects.nonNull(recordId)) {
-                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_STATUS_PREFIX + recordId);
                 }
-            }
-        } else {
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-            if (count >= 20) {
-                JSONObject jsonObject = new JSONObject();
-                jsonObject.put("recordId", recordId);
-                jsonObject.put("method", "setExamBreak");
-                jsonObject.put("examStatus", ExamRecordCacheUtil.getStatus(recordId));
-                TGError tgError = new TGError(jsonObject.toJSONString(), System.currentTimeMillis());
-                tgErrorService.save(tgError);
-                return false;
             } else {
-                count++;
-                setExamBreak(recordId, count);
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
             }
         }
-        return true;
+        if (!lock) {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("recordId", recordId);
+            jsonObject.put("method", "setExamBreak");
+            jsonObject.put("examStatus", ExamRecordCacheUtil.getStatus(recordId));
+            TGError tgError = new TGError(jsonObject.toJSONString(), System.currentTimeMillis());
+            tgErrorService.save(tgError);
+        }
+        return lock;
     }
 
     /**
@@ -1140,7 +1139,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
         Boolean finished = false;
         try {
             if (setBreak) {
-                this.setExamBreak(recordId, 0);
+                this.setExamBreak(recordId);
             }
             Long examId = ExamRecordCacheUtil.getExamId(recordId);
             Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);

+ 1 - 1
themis-exam/src/main/java/com/qmth/themis/exam/api/TEStudentController.java

@@ -260,7 +260,7 @@ public class TEStudentController {
                 if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING)) {
                     //先生成断点,再比较
                     ExamConstant.sendExamStopMsg(recordId, true);
-                    tOeExamRecordService.setExamBreak(recordId, 0);
+                    tOeExamRecordService.setExamBreak(recordId);
                 }
                 Boolean finished = tOeExamRecordService.sendExamBreakMsg(recordId, false);
                 if (finished) {