package com.qmth.themis.backend.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qmth.themis.business.annotation.ApiJsonObject;
import com.qmth.themis.business.annotation.ApiJsonProperty;
import com.qmth.themis.business.bean.backend.*;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.cache.RedisKeyHelper;
import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
import com.qmth.themis.business.cache.bean.ExamActivityRecordCacheBean;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
import com.qmth.themis.business.config.SystemConfig;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.AuthDto;
import com.qmth.themis.business.dto.ExamPropCountDto;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.entity.*;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.excel.ExportUtils;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.JacksonUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.ServletUtil;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @Description: 监考信息 前端控制器
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/6/25
 */
@Api(tags = "监考信息Controller")
@RestController
@RequestMapping("/${prefix.url.admin}/invigilate")
public class TIeInvigilateController {
    private final static Logger log = LoggerFactory.getLogger(TIeInvigilateController.class);

    @Resource
    MqDtoService mqDtoService;

    @Resource
    TEExamBreachLogService teExamBreachLogService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    TIeInvigilateWarnInfoService tIeInvigilateWarnInfoService;

    @Resource
    TIeInvigilateExceptionInfoService tIeInvigilateExceptionInfoService;

    @Resource
    TEExamStudentLogService teExamStudentLogService;

    @Resource
    TBExamInvigilateUserService tbExamInvigilateUserService;

    @Resource
    TEExamReexamService teExamReexamService;

    @Resource
    TEExamActivityService teExamActivityService;

    @Resource
    TEExamService teExamService;

    @Resource
    TEStudentService teStudentService;

    @Resource
    SystemConfig systemConfig;

//    @ApiOperation(value = "实时监控台列表接口")
//    @RequestMapping(value = "/list", method = RequestMethod.POST)
//    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListBean.class)})
//    public Result list(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
//                       @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
//                       @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
//                       @ApiParam(value = "试题下载状态", required = false) @RequestParam(required = false) Integer paperDownload,
//                       @ApiParam(value = "考生状态", required = false) @RequestParam(required = false) String status,
//                       @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
//                       @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity,
//                       @ApiParam(value = "预警量min", required = false) @RequestParam(required = false) Integer minWarningCount,
//                       @ApiParam(value = "预警量max", required = false) @RequestParam(required = false) Integer maxWarningCount,
//                       @ApiParam(value = "客户端网络通信状态", required = false) @RequestParam(required = false) String clientWebsocketStatus,
//                       @ApiParam(value = "监控设备通信状态", required = false) @RequestParam(required = false) String monitorStatusSource,
//                       @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
//                       @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
//        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
//        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
//        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
//        Long userId = null;
//        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
//            userId = tbUser.getId();
//        }
//        return ResultUtil.ok(tOeExamRecordService.invigilatePageList(new Page<>(pageNumber, pageSize), examId, examActivityId, roomCode, paperDownload, status, name, identity, minWarningCount, maxWarningCount, clientWebsocketStatus, monitorStatusSource, userId));
//    }

