package com.qmth.themis.mq.service.impl;

import cn.hutool.core.date.BetweenFormater;
import cn.hutool.core.date.DateUtil;
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;
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.constant.SpringContextHolder;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.dto.WarningDto;
import com.qmth.themis.business.entity.*;
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;
import com.qmth.themis.business.templete.TaskSyncTemplete;
import com.qmth.themis.business.templete.impl.*;
import com.qmth.themis.business.util.JacksonUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.TencentYunUtil;
import com.qmth.themis.business.util.UidUtil;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.GsonUtil;
import com.qmth.themis.common.util.SimpleBeanUtil;
import com.qmth.themis.mq.service.MqLogicService;
import com.tencentcloudapi.vod.v20180717.models.MediaBasicInfo;
import com.tencentcloudapi.vod.v20180717.models.MediaInfo;
import com.tencentcloudapi.vod.v20180717.models.SearchMediaResponse;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description: mq执行逻辑 impl
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/7/31
 */
@Service
public class MqLogicServiceImpl implements MqLogicService {

    private final static Logger log = LoggerFactory.getLogger(MqLogicServiceImpl.class);

    @Resource
    RedisUtil redisUtil;

    @Resource
    TMRocketMessageService tmRocketMessageService;

    @Resource
    TEUserLogService teUserLogService;

    @Resource
    TEExamStudentLogService teExamStudentLogService;

    @Resource
    TBSessionService tbSessionService;

//    @Resource
//    MyThreadPool myThreadPool;

    @Resource
    TOeExamBreakHistoryService tOeExamBreakHistoryService;

    @Resource
    TOeExamRecordService examRecordService;

    @Resource
    TOeFaceVerifyHistoryService faceVerifyHistoryService;

    @Resource
    TOeLivenessVerifyHistoryService livenessVerifyHistoryService;

    @Resource
    CommonService commonService;

    @Resource
    TIeExamInvigilateCallLogService tIeExamInvigilateCallLogService;

    @Resource
    TIeExamInvigilateCallService tIeExamInvigilateCallService;

    @Resource
    TIeInvigilateExceptionInfoService tIeInvigilateExceptionInfoService;

    @Resource
    TEExamService teExamService;

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    WarningService warningService;

    @Resource
    TEConfigService teConfigService;

    @Resource
    UidUtil uidUtil;

    @Resource
    TencentYunUtil tencentYunUtil;

    @Resource
    TMTencentVideoMessageService tencentVideoMessageService;

    @Resource
    TIpRegionService tIpRegionService;

    @Resource
    TEExamStudentOnlineLogService teExamStudentOnlineLogService;

    @Resource
    TBUserService tbUserService;

    @Resource
    TIeExamMediaLogService tIeExamMediaLogService;

    @Resource
    ThemisCacheService themisCacheService;

