wangliang 4 ani în urmă
părinte
comite
c11e9740d9

+ 2 - 5
themis-backend/src/main/java/com/qmth/themis/backend/api/TEExamReexamController.java

@@ -91,12 +91,9 @@ public class TEExamReexamController {
         for (String s : recordIdList) {
             //获取考试记录缓存
             Long recordId = Long.parseLong(s);
-            Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-            if (Objects.isNull(objectMap) || objectMap.size() == 0) {
+            examId = ExamRecordCacheUtil.getExamId(recordId);
+            if (Objects.isNull(examId)) {
                 TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(Long.parseLong(s));
-                if (Objects.isNull(tOeExamRecord)) {
-                    throw new BusinessException("考试记录[" + s + "]不存在");
-                }
                 examId = tOeExamRecord.getExamId();
                 examStudentId = tOeExamRecord.getExamStudentId();
                 examActivityId = tOeExamRecord.getExamActivityId();

+ 2 - 3
themis-backend/src/main/java/com/qmth/themis/backend/api/TIeInvigilateController.java

@@ -391,11 +391,10 @@ public class TIeInvigilateController {
             List<TEExamBreachLog> finalTeExamBreachLogList = teExamBreachLogList;
             for (int i = 0; i < recordIdList.size(); i++) {
                 Long l = Long.parseLong(String.valueOf(recordIdList.get(i)));
-                Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(l));
-                Long examId = null;
+                Long examId = ExamRecordCacheUtil.getExamId(l);
                 Long examStudentId = null;
                 Long examActivityId = null;
-                if (Objects.isNull(objectMap) || objectMap.size() == 0) {
+                if (Objects.isNull(examId)) {
                     TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(l);
                     examId = tOeExamRecord.getExamId();
                     examStudentId = tOeExamRecord.getExamStudentId();

+ 12 - 0
themis-business/src/main/java/com/qmth/themis/business/entity/TEExamStudentLog.java

@@ -65,6 +65,10 @@ public class TEExamStudentLog implements Serializable {
     @TableField(value = "obj_id")
     private Long objId;
 
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    private Long updateTime;
+
     public TEExamStudentLog() {
 
     }
@@ -98,6 +102,14 @@ public class TEExamStudentLog implements Serializable {
         this.objId = objId;
     }
 
+    public Long getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Long updateTime) {
+        this.updateTime = updateTime;
+    }
+
     public Long getObjId() {
         return objId;
     }

+ 13 - 1
themis-business/src/main/java/com/qmth/themis/business/entity/TIeInvigilateExceptionInfo.java

@@ -82,12 +82,15 @@ public class TIeInvigilateExceptionInfo implements Serializable {
     @TableField(value = "video_url")
     private String videoUrl;
 
+    @ApiModelProperty(value = "业务id")
+    @TableField(value = "obj_id")
+    private Long objId;
 
     public TIeInvigilateExceptionInfo() {
 
     }
 
-    public TIeInvigilateExceptionInfo(Long examId, Long examActivityId, Long examRecordId, Long examStudentId, String info, ExceptionEnum type,Integer difference) {
+    public TIeInvigilateExceptionInfo(Long examId, Long examActivityId, Long examRecordId, Long examStudentId, String info, ExceptionEnum type,Integer difference,Long objId) {
         this.id = Constants.idGen.next();
         this.examId = examId;
         this.examActivityId = examActivityId;
@@ -96,6 +99,15 @@ public class TIeInvigilateExceptionInfo implements Serializable {
         this.info = info;
         this.type = type;
         this.difference = difference;
+        this.objId = objId;
+    }
+
+    public Long getObjId() {
+        return objId;
+    }
+
+    public void setObjId(Long objId) {
+        this.objId = objId;
     }
 
     public String getPhotoUrl() {

+ 2 - 4
themis-business/src/main/java/com/qmth/themis/business/service/impl/WarningServiceImpl.java

@@ -3,7 +3,6 @@ package com.qmth.themis.business.service.impl;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
-import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.dto.WarningDto;
 import com.qmth.themis.business.entity.TEConfig;
@@ -183,9 +182,8 @@ public class WarningServiceImpl implements WarningService {
      * @param recordId
      */
     public void setWarningCount(Long recordId) {
-        Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-        if (Objects.nonNull(objectMap.get("warningCount"))) {
-            Integer warningCount = ExamRecordCacheUtil.getWarningCount(recordId);
+        Integer warningCount = ExamRecordCacheUtil.getWarningCount(recordId);
+        if (Objects.nonNull(warningCount)) {
             warningCount++;
             ExamRecordCacheUtil.setWarningCount(recordId, warningCount, true);
         } else {

+ 1 - 0
themis-business/src/main/resources/db/init.sql

@@ -1120,6 +1120,7 @@ CREATE TABLE `t_e_exam_student_log` (
   `remark` mediumtext COMMENT '备注',
   `create_time` bigint DEFAULT NULL COMMENT '创建时间',
   `obj_id` bigint DEFAULT NULL COMMENT '业务id',
+  `update_time` bigint DEFAULT NULL COMMENT '更新时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考生轨迹';
 

+ 15 - 11
themis-business/src/main/resources/mapper/TEExamReexamMapper.xml

@@ -3,22 +3,25 @@
 <mapper namespace="com.qmth.themis.business.dao.TEExamReexamMapper">
 
     <select id="reexamPageRequestList" resultType="com.qmth.themis.business.bean.backend.ReexamListRequestBean">
-        select
-            teer.id as reexamId,
-            tees.room_code as roomCode,
+        select * from(select
+             tees.room_code as roomCode,
             tees.room_name as roomName,
             tees.`identity`,
             tees.name,
-            teer.exam_activity_id as examActivityId,
-            teer.exam_student_id as examStudentId,
-            teer.exam_record_id as examRecordId,
+            tees.exam_activity_id as examActivityId,
+            tees.id as examStudentId,
             tees.course_code as courseCode,
             tees.course_name as courseName,
-            teer.exam_id as examId
+            toer.id as examRecordId,
+            tees.exam_id as examId,
+            tee.exam_count - tees.already_exam_count as examCount,
+            toer.status
         from
-            t_e_exam_reexam teer
-            left join t_e_exam_student tees on
-                tees.id = teer.exam_student_id
+            t_e_exam_student tees
+            left join t_oe_exam_record toer on
+            toer.exam_student_id = toer.exam_student_id
+            left join t_e_exam tee on
+            tees.exam_id = tee.id
             inner join (select distinct tbeiu.room_code from t_b_exam_invigilate_user tbeiu
             <if test="userId != null and userId != ''">
                 where tbeiu.user_id = #{userId}
@@ -45,7 +48,8 @@
                 </if>
                 and tees.enable = 1
             </where>
-            order by tees.room_code
+        ) t where t.examCount = 0 and status = 'finished'
+            order by t.roomCode
     </select>
 
     <select id="reexamPageNotDoneList" resultType="com.qmth.themis.business.bean.backend.ReexamListNotDoneBean">

+ 8 - 8
themis-business/src/main/resources/mapper/TOeExamRecordMapper.xml

@@ -178,9 +178,7 @@
         <include refid="invigilatePageHead"/>
         ,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and
         tiiwi.approve_status = 0) as warningNew
-		,date_format(date_sub(date_add(FROM_UNIXTIME(teea.start_time / 1000,'%Y-%m-%d %H:%i:%s'), interval IFNULL(teea.max_duration_seconds,
-		tee.max_duration_seconds) second), interval teea.max_duration_seconds / 60 - t.duration_seconds
-		minute),'%H:%i:%s') as remainTime
+		,date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s') as remainTime
         <include refid="invigilatePageMiddle"/>
 		<include refid="invigilatePageFoot"/>
         <if test="paperDownload != null and paperDownload != ''">
@@ -200,7 +198,7 @@
 		<include refid="invigilatePageHead" />
 		,t.monitor_live_url as monitorLiveUrl
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.approve_status = 0) as warningNew
-		,date_format(date_sub(date_add(FROM_UNIXTIME(teea.start_time / 1000,'%Y-%m-%d %H:%i:%s'), interval IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) second), interval teea.max_duration_seconds / 60 - t.duration_seconds minute),'%H:%i:%s') as remainTime
+		,date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s') as remainTime
 		<include refid="invigilatePageMiddle" />
 		<include refid="invigilatePageFoot" />
 		<if test="paperDownload != null and paperDownload != ''">
@@ -215,7 +213,7 @@
 		<include refid="invigilatePageHead" />
 		,t.monitor_live_url as monitorLiveUrl
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.approve_status = 0) as warningNew
-		,date_format(date_sub(date_add(FROM_UNIXTIME(teea.start_time / 1000,'%Y-%m-%d %H:%i:%s'), interval IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) second), interval teea.max_duration_seconds / 60 - t.duration_seconds minute),'%H:%i:%s') as remainTime
+		,date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s') as remainTime
 		<include refid="invigilatePageMiddle" />
 		<if test="examId != null and examId != ''">
 			and t.exam_id = #{examId}
@@ -228,9 +226,7 @@
 		,(select count(1) from t_ie_invigilate_exception_info tiiei where tiiei.exam_record_id = t.id) as exceptionCount
 		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.`type` =
 		'FACE_COUNT_ERROR' and tiiwi.`level` = 'D8') as multipleFaceCount
-		,date_format(date_sub(date_add(FROM_UNIXTIME(teea.start_time / 1000,'%Y-%m-%d %H:%i:%s'), interval IFNULL(teea.max_duration_seconds,
-		tee.max_duration_seconds) second), interval teea.max_duration_seconds / 60 - t.duration_seconds
-		minute),'%H:%i:%s') as remainTime
+		,date_format(date_sub(from_unixtime(IFNULL(teea.max_duration_seconds, tee.max_duration_seconds) - t.duration_seconds),INTERVAL 8 HOUR), '%H:%i:%s') as remainTime
 		<include refid="invigilatePageMiddle" />
 		<include refid="invigilatePageFoot" />
 		) t
@@ -467,6 +463,10 @@
 		select (@i := @i + 1) as seq,t.* from(
 		<include refid="invigilatePageHead" />
 		,tes.mobile_number as mobileNumber
+		,t.finish_type as finishType
+		,(select count(1) from t_ie_invigilate_warn_info tiiwi where tiiwi.exam_record_id = t.id and tiiwi.`type` =
+		'FACE_COUNT_ERROR' and tiiwi.`level` = 'D8') as multipleFaceCount
+		,(select count(1) from t_ie_invigilate_exception_info tiiei where tiiei.exam_record_id = t.id) as exceptionCount
 		<include refid="invigilatePageMiddle" />
 		left join t_e_student tes on s.student_id = tes.id
 		<include refid="invigilatePageFoot" />

+ 4 - 7
themis-exam/src/main/java/com/qmth/themis/exam/websocketTemplete/WebSocketOeMessageTemplete.java

@@ -2,7 +2,6 @@ package com.qmth.themis.exam.websocketTemplete;
 
 import com.alibaba.fastjson.JSONObject;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
-import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.WebsocketDto;
 import com.qmth.themis.business.enums.ExamRecordFieldEnum;
@@ -17,8 +16,6 @@ import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
 import java.util.Collections;
-import java.util.Date;
-import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -51,8 +48,8 @@ public class WebSocketOeMessageTemplete {
             throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
         }
         Long recordId = Long.parseLong(String.valueOf(jsonObject.get("recordId")));
-        Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-        if (Objects.isNull(objectMap)) {
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        if (Objects.isNull(examId)) {
             throw new BusinessException(ExceptionResultEnum.RECORD_NO);
         }
         ExamRecordCacheUtil.setPaperDownload(recordId, 0, true);
@@ -80,8 +77,8 @@ public class WebSocketOeMessageTemplete {
             throw new BusinessException("考试累计用时秒数不能为空");
         }
         Integer durationSeconds = Integer.parseInt((String.valueOf(jsonObject.get("durationSeconds"))));
-        Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-        if (Objects.isNull(objectMap)) {
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        if (Objects.isNull(examId)) {
             throw new BusinessException(ExceptionResultEnum.RECORD_NO);
         }
         ExamRecordCacheUtil.setAnswerProgress(recordId, progress, false);

+ 55 - 52
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -1,6 +1,5 @@
 package com.qmth.themis.mq.service.impl;
 
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@@ -234,60 +233,45 @@ public class MqLogicServiceImpl implements MqLogicService {
         mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);//表示成功处理
         Map<String, Object> tranMap = mqDto.getProperties();
         Long recordId = Long.parseLong(String.valueOf(tranMap.get("recordId")));
-        Long clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
         ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(recordId);
         WebsocketStatusEnum websocketStatusEnum = ExamRecordCacheUtil.getClientWebsocketStatus(recordId);
         if (Objects.nonNull(websocketStatusEnum) && !Objects.equals(websocketStatusEnum, WebsocketStatusEnum.ON_LINE) && (!Objects.equals(status, ExamRecordStatusEnum.FIRST_PREPARE) || !Objects.equals(status, ExamRecordStatusEnum.FINISHED) || !Objects.equals(status, ExamRecordStatusEnum.PERSISTED))) {
-            Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-            if (Objects.nonNull(objectMap)) {
-                Integer diff = 0;
-                if (Objects.nonNull(clientLastSyncTime)) {//大于等于当前时间,说明未重连或重登录
-                    Long l = (System.currentTimeMillis() - clientLastSyncTime) / 1000;
-                    diff = l.intValue();
-                }
-                Long examId = ExamRecordCacheUtil.getExamId(recordId);
-                Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
-                Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
-                ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
-                ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
-                Long breakId = null;
-                //增加断点记录,获取剩余断点次数
-                Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ? 0 : ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
-                Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
-                if (leftBreakResumeCount < 0) {
-                    Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
-                    teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
-                } else {
-                    alreadyBreakCount++;
-                    breakId = Constants.idGen.next();
-                    ExamRecordCacheUtil.setLastBreakId(recordId, breakId, false);
-                    ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF, false);
-                    Long lastBreakTime = System.currentTimeMillis();
-                    ExamRecordCacheUtil.setLastBreakTime(recordId, lastBreakTime, 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, lastBreakTime, alreadyBreakCount, lastStartTime};
-                    examRecordService.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.assembleSendOneWayMsg(mqDtoBreak);
-                    //考试断点异常原因 发送mq end
-                    //更新场次-考试记录缓存
-                    ExamActivityRecordCacheUtil.setExamRecordStatus(examActivityId, recordId, ExamRecordCacheUtil.getStatus(recordId));
-                }
-                //增加异常日志
-                TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, ExceptionEnum.NET_TIME_OUT.getCode(), ExceptionEnum.NET_TIME_OUT, diff);
-                tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
-                TEExamStudentLog teExamStudentLog = new TEExamStudentLog(SystemOperationEnum.BREAK_OFF.name(), SystemOperationEnum.BREAK_OFF.getCode(), SystemOperationEnum.BREAK_OFF.getCode(), examStudentCacheBean.getStudentId(), examStudentId, recordId, breakId);
-                teExamStudentLogService.save(teExamStudentLog);
-
-                //发送移动端监考退出考试mq消息 start
-                MqDto mqDtoExamStop = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_STOP.name(), recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
-                mqDtoService.assembleSendOneWayMsg(mqDtoExamStop);
-                //发送移动端监考退出考试mq消息 end
+            Long examId = ExamRecordCacheUtil.getExamId(recordId);
+            Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
+            Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
+            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+            ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
+            Long breakId = null;
+            //增加断点记录,获取剩余断点次数
+            Integer alreadyBreakCount = Objects.isNull(ExamRecordCacheUtil.getAlreadyBreakCount(recordId)) ? 0 : ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+            Integer leftBreakResumeCount = ec.getBreakResumeCount() - alreadyBreakCount;
+            if (leftBreakResumeCount < 0) {
+                Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
+                teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
+            } else {
+                alreadyBreakCount++;
+                breakId = Constants.idGen.next();
+                ExamRecordCacheUtil.setLastBreakId(recordId, breakId, false);
+                ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.BREAK_OFF, false);
+                Long lastBreakTime = System.currentTimeMillis();
+                ExamRecordCacheUtil.setLastBreakTime(recordId, lastBreakTime, 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, lastBreakTime, alreadyBreakCount, lastStartTime};
+                examRecordService.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.assembleSendOneWayMsg(mqDtoBreak);
+                //考试断点异常原因 发送mq end
+                //更新场次-考试记录缓存
+                ExamActivityRecordCacheUtil.setExamRecordStatus(examActivityId, recordId, ExamRecordCacheUtil.getStatus(recordId));
             }
+            //发送移动端监考退出考试mq消息 start
+            MqDto mqDtoExamStop = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXAM_STOP.name(), recordId, MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
+            mqDtoService.assembleSendOneWayMsg(mqDtoExamStop);
+            //发送移动端监考退出考试mq消息 end
         }
         TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);
         tmRocketMessage.setBody(JacksonUtil.parseJson(tmRocketMessage.getBody()));
@@ -538,13 +522,15 @@ public class MqLogicServiceImpl implements MqLogicService {
                     Long l = ((System.currentTimeMillis() - lastBreakTime) / 1000);
                     diff = l.intValue();
                 }
+
                 //修改异常日志
                 String reason = String.valueOf(jsonObject.getJSONObject("reason").get("reason"));
                 UpdateWrapper<TIeInvigilateExceptionInfo> tIeInvigilateExceptionInfoUpdateWrapper = new UpdateWrapper<>();
                 tIeInvigilateExceptionInfoUpdateWrapper.lambda().set(TIeInvigilateExceptionInfo::getInfo, reason)
                         .set(TIeInvigilateExceptionInfo::getType, exceptionEnum)
                         .set(TIeInvigilateExceptionInfo::getDifference, diff)
-                        .eq(TIeInvigilateExceptionInfo::getId, breakId);
+                        .set(TIeInvigilateExceptionInfo::getUpdateTime, System.currentTimeMillis())
+                        .eq(TIeInvigilateExceptionInfo::getObjId, breakId);
                 tIeInvigilateExceptionInfoService.update(tIeInvigilateExceptionInfoUpdateWrapper);
 
                 //修改考生轨迹
@@ -552,6 +538,7 @@ public class MqLogicServiceImpl implements MqLogicService {
                 teExamStudentLogUpdateWrapper.lambda().set(TEExamStudentLog::getType, exceptionEnum.name())
                         .set(TEExamStudentLog::getInfo, exceptionEnum.getCode())
                         .set(TEExamStudentLog::getRemark, reason)
+                        .set(TEExamStudentLog::getUpdateTime, System.currentTimeMillis())
                         .eq(TEExamStudentLog::getObjId, breakId);
                 teExamStudentLogService.update(teExamStudentLogUpdateWrapper);
             }
@@ -629,6 +616,22 @@ public class MqLogicServiceImpl implements MqLogicService {
         tOeExamBreakHistoryService.saveOrUpdate(tOeExamBreakHistory);
         redisUtil.setForHash(RedisKeyHelper.examBreakCacheKey(tOeExamBreakHistory.getId()), SimpleBeanUtil.objectToMap(tOeExamBreakHistory));
 
+        Long clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
+        Integer diff = 0;
+        if (Objects.nonNull(clientLastSyncTime)) {//大于等于当前时间,说明未重连或重登录
+            Long l = (System.currentTimeMillis() - clientLastSyncTime) / 1000;
+            diff = l.intValue();
+        }
+        Long examId = ExamRecordCacheUtil.getExamId(recordId);
+        Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
+        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
+        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
+        //增加异常日志
+        TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, ExceptionEnum.NET_TIME_OUT.getCode(), ExceptionEnum.NET_TIME_OUT, diff, ExamRecordCacheUtil.getLastBreakId(recordId));
+        tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
+        TEExamStudentLog teExamStudentLog = new TEExamStudentLog(SystemOperationEnum.BREAK_OFF.name(), SystemOperationEnum.BREAK_OFF.getCode(), SystemOperationEnum.BREAK_OFF.getCode(), examStudentCacheBean.getStudentId(), examStudentId, recordId, ExamRecordCacheUtil.getLastBreakId(recordId));
+        teExamStudentLogService.save(teExamStudentLog);
+
         Gson gson = new Gson();
         mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);
         TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);