    @ApiOperation(value = "实时监控台视频列表接口")
    @RequestMapping(value = "/list/video", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListVideoBean.class)})
    public Result listVideo(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
                            @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
                            @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                            @ApiParam(value = "试题下载状态", required = false) @RequestParam(required = false) Integer paperDownload,
                            @ApiParam(value = "考生状态", required = false) @RequestParam(required = false) String status,
                            @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
                            @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity,
                            @ApiParam(value = "预警量min", required = false) @RequestParam(required = false) Integer minWarningCount,
                            @ApiParam(value = "预警量max", required = false) @RequestParam(required = false) Integer maxWarningCount,
                            @ApiParam(value = "客户端网络通信状态", required = false) @RequestParam(required = false) String clientWebsocketStatus,
                            @ApiParam(value = "监控设备通信状态", required = false) @RequestParam(required = false) String monitorStatusSource,
                            @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
                            @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        return ResultUtil.ok(tOeExamRecordService.invigilatePageListVideo(new Page<>(pageNumber, pageSize), examId, examActivityId, roomCode, paperDownload, status, name, identity, minWarningCount, maxWarningCount, clientWebsocketStatus, monitorStatusSource, userId));
    }

    @ApiOperation(value = "实时监控台视频随机列表接口")
    @RequestMapping(value = "/list/video/random", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListVideoBean.class)})
    public Result listVideoRandom(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId, @ApiParam(value = "随机数", required = true) @RequestParam Integer randomNum) {
        if (Objects.isNull(randomNum) || Objects.equals(randomNum, "")) {
            throw new BusinessException("随机数不能为空");
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        return ResultUtil.ok(tOeExamRecordService.invigilatePageListVideoRandom(examId, userId, randomNum));
    }

    @ApiOperation(value = "监考明细管理列表接口")
    @RequestMapping(value = "/history/list", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考明细信息", response = InvigilateListHistoryBean.class)})
    public Result historyList(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
                              @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
                              @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                              @ApiParam(value = "科目代码", required = false) @RequestParam(required = false) String courseCode,
                              @ApiParam(value = "考生状态", required = false) @RequestParam(required = false) String status,
                              @ApiParam(value = "违纪状态", required = false) @RequestParam(required = false) Integer breachStatus,
                              @ApiParam(value = "交卷方式", required = false) @RequestParam(required = false) String finishType,
                              @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
                              @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity,
                              @ApiParam(value = "陌生人脸min", required = false) @RequestParam(required = false) Integer minMultipleFaceCount,
                              @ApiParam(value = "陌生人脸max", required = false) @RequestParam(required = false) Integer maxMultipleFaceCount,
                              @ApiParam(value = "异常处理min", required = false) @RequestParam(required = false) Integer minExceptionCount,
                              @ApiParam(value = "异常处理max", required = false) @RequestParam(required = false) Integer maxExceptionCount,
                              @ApiParam(value = "预警量min", required = false) @RequestParam(required = false) Integer minWarningCount,
                              @ApiParam(value = "预警量max", required = false) @RequestParam(required = false) Integer maxWarningCount,
                              @ApiParam(value = "客户端网络通信状态", required = false) @RequestParam(required = false) String clientWebsocketStatus,
                              @ApiParam(value = "监控设备通信状态", required = false) @RequestParam(required = false) String monitorStatusSource,
                              @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
                              @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        return ResultUtil.ok(tOeExamRecordService.invigilatePageListHistory(new Page<>(pageNumber, pageSize), examId, examActivityId, roomCode, courseCode, status, breachStatus, finishType, name, identity, minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount, maxExceptionCount, minWarningCount, maxWarningCount, clientWebsocketStatus, monitorStatusSource, userId));
    }

    @ApiOperation(value = "实时监控台列表明细接口")
    @RequestMapping(value = "/list/detail", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListDetailBean.class)})
    public Result listDetail(@ApiParam(value = "考试记录id") @RequestParam Long examRecordId) {
        if (Objects.isNull(examRecordId) || Objects.equals(examRecordId, "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        //获取考试记录缓存
        Long examId = ExamRecordCacheUtil.getExamId(examRecordId);
        TOeExamRecord tOeExamRecord = null;
        Long examStudentId = null;
        Long examActivityId = null;
        if (Objects.isNull(examId)) {
            tOeExamRecord = tOeExamRecordService.getById(examRecordId);
            examId = tOeExamRecord.getExamId();
            examStudentId = tOeExamRecord.getExamStudentId();
            examActivityId = tOeExamRecord.getExamActivityId();
        } else {
            examStudentId = ExamRecordCacheUtil.getExamStudentId(examRecordId);
            examActivityId = ExamRecordCacheUtil.getExamActivityId(examRecordId);
        }
        ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(examRecordId);
        Integer breachStatus = Objects.isNull(ExamRecordCacheUtil.getBreachStatus(examRecordId)) ? 1 : ExamRecordCacheUtil.getBreachStatus(examRecordId);
        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
        TEStudent teStudent = teStudentService.getById(examStudentCacheBean.getStudentId());
        String basePhotoPath = systemConfig.getProperty("aliyun.oss.url") + File.separator + teStudent.getBasePhotoPath();
        String identity = examStudentCacheBean.getIdentity();
        String examStudentName = examStudentCacheBean.getName();
        String courseNameCode = examStudentCacheBean.getCourseName() + "(" + examStudentCacheBean.getCourseCode() + ")";
        String roomCode = examStudentCacheBean.getRoomCode();
        String roomName = examStudentCacheBean.getRoomName();
        ExamActivityCacheBean examActivityCacheBean = teExamActivityService.getExamActivityCacheBean(examActivityId);
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        InvigilateListDetailBean invigilateListDetailBean = new InvigilateListDetailBean(examCacheBean.getName(), examActivityCacheBean.getCode(), examId, examActivityId, examStudentId, examRecordId, identity, examStudentName, courseNameCode, status, roomCode, roomName, breachStatus, basePhotoPath);

        //考生轨迹
        QueryWrapper<TEExamStudentLog> teExamStudentLogQueryWrapper = new QueryWrapper<>();
        teExamStudentLogQueryWrapper.lambda().eq(TEExamStudentLog::getExamRecordId, examRecordId)
                .eq(TEExamStudentLog::getExamStudentId, examStudentId);
        List<TEExamStudentLog> teExamStudentLogList = teExamStudentLogService.list(teExamStudentLogQueryWrapper);
        invigilateListDetailBean.setExamStudentLogList(teExamStudentLogList);

        //预警、异常、人脸
        //预警
        QueryWrapper<TIeInvigilateWarnInfo> tIeInvigilateWarnInfoQueryWrapper = new QueryWrapper<>();
        tIeInvigilateWarnInfoQueryWrapper.lambda().eq(TIeInvigilateWarnInfo::getExamRecordId, examRecordId);
        int warningCount = tIeInvigilateWarnInfoService.count(tIeInvigilateWarnInfoQueryWrapper);
        invigilateListDetailBean.setWarningCount(warningCount);

        //异常
        QueryWrapper<TIeInvigilateExceptionInfo> tIeInvigilateExceptionInfoQueryWrapper = new QueryWrapper<>();
        tIeInvigilateExceptionInfoQueryWrapper.lambda().eq(TIeInvigilateExceptionInfo::getExamRecordId, examRecordId);
        int exceptionCount = tIeInvigilateExceptionInfoService.count(tIeInvigilateExceptionInfoQueryWrapper);
        invigilateListDetailBean.setExceptionCount(exceptionCount);

        //陌生人脸
        tIeInvigilateWarnInfoQueryWrapper = new QueryWrapper<>();
        tIeInvigilateWarnInfoQueryWrapper.lambda().eq(TIeInvigilateWarnInfo::getExamRecordId, examRecordId)
                .eq(TIeInvigilateWarnInfo::getType, VerifyExceptionEnum.FACE_COUNT_ERROR)
                .eq(TIeInvigilateWarnInfo::getLevel, WarningLevelEnum.D8.name());
        int multipleFaceCount = tIeInvigilateWarnInfoService.count(tIeInvigilateWarnInfoQueryWrapper);
        invigilateListDetailBean.setMultipleFaceCount(multipleFaceCount);

        //清除预警未阅状态
        UpdateWrapper<TIeInvigilateWarnInfo> tIeExamInvigilateNoticeUpdateWrapper = new UpdateWrapper<>();
        tIeExamInvigilateNoticeUpdateWrapper.lambda().set(TIeInvigilateWarnInfo::getApproveStatus, 1)
                .eq(TIeInvigilateWarnInfo::getExamRecordId, examRecordId);
        tIeInvigilateWarnInfoService.update(tIeExamInvigilateNoticeUpdateWrapper);
        return ResultUtil.ok(invigilateListDetailBean);
    }

    @ApiOperation(value = "强制/手动交卷接口")
    @RequestMapping(value = "/finish", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    public Result finish(@ApiJsonObject(name = "invigilateFinish", value = {
            @ApiJsonProperty(key = "examRecordId", type = "long", example = "1", description = "考试记录id"),
            @ApiJsonProperty(key = "type", description = "收卷类型，手动/强制")
    }) @ApiParam(value = "考试记录信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("examRecordId")) || Objects.equals(mapParameter.get("examRecordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        List<String> recordIdList = (List<String>) mapParameter.get("examRecordId");
        if (Objects.isNull(mapParameter.get("type")) || Objects.equals(mapParameter.get("type"), "")) {
            throw new BusinessException(ExceptionResultEnum.FINISH_TYPE_IS_NULL);
        }
        if (recordIdList.size() > 0) {
            FinishTypeEnum type = FinishTypeEnum.valueOf(String.valueOf(mapParameter.get("type")));
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            //发送mq给客户端强制收卷start
            MqTagEnum mqTagEnum = null;
            if (Objects.equals(type, FinishTypeEnum.INTERRUPT)) {
                mqTagEnum = MqTagEnum.OE_MONITOR_FINISH;
            } else if (Objects.equals(type, FinishTypeEnum.BREACH)) {
                mqTagEnum = MqTagEnum.OE_WARNING_FINISH;
            } else {
                mqTagEnum = MqTagEnum.OE_HARD_FINISH;
            }
            MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), mqTagEnum.name(), JacksonUtil.parseJson(recordIdList), mqTagEnum, String.valueOf(tbUser.getId()), mapParameter, tbUser.getName());
            mqDtoService.assembleSendOneWayMsg(mqDto);
            //发送mq给客户端强制收卷end
        }
        return ResultUtil.ok(Collections.singletonMap("success", true));
    }

    @ApiOperation(value = "在线巡考列表接口")
    @RequestMapping(value = "/patrol/list", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = InvigilateListPatrolBean.class)})
    public Result patrolList(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
                             @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
                             @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                             @ApiParam(value = "考生状态", required = false) @RequestParam(required = false) String status,
                             @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
                             @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity,
                             @ApiParam(value = "陌生人脸min", required = false) @RequestParam(required = false) Integer minMultipleFaceCount,
                             @ApiParam(value = "陌生人脸max", required = false) @RequestParam(required = false) Integer maxMultipleFaceCount,
                             @ApiParam(value = "异常处理min", required = false) @RequestParam(required = false) Integer minExceptionCount,
                             @ApiParam(value = "异常处理max", required = false) @RequestParam(required = false) Integer maxExceptionCount,
                             @ApiParam(value = "预警量min", required = false) @RequestParam(required = false) Integer minWarningCount,
                             @ApiParam(value = "预警量max", required = false) @RequestParam(required = false) Integer maxWarningCount,
                             @ApiParam(value = "客户端网络通信状态", required = false) @RequestParam(required = false) String clientWebsocketStatus,
                             @ApiParam(value = "监控设备通信状态", required = false) @RequestParam(required = false) String monitorStatusSource,
                             @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
                             @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        return ResultUtil.ok(tOeExamRecordService.invigilatePagePatrolList(new Page<>(pageNumber, pageSize), examId, examActivityId, roomCode, status, name, identity, minMultipleFaceCount, maxMultipleFaceCount, minExceptionCount, maxExceptionCount, minWarningCount, maxWarningCount, clientWebsocketStatus, monitorStatusSource, userId));
    }

    @ApiOperation(value = "进度查询列表接口")
    @RequestMapping(value = "/progress/list", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考进度信息", response = InvigilateListProgressBean.class)})
    public Result progressList(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
                               @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
                               @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                               @ApiParam(value = "科目代码", required = false) @RequestParam(required = false) String courseCode,
                               @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
                               @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity,
                               @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
                               @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        return ResultUtil.ok(tOeExamRecordService.invigilatePageProgressList(new Page<>(pageNumber, pageSize), examId, examActivityId, roomCode, courseCode, name, identity, userId));
    }

    @ApiOperation(value = "进度查询列表导出接口")
    @RequestMapping(value = "/progress/list/export", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考进度信息", response = InvigilateListProgressBean.class)})
    public void progressListExport(@ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long examId,
                                   @ApiParam(value = "考试场次id", required = false) @RequestParam(required = false) Long examActivityId,
                                   @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                                   @ApiParam(value = "科目代码", required = false) @RequestParam(required = false) String courseCode,
                                   @ApiParam(value = "姓名", required = false) @RequestParam(required = false) String name,
                                   @ApiParam(value = "证件号", required = false) @RequestParam(required = false) String identity) throws Exception {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = (AuthDto) redisUtil.get(SystemConstant.userOauth + "::" + tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        List<InvigilateListProgressExcelBean> invigilateListProgressExcelBeanList = tOeExamRecordService.invigilatePageProgressListExport(examId, examActivityId, roomCode, courseCode, name, identity, userId);
        ExportUtils.exportEXCEL("进度信息", InvigilateListProgressExcelBean.class, invigilateListProgressExcelBeanList, ServletUtil.getResponse());
    }

    @ApiOperation(value = "违纪处理接口")
    @RequestMapping(value = "/breach", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    public Result breach(@ApiJsonObject(name = "invigilateBreach", value = {
            @ApiJsonProperty(key = "examRecordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "type", description = "违规类型", required = true),
            @ApiJsonProperty(key = "description", description = "描述", required = true),
            @ApiJsonProperty(key = "status", type = "int", example = "1", description = "新建/撤销", required = true)
    }) @ApiParam(value = "考试记录信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("examRecordId")) || Objects.equals(mapParameter.get("examRecordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        List<String> recordIdList = (List<String>) mapParameter.get("examRecordId");
        if (Objects.isNull(mapParameter.get("type")) || Objects.equals(mapParameter.get("type"), "")) {
            throw new BusinessException(ExceptionResultEnum.BREACH_TYPE_IS_NULL);
        }
        if (Objects.isNull(mapParameter.get("description")) || Objects.equals(mapParameter.get("description"), "")) {
            throw new BusinessException(ExceptionResultEnum.BREACH_DESC_IS_NULL);
        }
        String description = String.valueOf(mapParameter.get("description"));
        if (Objects.isNull(mapParameter.get("status")) || Objects.equals(mapParameter.get("status"), "")) {
            throw new BusinessException(ExceptionResultEnum.BREACH_STATUS_IS_NULL);
        }
        if (recordIdList.size() > 0) {
            Integer status = Integer.parseInt(String.valueOf(mapParameter.get("status")));
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            List<TEExamBreachLog> teExamBreachLogList = new ArrayList<>();
            if (status == 0) {//新建违纪
                BreachTypeEnum type = BreachTypeEnum.valueOf(String.valueOf(mapParameter.get("type")));
                List<TEExamBreachLog> finalTeExamBreachLogList = teExamBreachLogList;
                for (int i = 0; i < recordIdList.size(); i++) {
                    Long l = Long.parseLong(String.valueOf(recordIdList.get(i)));
                    Long examId = ExamRecordCacheUtil.getExamId(l);
                    Long examStudentId = null;
                    Long examActivityId = null;
                    if (Objects.isNull(examId)) {
                        TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(l);
                        examId = tOeExamRecord.getExamId();
                        examStudentId = tOeExamRecord.getExamStudentId();
                        examActivityId = tOeExamRecord.getExamActivityId();
                    } else {
                        examId = ExamRecordCacheUtil.getExamId(l);
                        examStudentId = ExamRecordCacheUtil.getExamStudentId(l);
                        examActivityId = ExamRecordCacheUtil.getExamActivityId(l);
                    }
                    TEExamBreachLog teExamBreachLog = new TEExamBreachLog(examId, examActivityId, l, examStudentId, type.name(), description, status);
                    teExamBreachLog.setCreateId(tbUser.getId());
                    finalTeExamBreachLogList.add(teExamBreachLog);
                    ExamRecordCacheUtil.setBreachStatus(l, 0, true);
                }
            } else {//撤销违纪
                BreachCancelTypeEnum type = BreachCancelTypeEnum.valueOf(String.valueOf(mapParameter.get("type")));
                QueryWrapper<TEExamBreachLog> teExamBreachLogQueryWrapper = new QueryWrapper<>();
                teExamBreachLogQueryWrapper.lambda().in(TEExamBreachLog::getExamRecordId, recordIdList);
                teExamBreachLogList = teExamBreachLogService.list(teExamBreachLogQueryWrapper);
                teExamBreachLogList.forEach(s -> {
                    s.setType(type.name());
                    s.setDescription(description);
                    s.setStatus(status);
                    s.setUpdateId(tbUser.getId());
                    ExamRecordCacheUtil.setBreachStatus(s.getExamRecordId(), 1, true);
                });
            }
            for(TEExamBreachLog eb:teExamBreachLogList){
            	teExamBreachLogService.saveOrUpdate(eb);
            }
        }
        return ResultUtil.ok(Collections.singletonMap("success", true));
    }

    @ApiOperation(value = "监考消息通知接口")
    @RequestMapping(value = "/notice", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    public Result notice(@ApiJsonObject(name = "invigilateNotice", value = {
            @ApiJsonProperty(key = "examRecordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "type", description = "消息类型，text/audio", required = true),
            @ApiJsonProperty(key = "content", description = "消息内容", required = true)
    }) @ApiParam(value = "考试记录信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("examRecordId")) || Objects.equals(mapParameter.get("examRecordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get("examRecordId")));
        if (Objects.isNull(mapParameter.get("type")) || Objects.equals(mapParameter.get("type"), "")) {
            throw new BusinessException(ExceptionResultEnum.MESSAGE_TYPE_IS_NULL);
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        //发送mq给客户端监考消息start
        MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.OE_IM_CLUSTERING.name(), recordId, MqTagEnum.OE_IM_CLUSTERING, String.valueOf(tbUser.getId()), mapParameter, tbUser.getName());
        mqDtoService.assembleSendOneWayMsg(mqDto);
        //发送mq给客户端监考消息end

        //发送mq给客户端监考强制活体验证start
        mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.OE_LIVENESS_VERIFY.name(), recordId, MqTagEnum.OE_LIVENESS_VERIFY, String.valueOf(tbUser.getId()), mapParameter, tbUser.getName());
        mqDtoService.assembleSendOneWayMsg(mqDto);
        //发送mq给客户端监考强制活体验证end
        return ResultUtil.ok(Collections.singletonMap("success", true));
    }

    @ApiOperation(value = "结束监考接口")
    @RequestMapping(value = "/exam/finish", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    public Result examFinish(@ApiParam(value = "考试id", required = true) @RequestParam Long examId) {
        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        long now = System.currentTimeMillis();
        if (now < examCacheBean.getEndTime()) {
            throw new BusinessException("未到考试批次结束时间");
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
        if (Objects.isNull(tbSession)) {
            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
        }
        QueryWrapper<TBExamInvigilateUser> examInvigilateUserQueryWrapper = new QueryWrapper<>();
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        examInvigilateUserQueryWrapper.lambda().eq(TBExamInvigilateUser::getOrgId, tbUser.getOrgId())
                .eq(TBExamInvigilateUser::getUserId, tbUser.getId());
        List<TBExamInvigilateUser> tbExamInvigilateUserList = tbExamInvigilateUserService.list(examInvigilateUserQueryWrapper);
        Set<String> roomCodeSet = null;
        if (Objects.nonNull(tbExamInvigilateUserList) && tbExamInvigilateUserList.size() > 0) {
            roomCodeSet = tbExamInvigilateUserList.stream().map(s -> s.getRoomCode()).collect(Collectors.toSet());
        }
        //根据roomCode获取当前老师所要监考的全部应考学生数
        QueryWrapper<TEExamStudent> teExamStudentQueryWrapper = new QueryWrapper<>();
        teExamStudentQueryWrapper.lambda().eq(TEExamStudent::getExamId, examId)
                .in(TEExamStudent::getRoomCode, roomCodeSet);
        List<TEExamStudent> teExamStudentList = teExamStudentService.list(teExamStudentQueryWrapper);
        Set<Long> examStudentIdSet = null;
        Set<Long> examActivityIdSet = null;
        AtomicReference<Integer> prepareCount = new AtomicReference<>(0); //已待考
        AtomicReference<Integer> examCount = new AtomicReference<>(0);//考试中
        if (Objects.nonNull(teExamStudentList) && teExamStudentList.size() > 0) {
            examStudentIdSet = new HashSet<>();
            examActivityIdSet = new HashSet<>();
            Set<Long> finalExamStudentIdSet = examStudentIdSet;
            Set<Long> finalExamActivityIdSet = examActivityIdSet;
            teExamStudentList.forEach(s -> {
                finalExamStudentIdSet.add(s.getId());
                finalExamActivityIdSet.add(s.getExamActivityId());
            });
        }

        Integer exceptionCount = 0, warningCount = 0, reexamCount = 0;//异常人数，预警人数，重考人数
        ExamPropCountDto examPropCountDto = new ExamPropCountDto();
        //获取异常人数
        if (Objects.nonNull(examStudentIdSet)) {
            QueryWrapper<TIeInvigilateExceptionInfo> tIeInvigilateExceptionInfoQueryWrapper = new QueryWrapper<>();
            tIeInvigilateExceptionInfoQueryWrapper.lambda().in(TIeInvigilateExceptionInfo::getExamStudentId, examStudentIdSet);
            exceptionCount = tIeInvigilateExceptionInfoService.count(tIeInvigilateExceptionInfoQueryWrapper);

            QueryWrapper<TIeInvigilateWarnInfo> tIeInvigilateWarnInfoQueryWrapper = new QueryWrapper<>();
            tIeInvigilateWarnInfoQueryWrapper.lambda().in(TIeInvigilateWarnInfo::getExamStudentId, examStudentIdSet);
            warningCount = tIeInvigilateWarnInfoService.count(tIeInvigilateWarnInfoQueryWrapper);

            QueryWrapper<TEExamReexam> teExamReexamQueryWrapper = new QueryWrapper<>();
            teExamReexamQueryWrapper.lambda().in(TEExamReexam::getExamStudentId, examStudentIdSet).eq(TEExamReexam::getStatus, 1);
            reexamCount = teExamReexamService.count(teExamReexamQueryWrapper);

            examPropCountDto.setExceptionCount(exceptionCount);
            examPropCountDto.setWarningCount(warningCount);
            examPropCountDto.setReexamCount(reexamCount);
        }
        if (Objects.nonNull(examActivityIdSet)) {
            //获取已待考、考试中学生
            examActivityIdSet.forEach(s -> {
                ExamActivityCacheBean examActivityCacheBean = teExamActivityService.getExamActivityCacheBean(s);
                Long startTime = examActivityCacheBean.getStartTime();
                Long finishTime = examActivityCacheBean.getFinishTime();
                Integer activityMaxDurationSeconds = Objects.nonNull(examActivityCacheBean.getMaxDurationSeconds()) ? examActivityCacheBean.getMaxDurationSeconds() : null;
                Integer maxDurationSeconds = Objects.nonNull(examCacheBean.getMaxDurationSeconds()) ? examCacheBean.getMaxDurationSeconds() : null;
                Integer finalMaxDurationSeconds = Objects.nonNull(activityMaxDurationSeconds) ? activityMaxDurationSeconds : maxDurationSeconds;
                Calendar calendar = Calendar.getInstance();
                if (Objects.nonNull(finalMaxDurationSeconds)) {
                    calendar.setTimeInMillis(startTime);
                    calendar.add(Calendar.SECOND, finalMaxDurationSeconds.intValue());
                }
//                Date now = new Date();
//                if (now.getTime() >= calendar.getTime().getTime() && now.getTime() <= finishTime.getTime()) {
                examPropCountDto.setMaxDurationSeconds(calendar.getTime().getTime());
                if (Objects.nonNull(examCacheBean.getForceFinish()) && examCacheBean.getForceFinish() == 1) {
                    examPropCountDto.setFinishTime(finishTime);
                }
                Map<String, Object> objectMap = redisUtil.getHashEntries(RedisKeyHelper.examActivityRecordCacheKey(s));
                if (Objects.nonNull(objectMap) && objectMap.size() > 0) {
                    objectMap.forEach((k, v) -> {
                        ExamActivityRecordCacheBean examActivityRecordCache = (ExamActivityRecordCacheBean) v;
                        ExamRecordStatusEnum examRecordStatusEnum = examActivityRecordCache.getStatus();
                        //已待考
                        if (Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.FIRST_PREPARE)) {
                            prepareCount.getAndSet(prepareCount.get() + 1);
                        }
                        //考试中
                        else if (Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.ANSWERING) || Objects.equals(examRecordStatusEnum, ExamRecordStatusEnum.RESUME_PREPARE)) {
                            examCount.getAndSet(examCount.get() + 1);
                        }
                    });
                }
//                }
            });
        }
        teExamService.updateInvigilateMonitorStatus(InvigilateMonitorStatusEnum.FINISHED, examId);
        teExamService.deleteExamCacheBean(examId);
        examPropCountDto.setPrepareCount(prepareCount.get());
        examPropCountDto.setExamCount(examCount.get());
        return ResultUtil.ok(examPropCountDto);
    }
}