    /**
     * mq最大重试次数逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqMaxReconsumeTime(MqDto mqDto, String key) {
        //超过最大重试次数，保存到数据库，后续可以发短信通知系统管理人员
        tmRocketMessageService.saveMqMessageError(mqDto, key);
    }

    /**
     * 用户轨迹逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqUserLogLogic(MqDto mqDto, String key) {
        String tag = mqDto.getTag();
        if (Objects.equals(MqTagEnum.USER.name(), tag)) {
            TEUserLog teUserLog = new TEUserLog(String.valueOf(mqDto.getBody()),
                    SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode(),
                    SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode(),
                    Long.parseLong(String.valueOf(mqDto.getObjId())));
            teUserLogService.save(teUserLog);
        } else if (Objects.equals(MqTagEnum.STUDENT.name(), tag)) {
            TEExamStudentLog teExamStudentLog = null;
            String ip = null, country = null, region = null, province = null, city = null, isp = null;
            if (Objects.nonNull(mqDto.getProperties())) {
                Long recordId = Long.parseLong(String.valueOf(mqDto.getProperties().get(SystemConstant.EXAM_RECORD_ID)));
                ip = Objects.nonNull(recordId) ? ExamRecordCacheUtil.getClientCurrentIp(recordId) : mqDto.getObjName();
                String[] strs = SystemConstant.getIpInfo(ip);
                if (Objects.nonNull(strs) && strs.length > 0) {
                    country = strs[0];
                    region = strs[1];
                    province = strs[2];
                    city = strs[3];
                    isp = strs[4];
                }
                String type = Objects.nonNull(mqDto.getProperties().get(SystemConstant.TYPE)) ?
                        String.valueOf(mqDto.getProperties().get(SystemConstant.TYPE)) :
                        SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode();
                String info = type;
                SystemOperationEnum systemOperationEnum = SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody()));
                String remark = String.valueOf(mqDto.getProperties().get(SystemConstant.REMARK));
                Long objId = null;
                Integer breachLogStatus = null;
                JSONObject jsonObject = new JSONObject();
                String title = systemOperationEnum.getTitle();
                if (systemOperationEnum == SystemOperationEnum.BREACH_HANDLE || systemOperationEnum == SystemOperationEnum.BREACH_REVOKE) {
                    if (systemOperationEnum == SystemOperationEnum.BREACH_HANDLE) {
                        remark = BreachTypeEnum.valueOf(remark).getCode();
                    } else {
                        remark = BreachCancelTypeEnum.valueOf(remark).getCode();
                    }
                    info = "【" + systemOperationEnum.getTitle() + "，原因：" + remark + "】" + type;
                    objId = Long.parseLong(String.valueOf(mqDto.getProperties().get(SystemConstant.OBJ_ID)));
                    breachLogStatus = Integer.parseInt(String.valueOf(mqDto.getProperties().get(SystemConstant.BREACH_LOG_STATUS)));
                    jsonObject.put(SystemConstant.BREACH_LOG_STATUS, breachLogStatus);
                    jsonObject.put(SystemConstant.TITLE, systemOperationEnum.getTitle());

                    List<TEExamStudentLog> teExamStudentLogList = teExamStudentLogService.list(new QueryWrapper<TEExamStudentLog>().lambda()
                            .eq(TEExamStudentLog::getObjId, objId)
                            .eq(TEExamStudentLog::getType, SystemOperationEnum.BREACH_HANDLE));
                    if (!CollectionUtils.isEmpty(teExamStudentLogList)) {
                        for (TEExamStudentLog t : teExamStudentLogList) {
                            JSONObject object = JSONObject.parseObject(t.getTitle());
                            object.put(SystemConstant.BREACH_LOG_STATUS, breachLogStatus);
                            t.setTitle(object.toJSONString());
                        }
                        teExamStudentLogService.saveOrUpdateBatch(teExamStudentLogList);
                    }
                    title = jsonObject.toJSONString();
                    ip = null;
                    country = null;
                    region = null;
                    province = null;
                    city = null;
                    isp = null;
                }
                teExamStudentLog = new TEExamStudentLog(String.valueOf(mqDto.getBody()), info, remark,
                        Long.parseLong(String.valueOf(mqDto.getObjId())),
                        Long.parseLong(String.valueOf(mqDto.getProperties().get(SystemConstant.EXAM_STUDENT_ID))),
                        recordId, title, ip, country, region, province, city, isp);
                teExamStudentLog.setObjId(objId);
            } else {
                if (Objects.nonNull(mqDto.getObjName())) {
                    ip = mqDto.getObjName();
                    String[] strs = SystemConstant.getIpInfo(ip);
                    if (Objects.nonNull(strs) && strs.length > 0) {
                        country = strs[0];
                        region = strs[1];
                        province = strs[2];
                        city = strs[3];
                        isp = strs[4];
                    }
                }
                teExamStudentLog = new TEExamStudentLog(String.valueOf(mqDto.getBody()),
                        SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode(),
                        SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody())).getCode(),
                        Long.parseLong(String.valueOf(mqDto.getObjId())), ip, country, region, province, city, isp);
                SystemOperationEnum systemOperationEnum = SystemOperationEnum.valueOf(String.valueOf(mqDto.getBody()));
                if (systemOperationEnum == SystemOperationEnum.LOGOUT && Objects.nonNull(mqDto.getObjId())) {
                    QueryWrapper<TBSession> tbSessionQueryWrapper = new QueryWrapper<>();
                    tbSessionQueryWrapper.lambda().eq(TBSession::getIdentity, String.valueOf(mqDto.getObjId()));
                    tbSessionService.remove(tbSessionQueryWrapper);
                }
            }
            teExamStudentLogService.save(teExamStudentLog);
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * session逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqSessionLogic(MqDto mqDto, String key) {
        String sessionId = mqDto.getObjId();
        TBSession tbSession = (TBSession) redisUtil.getUserSession(sessionId);
        if (Objects.isNull(tbSession)) {
            throw new BusinessException("缓存session为空");
        }
        tbSessionService.saveOrUpdate(tbSession);
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * task逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    public void execMqTaskLogic(MqDto mqDto, String key) {
        Map<String, Object> map = (Map<String, Object>) mqDto.getBody();
        String tag = mqDto.getTag();
//        myThreadPool.arbitratePoolTaskExecutor.execute(() -> {
        if (tag.contains("Import".toUpperCase())) {
            TaskImportTemplete taskImportTemplete = null;
            if (Objects.equals(MqTagEnum.EXAM_STUDENT_IMPORT.name(), tag)) {
                taskImportTemplete = SpringContextHolder.getBean(TaskExamStudentImportTemplete.class);
            } else if (Objects.equals(MqTagEnum.ROOM_CODE_IMPORT.name(), tag)) {
                taskImportTemplete = SpringContextHolder.getBean(TaskRoomCodeImportTemplete.class);
            } else if (Objects.equals(MqTagEnum.EXAM_PAPER_IMPORT.name(), tag)) {
                taskImportTemplete = SpringContextHolder.getBean(TaskExamPaperImportTemplete.class);
            }
            try {
                taskImportTemplete.importTask(map);
            } catch (IOException e) {
                log.error(SystemConstant.LOG_ERROR, e);
            }
        } else if (tag.contains("Push".toUpperCase())) {
            TaskSyncTemplete taskSyncTemplete = null;
            if (Objects.equals(MqTagEnum.CLOUD_MARK_DATA_PUSH.name(), tag)) {
                taskSyncTemplete = SpringContextHolder.getBean(TaskPushCloudMarkTemplete.class);
            }
            try {
                taskSyncTemplete.syncTask(map);
            } catch (IOException e) {
                log.error(SystemConstant.LOG_ERROR, e);
            }
        } else if (tag.contains("Sync".toUpperCase())) {
            TaskSyncTemplete taskSyncTemplete = null;
            if (Objects.equals(MqTagEnum.CLOUD_MARK_SCORE_SYNC.name(), tag)) {
                taskSyncTemplete = SpringContextHolder.getBean(TaskSyncCloudMarkTemplete.class);
            }
            try {
                taskSyncTemplete.syncTask(map);
            } catch (IOException e) {
                log.error(SystemConstant.LOG_ERROR, e);
            }
        } else {
            TaskExportTemplete taskExportTemplete = null;
            if (Objects.equals(MqTagEnum.ROOM_CODE_EXPORT.name(), tag)) {
                taskExportTemplete = SpringContextHolder.getBean(TaskRoomCodeExportTemplete.class);
            } else if (Objects.equals(MqTagEnum.EXAM_STUDENT_EXPORT.name(), tag)) {
                taskExportTemplete = SpringContextHolder.getBean(TaskExamStudentExportTemplete.class);
            } else if (Objects.equals(MqTagEnum.MARK_RESULT_SIMPLE_EXPORT.name(), tag)) {
                taskExportTemplete = SpringContextHolder.getBean(TaskMarkResultSimpleExportTemplete.class);
            } else if (Objects.equals(MqTagEnum.MARK_RESULT_STANDARD_EXPORT.name(), tag)) {
                taskExportTemplete = SpringContextHolder.getBean(TaskMarkResultStandardExportTemplete.class);
            }
            try {
                taskExportTemplete.exportTask(map);
            } catch (IOException e) {
                log.error(SystemConstant.LOG_ERROR, e);
            }
        }
//        });
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * websocket超时退出逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqWebsocketUnNormalLogic(MqDto mqDto, String key) throws NoSuchAlgorithmException {
        Map<String, Object> tranMap = mqDto.getProperties();
        Long recordId = Long.parseLong(String.valueOf(tranMap.get(SystemConstant.RECORD_ID)));
        ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(recordId);
        WebsocketStatusEnum websocketStatusEnum = ExamRecordCacheUtil.getClientWebsocketStatus(recordId);
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        if (Objects.isNull(examStudentId)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            examStudentId = tOeExamRecord.getExamStudentId();
        }
        if (Objects.nonNull(websocketStatusEnum) && (Objects.equals(websocketStatusEnum, WebsocketStatusEnum.OFF_LINE)
                && Objects.equals(status, ExamRecordStatusEnum.ANSWERING))) {
            examRecordService.examBreakLogic(recordId, true);
            SystemConstant.clientMonitorStatusStop(recordId);

            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
            Optional.ofNullable(examStudentCacheBean).orElseThrow(() -> new BusinessException("考生数据为空"));

            //2022/03/28加入更新移动端第一、第二机位推流状态
            examRecordService.sendExamRecordDataSaveMq(recordId, System.currentTimeMillis());

//            Map<String, Object> properties = new HashMap<>();
//            properties.put(SystemConstant.REMOVE_WEBSOCKET, true);
            //发送移动端监考退出考试mq消息 start
//            MqDto mqDtoExamStop = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_STOP.name(), recordId,
//                    MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
//            mqDtoService.assembleSendOneOrderMsg(mqDtoExamStop);

//            SystemConstant.mobileMonitorStatusStop(examStudentCacheBean.getStudentId(), recordId, true);
            //发送移动端监考退出考试mq消息 end
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 腾讯云视频回调逻辑
     *
     * @param mqDto
     * @param key
     * @throws NoSuchAlgorithmException
     */
    @Override
    @Transactional
    public void execMqTencentVideoLogic(MqDto mqDto, String key) throws NoSuchAlgorithmException, IOException {
        Map<String, Object> tranMap = mqDto.getProperties();
        Long recordId = Long.parseLong(String.valueOf(tranMap.get(SystemConstant.RECORD_ID)));
        Map<String, Object> resultMap = tencentYunUtil.callTencentVideo(recordId);

        if (!CollectionUtils.isEmpty(resultMap)) {
            SearchMediaResponse response = (SearchMediaResponse) resultMap.get("object");
            if (Objects.nonNull(response) && response.getTotalCount().intValue() > 0) {
                MediaInfo[] mediaInfos = response.getMediaInfoSet();
                for (int i = 0; i < mediaInfos.length; i++) {
                    MediaBasicInfo mediaBasicInfo = mediaInfos[i].getBasicInfo();
                    String mediaBasicName = mediaBasicInfo.getName();
                    String videoSource = SystemConstant.getMonitorRecordStreamId(mediaBasicName);
                    String[] timeArrays = mediaBasicName.split("_");
                    Long startTime = DateUtil.parse(timeArrays[6], SystemConstant.VIDEO_TIME_FORMAT).getTime();
                    Long endTime = DateUtil.parse(timeArrays[7], SystemConstant.VIDEO_TIME_FORMAT).getTime();
                    boolean lock = false;
                    for (int y = 0; y < SystemConstant.CONSUME_MESSAGE_BATCH_MAX_SIZE; y++) {
                        lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_TIME_OUT);
                        if (lock) {
                            try {
                                tencentVideoMessageService.saveAndCount(new TMTencentVideoMessage(response.getRequestId(), JacksonUtil.parseJson(response), recordId, videoSource, startTime, endTime, mediaBasicInfo.getMediaUrl()));
                                break;
                            } finally {
                                redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource);
                            }
                        } else {
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
            }
        } else {
            mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);//表示成功处理
            redisUtil.delete(key, mqDto.getId());
        }
    }

