wangliang 4 anni fa
parent
commit
e6bcb14668

+ 16 - 0
themis-business/src/main/java/com/qmth/themis/business/entity/TIeInvigilateExceptionInfo.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.qmth.themis.business.enums.ExceptionEnum;
+import com.qmth.themis.common.contanst.Constants;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -62,6 +63,21 @@ public class TIeInvigilateExceptionInfo implements Serializable {
     @TableField(value = "update_time", fill = FieldFill.UPDATE)
     private Date updateTime;
 
+    public TIeInvigilateExceptionInfo() {
+
+    }
+
+    public TIeInvigilateExceptionInfo(Long examId, Long examActivityId, Long examRecordId, Long examStudentId, String info, ExceptionEnum type,String remark) {
+        this.id = Constants.idGen.next();
+        this.examId = examId;
+        this.examActivityId = examActivityId;
+        this.examRecordId = examRecordId;
+        this.examStudentId = examStudentId;
+        this.info = info;
+        this.type = type;
+        this.remark = remark;
+    }
+
     public static long getSerialVersionUID() {
         return serialVersionUID;
     }

+ 35 - 8
themis-business/src/main/java/com/qmth/themis/business/enums/ExceptionEnum.java

@@ -1,23 +1,50 @@
 package com.qmth.themis.business.enums;
 
+import java.util.Objects;
+
 /**
-* @Description: 异常类型 enum
-* @Param:
-* @return:
-* @Author: wangliang
-* @Date: 2020/7/29
-*/
+ * @Description: 异常类型 enum
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2020/7/29
+ */
 public enum ExceptionEnum {
 
-    NET_TIME_OUT("网络超时");
+    NET_TIME_OUT("网络超时"),
+
+    MACHING_STOP("机器硬件故障、死机"),
+
+    NET_TIME_BREAK("网络断开"),
+
+    SOFTWARE_STOP("软件故障"),
+
+    POWER_CUT("停电"),
+
+    OTHER("其它");
 
     private String code;
 
-    private ExceptionEnum(String code){
+    private ExceptionEnum(String code) {
         this.code = code;
     }
 
     public String getCode() {
         return code;
     }
+
+    /**
+     * 状态转换 toName
+     *
+     * @param code
+     * @return
+     */
+    public static String convertToName(String code) {
+        for (ExceptionEnum e : ExceptionEnum.values()) {
+            if (Objects.equals(e.getCode(), code)) {
+                return e.name();
+            }
+        }
+        return null;
+    }
 }

+ 14 - 0
themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

@@ -9,9 +9,14 @@ import java.util.concurrent.ConcurrentHashMap;
 import javax.annotation.Resource;
 
 import com.qmth.themis.business.bean.exam.*;
+import com.qmth.themis.business.dto.MqDto;
 import com.qmth.themis.business.dto.WebsocketDto;
 import com.qmth.themis.business.enums.FinishTypeEnum;
+import com.qmth.themis.business.enums.MqTagEnum;
+import com.qmth.themis.business.enums.MqTopicEnum;
 import com.qmth.themis.business.enums.WebsocketTypeEnum;
+import com.qmth.themis.business.service.MqDtoService;
+import com.qmth.themis.business.util.JacksonUtil;
 import com.qmth.themis.exam.websocket.WebSocketMobileServer;
 import com.qmth.themis.exam.websocket.WebSocketOeServer;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -53,6 +58,9 @@ public class TEExamController {
     @Resource
     RedisUtil redisUtil;
 
+    @Resource
+    MqDtoService mqDtoService;
+
     @ApiOperation(value = "验证考试口令接口")
     @RequestMapping(value = "/short_code", method = RequestMethod.POST)
     @ApiResponses({@ApiResponse(code = 200, message = "考试信息", response = TEExam.class)})
@@ -123,6 +131,12 @@ public class TEExamController {
                 throw new BusinessException("考试记录id不能为空");
             }
             ExamStartBean examStartBean = teExamService.start(teStudent.getId(), param.getRecordId());
+            if (Objects.nonNull(param.getReason())) {
+                //考试断点异常原因 发送mq start
+                MqDto mqDto = new MqDto(MqTopicEnum.themisTopic.getCode(), MqTagEnum.EXCEPTION_LOG.name(), JacksonUtil.parseJson(param), MqTagEnum.EXCEPTION_LOG, String.valueOf(param.getRecordId()), param.getReason());
+                mqDtoService.assembleSendOneWayMsg(mqDto);
+                //考试断点异常原因 发送mq end
+            }
             ConcurrentHashMap<Long, WebSocketMobileServer> webSocketMap = WebSocketMobileServer.getWebSocketMap();
             if (Objects.nonNull(webSocketMap.get(param.getRecordId()))) {
                 WebSocketMobileServer webSocketMobileServer = webSocketMap.get(param.getRecordId());

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

@@ -196,7 +196,7 @@ public class TEStudentController {
             Long recordId = teStudentCacheDto.getUnFinishedRecordId();
             //获取考试记录缓存
             Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
-            String status = String.valueOf(objectMap.get("status"));
+            ExamRecordStatusEnum status = (ExamRecordStatusEnum) objectMap.get("status");
             if (Objects.isNull(status)) {
                 throw new BusinessException("考试状态不能为空");
             }
@@ -218,7 +218,7 @@ public class TEStudentController {
             //获取剩余断点次数
             Integer leftBreakResumeCount = Objects.isNull(ExamRecordCacheUtil.getLeftBreakResumeCount(recordId)) ? 0 : ExamRecordCacheUtil.getLeftBreakResumeCount(recordId);
             //如果断点时间大于整体断点时间,则强制交卷
-            if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING.name()) || Objects.equals(status, ExamRecordStatusEnum.BREAK_OFF.name())) {
+            if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING) || Objects.equals(status, ExamRecordStatusEnum.BREAK_OFF) || Objects.equals(status, ExamRecordStatusEnum.RESUME_PREPARE)) {
                 if (Objects.nonNull(clientLastSyncTime) && (System.currentTimeMillis() - clientLastSyncTime.getTime() / 1000) > ec.getBreakExpireSeconds()) {
                     teExamService.finish(teStudentCacheDto.getId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
                     List<TEExamDto> list = teExamService.getWaitingExam(teStudent.getId(), examId, orgId);
@@ -236,15 +236,6 @@ public class TEStudentController {
                             map.put("waiting", list);
                         }
                     } else {
-                        //更新考试记录
-                        objectMap.put("status", ExamRecordStatusEnum.RESUME_PREPARE);
-                        objectMap.put("lastBreakTime", new Date());
-                        objectMap.put("leftBreakResumeCount", leftBreakResumeCount);
-                        objectMap.put("lastStartTime", new Date());
-                        redisUtil.setForHash(RedisKeyHelper.examRecordCacheKey(recordId), objectMap);
-                        //发送mq,增加断点次数记录
-                        MqDto mqDto = new MqDto(MqTopicEnum.themisTopic.getCode(), MqTagEnum.EXAM_BREAK_HISTORY.name(), JacksonUtil.parseJson(objectMap), MqTagEnum.EXAM_BREAK_HISTORY, String.valueOf(recordId), "增加断点记录");
-                        mqDtoService.assembleSendOneWayMsg(mqDto);
                         ExamUnFinishBean examUnFinishBean = this.unFinishCommon(recordId, ec, examStudentCacheBean, examActivityCacheBean, examStudentId);
                         map.put("unFinished", examUnFinishBean);
                     }

+ 2 - 0
themis-exam/src/main/java/com/qmth/themis/exam/websocketTemplete/WebSocketOeMessageTemplete.java

@@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -80,6 +81,7 @@ public class WebSocketOeMessageTemplete {
         }
         objectMap.put("answerProgress", progress);
         objectMap.put("durationSeconds", durationSeconds);
+        objectMap.put("clientLastSyncTime", new Date());
         redisUtil.setForHash(RedisKeyHelper.examRecordCacheKey(recordId), objectMap);
         return this.syncAck(body);
     }

+ 53 - 11
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -1,17 +1,16 @@
 package com.qmth.themis.mq.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.google.gson.Gson;
+import com.qmth.themis.business.bean.exam.ExamStartParamBean;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
 import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.constant.SpringContextHolder;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.MqDto;
 import com.qmth.themis.business.entity.*;
-import com.qmth.themis.business.enums.BreakReasonEnum;
-import com.qmth.themis.business.enums.ExamRecordStatusEnum;
-import com.qmth.themis.business.enums.MqTagEnum;
-import com.qmth.themis.business.enums.SystemOperationEnum;
+import com.qmth.themis.business.enums.*;
 import com.qmth.themis.business.service.*;
 import com.qmth.themis.business.templete.TaskExportTemplete;
 import com.qmth.themis.business.templete.TaskImportTemplete;
@@ -81,6 +80,9 @@ public class MqLogicServiceImpl implements MqLogicService {
     @Resource
     TIeExamInvigilateCallService tIeExamInvigilateCallService;
 
+    @Resource
+    TIeInvigilateExceptionInfoService tIeInvigilateExceptionInfoService;
+
     /**
      * mq最大重试次数逻辑
      *
@@ -322,14 +324,10 @@ public class MqLogicServiceImpl implements MqLogicService {
     @Override
     @Transactional
     public void execMqExamBreakHistoryLogic(MqDto mqDto, String key) {
+        ExamStartParamBean examStartParamBean = JacksonUtil.readJson(JacksonUtil.parseJson(mqDto.getBody()), ExamStartParamBean.class);
         //更新考试记录
-        Map<String, Object> objectMap = JacksonUtil.readJson(String.valueOf(mqDto.getBody()), Map.class);
-        String status=(String)objectMap.get("status");
-        if(status!=null) {
-        	objectMap.put("status", ExamRecordStatusEnum.valueOf(status));
-        }
         Long recordId = Long.parseLong(mqDto.getObjId());
-
+        Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
         //先查询之前的断点记录
         QueryWrapper<TOeExamBreakHistory> tOeExamBreakHistoryQueryWrapper = new QueryWrapper<>();
         tOeExamBreakHistoryQueryWrapper.lambda().eq(TOeExamBreakHistory::getExamRecordId, recordId);
@@ -399,7 +397,7 @@ public class MqLogicServiceImpl implements MqLogicService {
     public void execMqLogLogic(MqDto mqDto, String key) {
         Gson gson = new Gson();
         String tag = mqDto.getTag();
-        if (tag.contains(MqTagEnum.MONITOR_LOG.name())) {
+        if (tag.contains(MqTagEnum.MONITOR_LOG.name())) {//监考日志
             TIeExamInvigilateCallLog tIeExamInvigilateCallLog = JacksonUtil.readJson(JacksonUtil.parseJson(mqDto.getBody()), TIeExamInvigilateCallLog.class);
             Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(tIeExamInvigilateCallLog.getExamRecordId()));
             Long examId = null, examActivityId = null, examStudentId = null;
@@ -428,6 +426,50 @@ public class MqLogicServiceImpl implements MqLogicService {
             }
             tIeExamInvigilateCallService.saveOrUpdate(tIeExamInvigilateCall);
             tIeExamInvigilateCallLogService.saveOrUpdate(tIeExamInvigilateCallLog);
+        } else if (tag.contains(MqTagEnum.EXCEPTION_LOG.name())) {//考试断点异常日志
+            ExamStartParamBean examStartParamBean = JacksonUtil.readJson(JacksonUtil.parseJson(mqDto.getBody()), ExamStartParamBean.class);
+            //更新考试记录
+            Long recordId = Long.parseLong(mqDto.getObjId());
+            Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examRecordCacheKey(recordId));
+            //获取最近断点时间
+            Date lastBreakTime = (Date) objectMap.get("lastBreakTime");
+            Long diff = null;
+            if (Objects.nonNull(lastBreakTime)) {
+                diff = (System.currentTimeMillis() - lastBreakTime.getTime()) / 1000 * 60;
+            }
+            //先查询之前的断点记录
+            QueryWrapper<TOeExamBreakHistory> tOeExamBreakHistoryQueryWrapper = new QueryWrapper<>();
+            tOeExamBreakHistoryQueryWrapper.lambda().eq(TOeExamBreakHistory::getExamRecordId, recordId);
+            List<TOeExamBreakHistory> tOeExamBreakHistoryList = tOeExamBreakHistoryService.list(tOeExamBreakHistoryQueryWrapper);
+            //删除历史断点缓存
+            if (Objects.nonNull(tOeExamBreakHistoryList) && tOeExamBreakHistoryList.size() > 0) {
+                tOeExamBreakHistoryList.forEach(s -> {
+                    redisUtil.delete(RedisKeyHelper.examBreakCacheKey(s.getId()));
+                });
+            }
+            //增加断点记录
+            TOeExamBreakHistory tOeExamBreakHistory = new TOeExamBreakHistory(recordId, new Date(), BreakReasonEnum.NET_TIME_OUT, BreakReasonEnum.NET_TIME_OUT.name());
+            tOeExamBreakHistoryService.save(tOeExamBreakHistory);
+            redisUtil.setForHash(RedisKeyHelper.examBreakCacheKey(tOeExamBreakHistory.getId()), SimpleBeanUtil.objectToMap(tOeExamBreakHistory));
+
+            Integer leftBreakResumeCount = Objects.isNull(ExamRecordCacheUtil.getLeftBreakResumeCount(recordId)) ? 0 : ExamRecordCacheUtil.getLeftBreakResumeCount(recordId);
+            objectMap.put("lastBreakId", tOeExamBreakHistory.getId());
+            objectMap.put("status", ExamRecordStatusEnum.RESUME_PREPARE);
+            objectMap.put("lastBreakTime", new Date());
+            objectMap.put("leftBreakResumeCount", leftBreakResumeCount--);
+            objectMap.put("lastStartTime", new Date());
+            redisUtil.setForHash(RedisKeyHelper.examRecordCacheKey(recordId), objectMap);
+
+            //增加异常日志
+            JSONObject jsonObject = (JSONObject) JSONObject.parse(examStartParamBean.getReason());
+            Long examId = Long.parseLong(String.valueOf(objectMap.get("examId")));
+            Long examStudentId = Long.parseLong(String.valueOf(objectMap.get("examStudentId")));
+            Long examActivityId = Long.parseLong(String.valueOf(objectMap.get("examActivityId")));
+            TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId, recordId, examStudentId, String.valueOf(jsonObject.get("reason")), ExceptionEnum.valueOf(String.valueOf(jsonObject.get("type"))), String.valueOf(diff));
+            tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);
+            teExamStudentLogService.saveStudentLogInfo(mqDto.getTimestamp(), MqTagEnum.valueOf(String.valueOf(mqDto.getType())).getId(), SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode(), JacksonUtil.parseJson(mqDto));
+        } else if (tag.contains(MqTagEnum.WARNING_LOG.name())) {//考试预警日志
+
         }
         mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);
         TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);