    /**
     * 计算客观分逻辑
     *
     * @param mqDto
     * @param key
     */
//    @Override
//    @Transactional
//    public void execMqCalculateObjectiveScoreLogic(MqDto mqDto, String key) {
//        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
//        examRecordService.calculateObjectiveScore(param);
//        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
//    }

    /**
     * 人脸验证保存
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqFaceVerifySaveLogic(MqDto mqDto, String key) {
        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
        Long id = Long.parseLong(String.valueOf(param.get(SystemConstant.ID)));
        Long recordId = Long.parseLong(String.valueOf(param.get(SystemConstant.RECORD_ID)));
        String type = (String) param.get(SystemConstant.TYPE);
        String photoUrl = (String) param.get("photoUrl");
        Integer faceCount = (Integer) param.get("faceCount");
        Double similarity = (Double) param.get("similarity");
        Double realness = (Double) param.get("realness");
        Long time = (Long) param.get(SystemConstant.HEADER_TIME);
        String exception = (String) param.get("exception");
        TEConfig teConfig = teConfigService.getGlobalConfig();
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        if (Objects.isNull(examStudentId)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            examStudentId = tOeExamRecord.getExamStudentId();
        }
        String ip = null, country = null, region = null, province = null, city = null, isp = null;
        ip = Objects.nonNull(recordId) ? ExamRecordCacheUtil.getClientCurrentIp(recordId) : mqDto.getObjName();
        String[] strs = SystemConstant.getIpInfo(ip);
        if (Objects.nonNull(strs) && strs.length > 0) {
            country = strs[0];
            region = strs[1];
            province = strs[2];
            city = strs[3];
            isp = strs[4];
        }
        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
        if (Objects.nonNull(photoUrl) && (Objects.equals(type.toUpperCase(), ExamTypeEnum.FIRST_START.name()) || Objects.equals(type.toUpperCase(), ExamTypeEnum.RESUME_START.name()))) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(PhotoTypeEnum.FACE_VERIFY_PHOTO.name(), photoUrl);
            TEExamStudentLog teExamStudentLog = new TEExamStudentLog(type, ExamTypeEnum.valueOf(type).getCode(),
                    jsonObject.toJSONString(), examStudentCacheBean.getStudentId(), examStudentCacheBean.getId(),
                    recordId, ExamTypeEnum.valueOf(type).getTitle(), ip, country, region, province, city, isp);
            faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
            teExamStudentLogService.saveOrUpdate(teExamStudentLog);
        } else if (Objects.equals(type.toUpperCase(), ExamTypeEnum.IN_PROCESS.name())) {
            VerifyExceptionEnum warningEnum = VerifyExceptionEnum.valueOf(exception);
            WarningDto warningDto = new WarningDto(warningEnum, faceCount, realness, recordId, photoUrl, ip, country, region, province, city, isp);
            if (Objects.equals(VerifyExceptionEnum.NONE, warningEnum)) {//无异常，往考生日志表里插一条
                if (Objects.nonNull(realness) && realness.intValue() == 0) {//真实性异常
                    String realnessErrorRandom = ExamRecordCacheUtil.getRealnessErrorRandom(recordId);
                    if (Objects.isNull(realnessErrorRandom)) {
                        realnessErrorRandom = SystemConstant.getNanoId();
                        ExamRecordCacheUtil.setRealnessErrorRandom(recordId, realnessErrorRandom);
                    }
                    ExamRecordCacheUtil.setRealnessErrorNum(recordId, new AtomicInteger(ExamRecordCacheUtil.getRealnessErrorNum(recordId).incrementAndGet()));
                    warningDto.setRandom(realnessErrorRandom);
                    faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, realnessErrorRandom);
                    Integer count = ExamRecordCacheUtil.getRealnessErrorNum(recordId).get();
                    if (count > teConfig.getRealnessCount()) {
                        ExamRecordCacheUtil.setRealnessErrorRandom(recordId, SystemConstant.getNanoId());
                        ExamRecordCacheUtil.setRealnessErrorNum(recordId, new AtomicInteger(0));
                        warningService.realnessError(warningDto);
                    }
                } else {
                    faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
                }
            } else {
                if (Objects.equals(VerifyExceptionEnum.FACE_COUNT_ERROR, warningEnum)) {//人脸数量异常
                    if (faceCount > 1) {//多张人脸
                        String multipleFaceRandom = ExamRecordCacheUtil.getMultipleFaceCountErrorRandom(recordId);
                        if (Objects.isNull(multipleFaceRandom)) {
                            multipleFaceRandom = SystemConstant.getNanoId();
                            ExamRecordCacheUtil.setMultipleFaceCountErrorRandom(recordId, multipleFaceRandom);
                        }
                        ExamRecordCacheUtil.setMultipleFaceCountErrorNum(recordId, new AtomicInteger(ExamRecordCacheUtil.getMultipleFaceCountErrorNum(recordId).incrementAndGet()));
                        warningDto.setRandom(multipleFaceRandom);
                        faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, multipleFaceRandom);
                        Integer count = ExamRecordCacheUtil.getMultipleFaceCountErrorNum(recordId).get();
                        if (count > teConfig.getMultipleFaceCountError()) {
                            ExamRecordCacheUtil.setMultipleFaceCountErrorRandom(recordId, SystemConstant.getNanoId());
                            ExamRecordCacheUtil.setMultipleFaceCountErrorNum(recordId, new AtomicInteger(0));
                            warningService.faceCountError(warningDto);
                        }
                    } else if (faceCount <= 0) {
                        String noFaceRandom = ExamRecordCacheUtil.getNoFaceCountErrorRandom(recordId);
                        if (Objects.isNull(noFaceRandom)) {
                            noFaceRandom = SystemConstant.getNanoId();
                            ExamRecordCacheUtil.setNoFaceCountErrorRandom(recordId, noFaceRandom);
                        }
                        ExamRecordCacheUtil.setNoFaceCountErrorNum(recordId, new AtomicInteger(ExamRecordCacheUtil.getNoFaceCountErrorNum(recordId).incrementAndGet()));
                        warningDto.setRandom(noFaceRandom);
                        faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, noFaceRandom);
                        Integer count = ExamRecordCacheUtil.getNoFaceCountErrorNum(recordId).get();
                        if (count > teConfig.getNoFaceCountError()) {
                            ExamRecordCacheUtil.setNoFaceCountErrorRandom(recordId, SystemConstant.getNanoId());
                            ExamRecordCacheUtil.setNoFaceCountErrorNum(recordId, new AtomicInteger(0));
                            warningService.faceCountError(warningDto);
                        }
                    }
                } else if (Objects.equals(VerifyExceptionEnum.FACE_COMPARE_ERROR, warningEnum)) {//人脸比对异常
                    String faceCompareErrorRandom = ExamRecordCacheUtil.getFaceCompareErrorRandom(recordId);
                    if (Objects.isNull(faceCompareErrorRandom)) {
                        faceCompareErrorRandom = SystemConstant.getNanoId();
                        ExamRecordCacheUtil.setFaceCompareErrorRandom(recordId, faceCompareErrorRandom);
                    }
                    ExamRecordCacheUtil.setFaceCompareErrorNum(recordId, new AtomicInteger(ExamRecordCacheUtil.getFaceCompareErrorNum(recordId).incrementAndGet()));
                    warningDto.setRandom(faceCompareErrorRandom);
                    faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, faceCompareErrorRandom);
                    Integer count = ExamRecordCacheUtil.getFaceCompareErrorNum(recordId).get();
                    if (count > teConfig.getTotalFaceCompareErrorCount()) {
                        ExamRecordCacheUtil.setFaceCompareErrorRandom(recordId, SystemConstant.getNanoId());
                        ExamRecordCacheUtil.setFaceCompareErrorNum(recordId, new AtomicInteger(0));
                        warningService.faceCompareError(warningDto);
                    }
                } else if (Objects.equals(VerifyExceptionEnum.EYE_CLOSE_ERROR, warningEnum)) {//闭眼检测异常
                    String eyeCloseErrorRandom = ExamRecordCacheUtil.getEyeCloseErrorRandom(recordId);
                    if (Objects.isNull(eyeCloseErrorRandom)) {
                        eyeCloseErrorRandom = SystemConstant.getNanoId();
                        ExamRecordCacheUtil.setEyeCloseErrorRandom(recordId, eyeCloseErrorRandom);
                    }
                    ExamRecordCacheUtil.setEyeCloseErrorNum(recordId, new AtomicInteger(ExamRecordCacheUtil.getEyeCloseErrorNum(recordId).incrementAndGet()));
                    warningDto.setRandom(eyeCloseErrorRandom);
                    faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, eyeCloseErrorRandom);
                    Integer count = ExamRecordCacheUtil.getEyeCloseErrorNum(recordId).get();
                    if (count > teConfig.getTotalEyeCloseErrorCount()) {
                        ExamRecordCacheUtil.setEyeCloseErrorRandom(recordId, SystemConstant.getNanoId());
                        ExamRecordCacheUtil.setEyeCloseErrorNum(recordId, new AtomicInteger(0));
                        warningService.eyeCloseError(warningDto);
                    }
                }
            }
        } else {
            faceVerifyHistoryService.save(id, recordId, type, photoUrl, faceCount, similarity, realness, time, exception, null);
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 活体验证
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqLivenessVerifySaveLogic(MqDto mqDto, String key) {
        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
        Long id = Long.parseLong(String.valueOf(param.get(SystemConstant.ID)));
        Long recordId = Long.parseLong(String.valueOf(param.get(SystemConstant.RECORD_ID)));
        String type = (String) param.get(SystemConstant.TYPE);
        String actions = (String) param.get("actions");
        Integer retry = (Integer) param.get("retry");
        Long startTime = (Long) param.get("startTime");
        Long finishTime = (Long) param.get("finishTime");
        String exception = (String) param.get("exception");
        String ip = null, country = null, region = null, province = null, city = null, isp = null;
        ip = Objects.nonNull(recordId) ? ExamRecordCacheUtil.getClientCurrentIp(recordId) : mqDto.getObjName();
        String[] strs = SystemConstant.getIpInfo(ip);
        if (Objects.nonNull(strs) && strs.length > 0) {
            country = strs[0];
            region = strs[1];
            province = strs[2];
            city = strs[3];
            isp = strs[4];
        }
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        if (Objects.isNull(examStudentId)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            examStudentId = tOeExamRecord.getExamStudentId();
        }
        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
        if (Objects.equals(type.toUpperCase(), ExamTypeEnum.FIRST_START.name()) || Objects.equals(type.toUpperCase(), ExamTypeEnum.RESUME_START.name())) {
            JSONArray jsonArray = JSONArray.parseArray(actions);
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String photoType = Objects.nonNull(jsonObject.get(SystemConstant.TYPE)) ? String.valueOf(jsonObject.get(SystemConstant.TYPE)) : null;
                String photoUrl = Objects.nonNull(jsonObject.get("photoUrl")) ? String.valueOf(jsonObject.get("photoUrl")) : null;
                if (Objects.nonNull(photoType) && Objects.nonNull(photoUrl) && Objects.equals(photoType.toLowerCase(), "startup")) {
                    JSONObject object = new JSONObject();
                    object.put(PhotoTypeEnum.LIVENESS_VERIFY_PHOTO.name(), photoUrl);
                    TEExamStudentLog teExamStudentLog = new TEExamStudentLog(type,
                            LivenessTypeEnum.valueOf(type).getCode(), object.toJSONString(),
                            examStudentCacheBean.getStudentId(), examStudentCacheBean.getId(), recordId,
                            LivenessTypeEnum.valueOf(type).getTitle(), ip, country, region, province, city, isp);
                    livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, null);
                    teExamStudentLogService.saveOrUpdate(teExamStudentLog);
                    break;
                }
            }
        } else if (Objects.equals(type.toUpperCase(), ExamTypeEnum.IN_PROCESS.name())) {
            VerifyExceptionEnum warningEnum = VerifyExceptionEnum.valueOf(exception);
            if (Objects.equals(VerifyExceptionEnum.NONE, warningEnum)) {//无异常，往考生日志表里插一条
                livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, null);
            } else {
                String livenessActionErrorRandom = ExamRecordCacheUtil.getLivenessActionErrorRandom(recordId);
                if (Objects.isNull(livenessActionErrorRandom)) {
                    livenessActionErrorRandom = SystemConstant.getNanoId();
                    ExamRecordCacheUtil.setLivenessActionErrorRandom(recordId, livenessActionErrorRandom);
                }
                WarningDto warningDto = new WarningDto(warningEnum, null, null, recordId, null, ip, country, region, province, city, isp);
                warningDto.setRandom(livenessActionErrorRandom);
                livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, livenessActionErrorRandom);
                ExamRecordCacheUtil.setLivenessActionErrorRandom(recordId, SystemConstant.getNanoId());
                warningService.livenessActionError(warningDto);
            }
        } else {
            livenessVerifyHistoryService.save(id, recordId, type, actions, retry, startTime, finishTime, exception, null);
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 考试记录数据持久化
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqRecordPersistedLogic(MqDto mqDto, String key) {
        try {
            Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
            Long recordId = Long.parseLong(String.valueOf(param.get(SystemConstant.RECORD_ID)));
            Long studentId = Long.parseLong(String.valueOf(param.get(SystemConstant.STUDENT_ID)));
            commonService.persisted(recordId, studentId);

            tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 考试断点记录数据持久化
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqRecordBreakHistoryPersistedLogic(MqDto mqDto, String key) {
        Long breakId = Long.parseLong(mqDto.getObjId());
        tOeExamBreakHistoryService.examRecordBreakHistoryDataSave(breakId);
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    @Override
    @Transactional
    public void execMqRecordUpdateLogic(MqDto mqDto, String key) {
        Long recordId = Long.parseLong(mqDto.getObjId());
        Long updateTime = ExamRecordCacheUtil.getUpdateTime(recordId);
        if (Objects.isNull(updateTime) || (Objects.nonNull(updateTime) &&
                mqDto.getTimestamp().longValue() >= updateTime.longValue())) {
            if (Objects.nonNull(mqDto.getObjName()) && Objects.equals(mqDto.getObjName(), SystemConstant.WARNING_COUNT_UPDATE)) {
                examRecordService.updateWarningCountByDb(recordId);
            } else if (Objects.nonNull(mqDto.getObjName()) && Objects.equals(mqDto.getObjName(), SystemConstant.WARNING_UNREAD_UPDATE)) {
                Map<String, Object> tranMap = mqDto.getProperties();
                Integer number = (Integer) tranMap.get(SystemConstant.NUMBER);
                number = Objects.isNull(number) || number < 0 ? 0 : number;
                examRecordService.updateWarningUnreadByDb(recordId, number);
            } else {
                examRecordService.examRecordDataSave(recordId);
            }
            tIpRegionService.saveIpRegion(recordId);
            Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
            Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
            String examRecordChangeKey = examActivityId + "_" + examStudentCacheBean.getRoomCode();
            themisCacheService.addExamRecordChangeCache(examRecordChangeKey, mqDto.getTimestamp().longValue());
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 监控通话申请逻辑
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqLogLogic(MqDto mqDto, String key) {
        String tag = mqDto.getTag();
        if (Objects.equals(MqTagEnum.MONITOR_LOG.name(), tag)) {//监考日志
            TIeExamInvigilateCallLog tIeExamInvigilateCallLog = JacksonUtil.readJson(JacksonUtil.parseJson(mqDto.getBody()), TIeExamInvigilateCallLog.class);
            Long recordId = tIeExamInvigilateCallLog.getExamRecordId();
            Long examId = null, examActivityId = null, examStudentId = null;
            //有缓存则从缓存取
            if (Objects.nonNull(ExamRecordCacheUtil.getExamActivityId(recordId))) {
                examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
                examId = ExamRecordCacheUtil.getExamId(recordId);
                examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
            } else {//否则数据库取
                TOeExamRecord tOeExamRecord = examRecordService.getById(tIeExamInvigilateCallLog.getExamRecordId());
                examActivityId = tOeExamRecord.getExamActivityId();
                examId = tOeExamRecord.getExamId();
                examStudentId = tOeExamRecord.getExamStudentId();
            }
            tIeExamInvigilateCallLog.setExamId(examId);
            tIeExamInvigilateCallLog.setExamActivityId(examActivityId);
            tIeExamInvigilateCallLog.setExamStudentId(examStudentId);

            QueryWrapper<TIeExamInvigilateCall> tIeExamInvigilateCallQueryWrapper = new QueryWrapper<>();
            tIeExamInvigilateCallQueryWrapper.lambda()
                    .eq(TIeExamInvigilateCall::getExamRecordId, tIeExamInvigilateCallLog.getExamRecordId())
                    .eq(TIeExamInvigilateCall::getSource, tIeExamInvigilateCallLog.getSource());
            TIeExamInvigilateCall tIeExamInvigilateCall = tIeExamInvigilateCallService.getOne(tIeExamInvigilateCallQueryWrapper);
            //根据examRecordId和source取TIeExamInvigilateCall记录
            if (Objects.isNull(tIeExamInvigilateCall)) {//没有新增
                tIeExamInvigilateCall = GsonUtil.fromJson(GsonUtil.toJson(tIeExamInvigilateCallLog), TIeExamInvigilateCall.class);
                tIeExamInvigilateCall.setId(uidUtil.getId());
            } else {//否则更新
                tIeExamInvigilateCall.setLiveUrl(tIeExamInvigilateCallLog.getLiveUrl());
                tIeExamInvigilateCall.setStatus(tIeExamInvigilateCallLog.getStatus());
                tIeExamInvigilateCall.setCallStatus(tIeExamInvigilateCallLog.getCallStatus());
                tIeExamInvigilateCall.setType(tIeExamInvigilateCallLog.getType());
                if (Objects.nonNull(tIeExamInvigilateCallLog.getFormUserId())) {
                    tIeExamInvigilateCall.setFormUserId(tIeExamInvigilateCallLog.getFormUserId());
                }
                if (Objects.nonNull(tIeExamInvigilateCallLog.getReceiveUserId())) {
                    tIeExamInvigilateCall.setReceiveUserId(tIeExamInvigilateCallLog.getReceiveUserId());
                }
                if (Objects.equals(tIeExamInvigilateCallLog.getCallStatus(), MonitorCallStatusSourceEnum.START)) {
                    tIeExamInvigilateCall.setStartTime(System.currentTimeMillis());
                }
                if (Objects.equals(tIeExamInvigilateCallLog.getCallStatus(), MonitorCallStatusSourceEnum.CALLING)) {
                    tIeExamInvigilateCall.setCallTime(tIeExamInvigilateCallLog.getCallTime());
                }
                if (Objects.equals(tIeExamInvigilateCallLog.getCallStatus(), MonitorCallStatusSourceEnum.STOP)) {
                    tIeExamInvigilateCall.setEndTime(tIeExamInvigilateCallLog.getEndTime());
                    if (Objects.nonNull(tIeExamInvigilateCall.getCallTime()) &&
                            tIeExamInvigilateCall.getCallTime().longValue() > tIeExamInvigilateCall.getStartTime().longValue()) {
                        try {
                            String formUserName = null, receiveUserName = null;
                            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
                            TBUser formTbUser = tbUserService.getById(tIeExamInvigilateCall.getFormUserId());
                            formUserName = Objects.isNull(formTbUser) ? examStudentCacheBean.getName() : formTbUser.getName();
                            TBUser receiveTbUser = tbUserService.getById(tIeExamInvigilateCall.getReceiveUserId());
                            receiveUserName = Objects.isNull(receiveTbUser) ? examStudentCacheBean.getName() : receiveTbUser.getName();
                            Date callDate = new Date(tIeExamInvigilateCall.getCallTime());
                            Date endDate = new Date(tIeExamInvigilateCall.getEndTime());
                            StringJoiner stringJoiner = new StringJoiner("");
                            stringJoiner.add("发起人：").add(formUserName).add("\r\n").add("接收人：").add(receiveUserName).add("\r\n")
                                    .add("通话时间段：").add(DateUtil.format(callDate, Constants.DEFAULT_DATE_PATTERN)).add("~").add(DateUtil.format(endDate, Constants.DEFAULT_DATE_PATTERN)).add("\r\n")
                                    .add("持续时长约：").add(DateUtil.formatBetween(callDate, endDate, BetweenFormater.Level.SECOND));
                            TIeExamMediaLog tIeExamMediaLog = new TIeExamMediaLog(examId, examActivityId, examStudentId, recordId,
                                    tIeExamInvigilateCall.getSource(), stringJoiner.toString(), tIeExamInvigilateCall.getStartTime(),
                                    tIeExamInvigilateCall.getCallTime(), tIeExamInvigilateCall.getEndTime(), tIeExamInvigilateCall.getFormUserId(), tIeExamInvigilateCall.getReceiveUserId());
                            tIeExamMediaLogService.save(tIeExamMediaLog);
                        } catch (Exception e) {
                        }
                    }
                }
            }
            if (Objects.nonNull(tIeExamInvigilateCall.getUpdateTime())) {
                Long dbtimestamp = Objects.nonNull(tIeExamInvigilateCall.getUpdateTime()) ?
                        tIeExamInvigilateCall.getUpdateTime() :
                        0L;
                //如果mq时间大于则更新
                if (mqDto.getTimestamp() > dbtimestamp) {
                    tIeExamInvigilateCallService.updateById(tIeExamInvigilateCall);
                }
            } else {//否则新增
                tIeExamInvigilateCallService.saveOrUpdate(tIeExamInvigilateCall);
            }
            tIeExamInvigilateCallLogService.saveOrUpdate(tIeExamInvigilateCallLog);
        } else if (Objects.equals(MqTagEnum.EXCEPTION_LOG.name(), tag)) {//考试断点异常日志
            JSONObject jsonObject = JSONObject.parseObject(String.valueOf(mqDto.getBody()));
//            ExceptionEnum exceptionEnum = ExceptionEnum.valueOf(
//                    ExceptionEnum.convertToName(String.valueOf(jsonObject.getJSONObject("reason").get(SystemConstant.TYPE))));
            Long recordId = Long.parseLong(mqDto.getObjId());
            Long lastPrepareTime = ExamRecordCacheUtil.getLastPrepareTime(recordId);
            Long breakId = ExamRecordCacheUtil.getLastBreakId(recordId);
            if (Objects.nonNull(breakId)) {
                //获取断点时间
                Long lastBreakTime = ExamRecordCacheUtil.getLastBreakTime(recordId);
                Integer diff = 0;
                if (Objects.nonNull(lastBreakTime)) {
                    Long l = ((lastPrepareTime - 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.EXAM_BREAK)
                        .set(TIeInvigilateExceptionInfo::getDifference, diff)
                        .set(TIeInvigilateExceptionInfo::getUpdateTime, lastPrepareTime)
                        .eq(TIeInvigilateExceptionInfo::getObjId, breakId);
                tIeInvigilateExceptionInfoService.update(tIeInvigilateExceptionInfoUpdateWrapper);

                //修改考生轨迹
                UpdateWrapper<TEExamStudentLog> teExamStudentLogUpdateWrapper = new UpdateWrapper<>();
                teExamStudentLogUpdateWrapper.lambda().set(TEExamStudentLog::getType, ExceptionEnum.EXAM_BREAK.name())
                        .set(TEExamStudentLog::getInfo, ExceptionEnum.EXAM_BREAK.getCode())
                        .set(TEExamStudentLog::getRemark, reason)
                        .set(TEExamStudentLog::getUpdateTime, lastPrepareTime)
                        .eq(TEExamStudentLog::getObjId, breakId);
                teExamStudentLogService.update(teExamStudentLogUpdateWrapper);
            }
        } else if (Objects.equals(MqTagEnum.ONLINE_LOG.name(), tag)) {//考生上下线
            TEExamStudentOnlineLog teExamStudentOnlineLog = JacksonUtil.readJson(String.valueOf(mqDto.getBody()), TEExamStudentOnlineLog.class);
            if (Objects.nonNull(teExamStudentOnlineLog)) {
                teExamStudentOnlineLogService.save(teExamStudentOnlineLog);
            }
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 重新算分
     *
     * @param mqDto
     * @param key
     */
    @Override
    public void execMqCalculateScoreLogic(MqDto mqDto, String key) {
        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
        Long examId = Long.parseLong(String.valueOf(param.get(SystemConstant.EXAM_ID)));
        Long taskId = Long.parseLong(String.valueOf(param.get(SystemConstant.TASK_ID)));
        teExamService.calculateScore(examId, taskId);
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 更新考生信息
     */
    @Override
    @Transactional
    public void execMqExamStudentUpdateLogic(MqDto mqDto, String key) {
        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
        teExamStudentService.updateByMqMsg(param);
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 考试断点
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqExamBreakLogic(MqDto mqDto, String key) throws NoSuchAlgorithmException {
        ExceptionEnum exceptionEnum = ExceptionEnum.valueOf(String.valueOf(mqDto.getBody()));
        Long recordId = Long.parseLong(mqDto.getObjId());

        //先查询之前的断点记录
//        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(ExamRecordCacheUtil.getLastBreakId(recordId),
                recordId, System.currentTimeMillis(), exceptionEnum, exceptionEnum.name());
        tOeExamBreakHistoryService.saveOrUpdate(tOeExamBreakHistory);
        redisUtil.setForHash(RedisKeyHelper.examBreakCacheKey(tOeExamBreakHistory.getId()),
                SimpleBeanUtil.objectToMap(tOeExamBreakHistory));

        //        Long clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
        //        Integer diff = 0;
        //        Long startTime = System.currentTimeMillis();
        //        if (Objects.nonNull(clientLastSyncTime)) {//大于等于当前时间，说明未重连或重登录
        //            Long l = (startTime - clientLastSyncTime) / 1000;
        //            diff = l.intValue();
        //        }
        Long examId = ExamRecordCacheUtil.getExamId(recordId);
        Long examActivityId = ExamRecordCacheUtil.getExamActivityId(recordId);
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        if (Objects.isNull(examStudentId)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            examStudentId = tOeExamRecord.getExamStudentId();
            examActivityId = tOeExamRecord.getExamActivityId();
            examId = tOeExamRecord.getExamId();
        }
        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
        //增加异常日志
        TIeInvigilateExceptionInfo tIeInvigilateExceptionInfo = new TIeInvigilateExceptionInfo(examId, examActivityId,
                recordId, examStudentId, ExceptionEnum.NET_TIME_OUT.getCode(), ExceptionEnum.NET_TIME_OUT, null,
                ExamRecordCacheUtil.getLastBreakId(recordId));
        //        if (Objects.nonNull(diff)) {
        //            tIeInvigilateExceptionInfo.setCreateTime(clientLastSyncTime);
        //            tIeInvigilateExceptionInfo.setUpdateTime(startTime);
        //        }
        tIeInvigilateExceptionInfoService.saveOrUpdate(tIeInvigilateExceptionInfo);

        //增加考试记录异常数
        examRecordService.updateExceptionCountCache(recordId);

        String ip = ExamRecordCacheUtil.getClientCurrentIp(recordId), country = null, region = null, province = null, city = null, isp = null;
        String[] strs = SystemConstant.getIpInfo(ip);
        if (Objects.nonNull(strs) && strs.length > 0) {
            country = strs[0];
            region = strs[1];
            province = strs[2];
            city = strs[3];
            isp = strs[4];
        }

        TEExamStudentLog teExamStudentLog = new TEExamStudentLog(SystemOperationEnum.BREAK_OFF.name(),
                SystemOperationEnum.BREAK_OFF.getCode(), SystemOperationEnum.BREAK_OFF.getCode(),
                examStudentCacheBean.getStudentId(), examStudentId, recordId,
                ExamRecordCacheUtil.getLastBreakId(recordId), SystemOperationEnum.BREAK_OFF.getTitle(),
                ip, country, region, province, city, isp);
        teExamStudentLogService.save(teExamStudentLog);

        SystemConstant.clientMonitorStatusStop(recordId);
        examRecordService.sendExamRecordDataSaveMq(recordId, System.currentTimeMillis());

//        Map<String, Object> properties = new HashMap<>();
//        properties.put(SystemConstant.REMOVE_WEBSOCKET, true);
        //发送移动端监考退出考试mq消息 start
//        MqDto mqDtoExamStop = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_STOP.name(), recordId,
//                MqTagEnum.EXAM_STOP, String.valueOf(recordId), String.valueOf(recordId));
//        mqDtoService.assembleSendOneOrderMsg(mqDtoExamStop);
        //发送移动端监考退出考试mq消息 end

        //更新移动端第一机位推流状态为stop
//        SystemConstant.mobileMonitorStatusStop(examStudentCacheBean.getStudentId(), recordId, true);

        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 考试断点延时消息
     *
     * @param mqDto
     * @param key
     */
    @Override
    @Transactional
    public void execMqExamBreakDelayLogic(MqDto mqDto, String key) {
        Map<String, Object> tranMap = mqDto.getProperties();
        Long recordId = Long.parseLong(String.valueOf(tranMap.get(SystemConstant.RECORD_ID)));
        ExamRecordStatusEnum statusEnum = ExamRecordCacheUtil.getStatus(recordId);
        if (Objects.nonNull(statusEnum) && !Objects.equals(statusEnum, ExamRecordStatusEnum.FINISHED) && !Objects
                .equals(statusEnum, ExamRecordStatusEnum.PERSISTED) && !Objects
                .equals(statusEnum, ExamRecordStatusEnum.ANSWERING) && !Objects
                .equals(statusEnum, ExamRecordStatusEnum.RESUME_PREPARE)) {
            examRecordService.examBreakLogic(recordId, false);
        }
        tmRocketMessageService.saveMqMessageSuccess(mqDto, key);
    }

    /**
     * 普通消息
     *
     * @param msgs
     * @param consumeConcurrentlyContext
     * @param mqExecTypeEnum
     * @return
     */
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                    ConsumeConcurrentlyContext consumeConcurrentlyContext, MqExecTypeEnum mqExecTypeEnum) {
        MqDto mqDto = null;
        try {
            long threadId = Thread.currentThread().getId();
            String threadName = Thread.currentThread().getName();
            for (MessageExt messageExt : msgs) {
                log.info(":{}-:{} consumer 重试次数:{}", threadId, threadName, messageExt.getReconsumeTimes());
                mqDto = JacksonUtil.readJson(new String(messageExt.getBody(), Constants.CHARSET), MqDto.class);
                log.info(":{}-:{} consumer 接收到的消息:{}", threadId, threadName, JacksonUtil.parseJson(mqDto));
                int reconsumeTime = messageExt.getReconsumeTimes();
                if (reconsumeTime >= SystemConstant.MAXRECONSUMETIMES) {
                    this.execMqMaxReconsumeTime(mqDto, SystemConstant.MQ_TOPIC_BUFFER_LIST);
                } else {
                    if (Objects.nonNull(mqDto.getAck()) && mqDto.getAck().intValue() != SystemConstant.STANDARD_ACK_TYPE
                            && Objects.nonNull(redisUtil.get(SystemConstant.MQ_TOPIC_BUFFER_LIST, mqDto.getId()))
                            && redisUtil.lock(SystemConstant.REDIS_LOCK_MQ_PREFIX + mqDto.getId(),
                            SystemConstant.REDIS_LOCK_MQ_TIME_OUT)) {
                        try {
                            Method method = this.getClass()
                                    .getDeclaredMethod(mqExecTypeEnum.getDesc(), MqDto.class, String.class);
                            log.info(":{}-:{} 准备执行mq exec:{}逻辑", threadId, threadName, method.getName());
                            method.invoke(SpringContextHolder.getBean(MqLogicService.class), mqDto,
                                    SystemConstant.MQ_TOPIC_BUFFER_LIST);
//                            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                        } finally {
                            if (Objects.nonNull(mqDto)) {
                                redisUtil.releaseLock(SystemConstant.REDIS_LOCK_MQ_PREFIX + mqDto.getId());
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("mq普通消息监听，消息消费出错", e);
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;//重试
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;//成功
    }

    /**
     * 延时消息
     *
     * @param msgs
     * @param consumeConcurrentlyContext
     * @param mqExecTypeEnum
     * @return
     */
    @Override
    public ConsumeConcurrentlyStatus consumeMessageDelay(List<MessageExt> msgs,
                                                         ConsumeConcurrentlyContext consumeConcurrentlyContext, MqExecTypeEnum mqExecTypeEnum) {
        MqDto mqDto = null;
        try {
            long threadId = Thread.currentThread().getId();
            String threadName = Thread.currentThread().getName();
            for (MessageExt messageExt : msgs) {
                log.info(":{}-:{} consumer重试次数:{}", threadId, threadName, messageExt.getReconsumeTimes());
                mqDto = JacksonUtil.readJson(new String(messageExt.getBody(), Constants.CHARSET), MqDto.class);
                log.info(":{}-:{} consumer接收到的消息:{}", threadId, threadName, JacksonUtil.parseJson(mqDto));
                int reconsumeTime = messageExt.getReconsumeTimes();
                if (reconsumeTime >= SystemConstant.MAXRECONSUMETIMES) {
                    this.execMqMaxReconsumeTime(mqDto, SystemConstant.MQ_DELAY_TOPIC_BUFFER_LIST);
                } else {
                    if (Objects.nonNull(mqDto.getAck()) && mqDto.getAck().intValue() != SystemConstant.STANDARD_ACK_TYPE
                            && Objects.nonNull(SystemConstant.MQ_DELAY_TOPIC_BUFFER_LIST) && redisUtil
                            .lock(SystemConstant.REDIS_LOCK_MQ_PREFIX + mqDto.getId(),
                                    SystemConstant.REDIS_LOCK_MQ_TIME_OUT)) {
                        try {
                            Method method = this.getClass()
                                    .getDeclaredMethod(mqExecTypeEnum.getDesc(), MqDto.class, String.class);
                            log.info(":{}-:{} 准备执行mq exec:{}逻辑", threadId, threadName, method.getName());
                            method.invoke(SpringContextHolder.getBean(MqLogicService.class), mqDto,
                                    SystemConstant.MQ_DELAY_TOPIC_BUFFER_LIST);
//                            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                        } finally {
                            if (Objects.nonNull(mqDto)) {
                                redisUtil.releaseLock(SystemConstant.REDIS_LOCK_MQ_PREFIX + mqDto.getId());
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("mq延时消息监听，消息消费出错", e);
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;//重试
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;//成功
    }

//    /**
//     * 创建qrcode
//     *
//     * @param videoUrl
//     * @param jsonObject
//     * @return
//     * @throws IOException
//     */
//    private JSONObject createQrCode(String videoUrl, JSONObject jsonObject) throws IOException {
//        InputStream inputStream = null;
//        try {
//            byte[] b = QrCodeUtil.createQRCode(300, 300, videoUrl);
//            inputStream = new ByteArrayInputStream(b);
//            String md5 = DigestUtils.md5Hex(new ByteArrayInputStream(b));
//
//            AliYunOssPublicDomain aliYunOssPublicDomain = ossUtil.getAliYunOssPublicDomain();
//            Boolean oss = (Boolean) aliYunOssPublicDomain.getMap().get(SystemConstant.OSS);
//
//            LocalDateTime nowTime = LocalDateTime.now();
//            StringJoiner stringJoiner = new StringJoiner("");
//            if (!oss) {
//                stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
//            }
//            String fileType = ".png";
//            UploadFileEnum type = UploadFileEnum.upload;
//            stringJoiner.add(type.name()).add(File.separator);
//            stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
//                    .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
//                    .add(String.format("%02d", nowTime.getDayOfMonth()));
//            if (oss) {//上传至oss
//                stringJoiner.add(File.separator).add(SystemConstant.getNanoId()).add(fileType);
//                ossUtil.upload(ossUtil.isPublic(type), stringJoiner.toString(), inputStream, md5);
//                jsonObject.put("qrCodeUrl", aliYunOssPublicDomain.getPublicUrl() + File.separator + stringJoiner.toString());
//            } else {//上传至服务器
//                File finalFile = new File(
//                        stringJoiner.add(File.separator).add(SystemConstant.getNanoId())
//                                .add(fileType).toString());
//                if (!finalFile.exists()) {
//                    finalFile.getParentFile().mkdirs();
//                    finalFile.createNewFile();
//                }
//                String fileHost = (String) aliYunOssPublicDomain.getMap().get(SystemConstant.FILE_HOST);
//                jsonObject.put("qrCodeUrl", "http://" + fileHost + File.separator + finalFile.getPath());
//                FileUtils.copyInputStreamToFile(inputStream, finalFile);
//            }
//        } catch (Exception e) {
//            log.error(SystemConstant.LOG_ERROR, e);
//        } finally {
//            if (Objects.nonNull(inputStream)) {
//                inputStream.close();
//            }
//        }
//        return jsonObject;
//    }
}