package com.qmth.themis.admin.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.annotation.Logs;
import com.qmth.themis.business.cache.ExamingDataCacheUtil;
import com.qmth.themis.business.cache.RedisKeyHelper;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
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.dto.cache.TEStudentCacheDto;
import com.qmth.themis.business.dto.request.TEExamDto;
import com.qmth.themis.business.entity.*;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.*;
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.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 考试批次 前端控制器
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/6/25
 */
@Api(tags = "考试批次Controller")
@RestController
@RequestMapping(SystemConstant.PREFIX_URL_ADMIN + "/exam")
@Validated
public class TEExamController {
    private final static Logger log = LoggerFactory.getLogger(TEExamController.class);

    @Resource
    TEExamService teExamService;

    @Resource
    TEExamActivityService teExamActivityService;

    @Resource
    MqDtoService mqDtoService;

    @Resource
    TBExamInvigilateUserService tbExamInvigilateUserService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    UidUtil uidUtil;

    @Resource
    MqUtil mqUtil;

    @Resource
    TBTaskHistoryService taskHistoryService;

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    private TEExamPaperService examPaperService;

    @Resource
    ThemisCacheService themisCacheService;

    @Resource
    TEExamCourseService teExamCourseService;

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    CommonService commonService;

    @Resource
    TEExamSummaryService teExamSummaryService;

    @ApiOperation(value = "考试批次修改/新增接口")
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    @Transactional
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Logs(value = LogEnum.EXAM)
    public Result save(@ApiParam(value = "考试批次信息", required = true) @RequestBody TEExamDto teExamDto) {
        if (Objects.isNull(teExamDto) || Objects.equals(teExamDto, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_INFO_IS_NULL);
        }
        if (Objects.isNull(teExamDto.getMode()) || Objects.equals(teExamDto.getMode(), "")) {
            throw new BusinessException("考试模式不能为空");
        }
        if (Objects.isNull(teExamDto.getName()) || Objects.equals(teExamDto.getName(), "")) {
            throw new BusinessException("批次名称不能为空");
        }
        if (Objects.isNull(teExamDto.getCode()) || Objects.equals(teExamDto.getCode(), "")) {
            throw new BusinessException("批次编码不能为空");
        }
        TEExam teExam = null;
        Long oldId = null;
        Integer size = 0;
        try {
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            TBOrg tbOrg = (TBOrg) ServletUtil.getRequestOrg();
            if (Objects.nonNull(tbOrg) && Objects.isNull(tbOrg.getId())) {
                throw new BusinessException("考试的机构id不允许为空");
            }
            teExamDto.setOrgId(tbOrg.getId());
            oldId = teExamDto.getId();
            TEExam oldTeExam = null;
            if (Objects.nonNull(oldId)) {
                teExamDto.setUpdateId(tbUser.getId());
                ExamCacheBean examCacheBean = teExamService.getExamCacheBean(oldId);
                oldTeExam = teExamService.cacheConvert(examCacheBean);
                if (Objects.equals(oldTeExam.getMonitorStatus(), InvigilateMonitorStatusEnum.FINISHED)) {
                    throw new BusinessException("监考结束的考试批次不可以修改");
                }
                boolean recordUpdate = true;
                if ((Objects.nonNull(oldTeExam.getRecordSelectStrategy()) && Objects.nonNull(teExamDto.getRecordSelectStrategy()) && !Objects.equals(oldTeExam.getRecordSelectStrategy(), teExamDto.getRecordSelectStrategy()))) {
                    recordUpdate = false;
                }
                boolean objectiveUpdate = true;
                if ((Objects.nonNull(oldTeExam.getObjectiveScorePolicy()) && Objects.nonNull(teExamDto.getObjectiveScorePolicy()) && !Objects.equals(oldTeExam.getObjectiveScorePolicy(), teExamDto.getObjectiveScorePolicy()))) {
                    objectiveUpdate = false;
                }
                QueryWrapper<TOeExamRecord> tOeExamRecordQueryWrapper = new QueryWrapper<>();
                tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamId, oldId);
                int count = tOeExamRecordService.count(tOeExamRecordQueryWrapper);
                if (count > 0) {
                    if (!recordUpdate) {
                        throw new BusinessException("已有考试记录，取分策略不允许修改");
                    }
                    if (!objectiveUpdate) {
                        throw new BusinessException("已有考试记录，客观题判分策略不允许修改");
                    }
                }
            } else {
                size++;
                teExamDto.setId(uidUtil.getId());
                teExamDto.setCreateId(tbUser.getId());
            }
            teExamDto.setMonitorStatus(Objects.nonNull(oldTeExam) ? oldTeExam.getMonitorStatus() : InvigilateMonitorStatusEnum.NOT_START);
            teExam = new TEExam(teExamDto);
            if (Objects.nonNull(teExam.getEndTime()) && teExam.getEndTime().longValue() < System.currentTimeMillis()) {
                throw new BusinessException("修改的批次结束时间只能大于当前时间");
            }
            if (Objects.nonNull(oldTeExam) && Objects.nonNull(teExam)
                    && oldTeExam.getForceFinish().intValue() != teExam.getForceFinish().intValue()
                    && teExam.getMode() == oldTeExam.getMode()) {
                QueryWrapper<TEExamActivity> teExamActivityQueryWrapper = new QueryWrapper<>();
                teExamActivityQueryWrapper.lambda().eq(TEExamActivity::getExamId, oldId);
                List<TEExamActivity> teExamActivityList = teExamActivityService.list(teExamActivityQueryWrapper);
                if (!CollectionUtils.isEmpty(teExamActivityList)) {
                    if (teExam.getForceFinish().intValue() == 1) {//从非强制交卷改成强制交卷，这时需生成场次定时任务
                        //新增quartz任务,发送mq消息start
                        Map<String, Object> prop = new HashMap<>();
                        prop.put(SystemConstant.OPER, SystemConstant.INSERT);
                        prop.put(SystemConstant.EXAM, teExam);
                        MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.EXAM_ACTIVITY.name(),
                                JacksonUtil.parseJson(teExamActivityList), MqTagEnum.EXAM_ACTIVITY,
                                String.valueOf(teExam.getId()), prop, tbUser.getName());
                        mqDtoService.assembleSendAsyncOrderMsg(mqDto);
                        //新增quartz任务,发送mq消息end
                    } else {//否则删除场次定时任务
                        //删除quartz任务,发送mq消息start
                        Map<String, Object> prop = new HashMap<>();
                        prop.put(SystemConstant.OPER, SystemConstant.DELETE);
                        prop.put(SystemConstant.EXAM, teExam);
                        MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.EXAM_ACTIVITY.name(),
                                JacksonUtil.parseJson(teExamActivityList), MqTagEnum.EXAM_ACTIVITY,
                                String.valueOf(teExam.getId()), prop, tbUser.getName());
                        mqDtoService.assembleSendAsyncOrderMsg(mqDto);
                        //删除quartz任务,发送mq消息end
                    }
                }
            }
            List<String> monitorRecordList = teExamDto.getMonitorRecord();
            if (Objects.nonNull(monitorRecordList) && monitorRecordList.size() > 1) {
                if (monitorRecordList.contains(MonitorVideoSourceEnum.CLIENT_CAMERA) && monitorRecordList.contains(MonitorVideoSourceEnum.CLIENT_SCREEN)) {
                    throw new BusinessException("客户端摄像头和屏幕只能选一个");
                }
            }
            teExam.setScanTime(null);
            teExamService.saveOrUpdate(teExam);
            //只有集中统一模式，并且强制交卷才会生成场次定时任务。否则等待每天凌晨的定时任务交卷
            //如果是随到随考，则默认插入一条场次信息
            if (teExam.getMode() == ExamModeEnum.ANYTIME) {//随到随考模式
                QueryWrapper<TEExamActivity> teExamActivityQueryWrapper = new QueryWrapper<>();
                teExamActivityQueryWrapper.lambda().eq(TEExamActivity::getExamId, teExam.getId());
                TEExamActivity teExamActivity = teExamActivityService.getOne(teExamActivityQueryWrapper);
                if (Objects.isNull(teExamActivity)) {
                    teExamActivity = new TEExamActivity(teExam.getId(), teExam.getPrepareSeconds(),
                            teExam.getMaxDurationSeconds(),
                            (int) ((teExam.getEndTime() - teExam.getStartTime()) / 1000), teExam.getStartTime(),
                            teExam.getEndTime(), tbUser.getId());
                    teExamActivity.setCode(String.valueOf(redisUtil.getRedisActivityCodeSequence(teExam.getId())));
                    teExamActivity.setEnable(1);
                } else {
                    teExamActivity.setPrepareSeconds(teExam.getPrepareSeconds());
                    teExamActivity.setMaxDurationSeconds(teExam.getMaxDurationSeconds());
                    teExamActivity.setOpeningSeconds((int) ((teExam.getEndTime() - teExam.getStartTime()) / 1000));
                    teExamActivity.setStartTime(teExam.getStartTime());
                    teExamActivity.setFinishTime(teExam.getEndTime());
                    teExamActivity.setUpdateId(tbUser.getId());
                }
                teExamActivityService.saveOrUpdate(teExamActivity);
                teExamActivityService.updateExamActivityCacheBean(teExamActivity.getId());
            }
            //加入更新考试列表缓存方法
            List<TEExamActivity> teExamActivityList = teExamActivityService.list(new QueryWrapper<TEExamActivity>().lambda().eq(TEExamActivity::getExamId, teExam.getId()).eq(TEExamActivity::getEnable, 1));
            if (!CollectionUtils.isEmpty(teExamActivityList)) {
                for (TEExamActivity t : teExamActivityList) {
                    if (Objects.nonNull(t) && Objects.nonNull(t.getEnable()) && t.getEnable().intValue() == 1 && Objects.nonNull(teExam.getEnable()) && teExam.getEnable().intValue() == 1 && (Objects.nonNull(teExam.getEndTime()) && teExam.getEndTime().longValue() > System.currentTimeMillis())) {
                        themisCacheService.updateCurrentExamListCache(t.getExamId().toString(), t.getId());
                        themisCacheService.updateOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
                    } else {
                        themisCacheService.removeCurrentExamListCache(t.getExamId().toString(), t.getId());
                        themisCacheService.removeOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
                    }
                }
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (Objects.nonNull(teExam) && redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_PREFIX + teExam.getId(), SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_TIME_OUT)) {
                try {
                    Integer value = redisUtil.getRedisActivityCodeSequenceValue(teExam.getId());
                    redisUtil.setRedisActivityCodeSequence(teExam.getId(), value - size);
                } finally {
                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_PREFIX + teExam.getId());
                }
            }
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length())
                        .replaceAll("'", "");
                throw new BusinessException(
                        "批次名称[" + teExam.getName() + "]下的" + FieldUniqueEnum.convertToCode(columnStr) + "不能重复创建");
            } else if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        teExamService.updateExamCacheBean(teExam.getId());
        examPaperService.disposeObjectiveAnswer(teExam.getId());
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "考试批次统计接口")
    @RequestMapping(value = "/count", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"count\":1}", response = Result.class)})
    public Result count(@ApiParam(value = "考试批次编码", required = false) @RequestParam(required = false) String code,
                        @ApiParam(value = "考试批次名称", required = false) @RequestParam(required = false) String name,
                        @ApiParam(value = "考试批次模式", required = false) @RequestParam(required = false) ExamModeEnum mode) {
        QueryWrapper<TEExam> teExamQueryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(code)) {
            teExamQueryWrapper.lambda().eq(TEExam::getCode, code);
        }
        if (Objects.nonNull(name)) {
            teExamQueryWrapper.lambda().eq(TEExam::getName, name);
        }
        if (Objects.nonNull(mode)) {
            teExamQueryWrapper.lambda().eq(TEExam::getMode, mode);
        }
        int count = teExamService.count(teExamQueryWrapper);
        return ResultUtil.ok(Collections.singletonMap(SystemConstant.COUNT, count));
    }

    @ApiOperation(value = "考试批次查询接口")
    @RequestMapping(value = "/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试批次信息", response = TEExam.class)})
    public Result query(@ApiParam(value = "用户id", required = false) @RequestParam(required = false) Long userId,
                        @ApiParam(value = "考试批次id", required = false) @RequestParam(required = false) Long id,
                        @ApiParam(value = "考试批次编码", required = false) @RequestParam(required = false) String code,
                        @ApiParam(value = "考试批次名称", required = false) @RequestParam(required = false) String name,
                        @ApiParam(value = "考试批次模式", required = false) @RequestParam(required = false) String mode,
                        @ApiParam(value = "是否启用", required = false) @RequestParam(required = false) Integer enable,
                        @ApiParam(value = "类型(区分实时监考台和考务)", required = false) @RequestParam(required = false) String type,
                        @ApiParam(value = "查询是否结束监考", required = false) @RequestParam(required = false) Boolean finished,
                        @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) int pageNumber,
                        @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.SELECT_PAGE_SIZE_MAX) int pageSize) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INSPECTION.name())) {
            userId = null;
        }
        return ResultUtil.ok(teExamService.examQuery(new Page<>(pageNumber, pageSize), userId, id, code, name, mode, enable, tbUser.getOrgId(), type, finished));
    }

    @ApiOperation(value = "根据考试批次编码查询考场接口")
    @RequestMapping(value = "/examroom/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试批次信息", response = TEExam.class)})
    public Result examroomQuery(@ApiParam(value = "考试批次编码", required = true) @RequestParam String code) {
        return ResultUtil.ok(teExamService.examroomQuery(code));
    }

    @ApiOperation(value = "考试批次停用/启用接口")
    @RequestMapping(value = "/toggle", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result examToggle(@ApiJsonObject(name = "examToggle", value = {
            @ApiJsonProperty(key = "id", type = "long", example = "1", description = "考试批次ID"),
            @ApiJsonProperty(key = "enable", type = "int", example = "1", description = "是否启用")}) @ApiParam(value = "考试批次信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get(SystemConstant.ID)) || Objects.equals(mapParameter.get(SystemConstant.ID), "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        Long examId = Long.parseLong(String.valueOf(mapParameter.get(SystemConstant.ID)));
        if (Objects.isNull(mapParameter.get("enable")) || Objects.equals(mapParameter.get("enable"), "")) {
            throw new BusinessException(ExceptionResultEnum.ENABLE_IS_NULL);
        }
        Integer enable = Integer.parseInt(String.valueOf(mapParameter.get("enable")));
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));
        TEExam teExam = teExamService.cacheConvert(examCacheBean);
        if (Objects.nonNull(enable) && enable.intValue() == 0) {
            Integer examCount = tOeExamRecordService.findByExamIdOrExamActivityIdCount(teExam.getId(), null);
            if (Objects.nonNull(examCount) && examCount.intValue() > 0) {
                throw new BusinessException("已有考试记录不能禁用");
            }
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        teExam.setEnable(enable);
        teExam.setUpdateId(tbUser.getId());
        teExamService.updateById(teExam);
        teExamService.updateExamCacheBean(teExam.getId());

        //加入更新考试列表缓存方法
        List<TEExamActivity> teExamActivityList = teExamActivityService.list(new QueryWrapper<TEExamActivity>().lambda().eq(TEExamActivity::getExamId, teExam.getId()).eq(TEExamActivity::getEnable, 1));
        for (TEExamActivity t : teExamActivityList) {
            if (Objects.nonNull(t.getEnable()) && t.getEnable().intValue() == 1 && teExam.getEnable().intValue() == 1 && (Objects.nonNull(teExam.getEndTime()) && teExam.getEndTime().longValue() > System.currentTimeMillis())) {
                themisCacheService.updateCurrentExamListCache(t.getExamId().toString(), t.getId());
                themisCacheService.updateOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
            } else {
                themisCacheService.removeCurrentExamListCache(t.getExamId().toString(), t.getId());
                themisCacheService.removeOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "考试批次详情接口")
    @RequestMapping(value = "/detail", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试批次信息", response = TEExam.class)})
    public Result detail(@ApiParam(value = "考试批次id", required = true) @RequestParam Long id) {
        if (Objects.isNull(id) || Objects.equals(id, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(id);
        Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));
        return ResultUtil.ok(new TEExamDto(teExamService.cacheConvert(examCacheBean)));
    }

    @ApiOperation(value = "考试批次复制接口")
    @RequestMapping(value = "/copy", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result copy(@ApiJsonObject(name = "examCopy", value = {
            @ApiJsonProperty(key = "sourceId", type = "long", example = "1", description = "来源批次ID"),
            @ApiJsonProperty(key = "code", description = "代码"),
            @ApiJsonProperty(key = "name", description = "名称")}) @ApiParam(value = "考试批次信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("sourceId")) || Objects.equals(mapParameter.get("sourceId"), "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        Long examId = Long.parseLong(String.valueOf(mapParameter.get("sourceId")));
        if (Objects.isNull(mapParameter.get("code")) || Objects.equals(mapParameter.get("code"), "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_CODE_IS_NULL);
        }
        String code = String.valueOf(mapParameter.get("code"));
        if (Objects.isNull(mapParameter.get(SystemConstant.NAME)) || Objects.equals(mapParameter.get(SystemConstant.NAME), "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_NAME_IS_NULL);
        }
        String name = String.valueOf(mapParameter.get(SystemConstant.NAME));
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));

        TEExam teExam = teExamService.cacheConvert(examCacheBean);
        List<TEExamActivity> teExamActivityList = null;
        try {
            teExam.setId(uidUtil.getId());
            teExam.setUpdateId(null);
            teExam.setUpdateTime(null);
            teExam.setShortCode(null);
            teExam.setMonitorStatus(null);
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            teExam.setCreateId(tbUser.getId());
            teExam.setCode(code);
            teExam.setName(name);
            teExamService.save(teExam);

            QueryWrapper<TEExamActivity> teExamActivityQueryWrapper = new QueryWrapper<>();
            teExamActivityQueryWrapper.lambda().eq(TEExamActivity::getExamId, examId);
            teExamActivityList = teExamActivityService.list(teExamActivityQueryWrapper);
            if (!CollectionUtils.isEmpty(teExamActivityList)) {
                teExamActivityList.forEach(s -> {
                    s.setId(uidUtil.getId());
                    s.setExamId(teExam.getId());
                    s.setCreateId(teExam.getCreateId());
                    s.setUpdateId(null);
                    s.setUpdateTime(null);
                    s.setCode(String.valueOf(redisUtil.getRedisActivityCodeSequence(teExam.getId())));
                });
                teExamActivityService.saveOrUpdateBatch(teExamActivityList);

                for (TEExamActivity t : teExamActivityList) {
                    if (Objects.nonNull(t) && Objects.nonNull(t.getEnable()) && t.getEnable().intValue() == 1 && Objects.nonNull(teExam.getEnable()) && teExam.getEnable().intValue() == 1 && (Objects.nonNull(teExam.getEndTime()) && teExam.getEndTime().longValue() > System.currentTimeMillis())) {
                        themisCacheService.updateCurrentExamListCache(t.getExamId().toString(), t.getId());
                        themisCacheService.updateOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
                    } else {
                        themisCacheService.removeCurrentExamListCache(t.getExamId().toString(), t.getId());
                        themisCacheService.removeOrgExamListCache(teExam.getOrgId().toString(), t.getExamId().toString(), t.getId());
                    }
                }

                if (Objects.nonNull(teExam.getForceFinish()) && teExam.getForceFinish().intValue() == 1) {
                    //新增quartz任务,发送mq消息start
                    Map<String, Object> prop = new HashMap<>();
                    prop.put(SystemConstant.OPER, SystemConstant.INSERT);
                    prop.put(SystemConstant.EXAM, teExam);
                    MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.EXAM_ACTIVITY.name(),
                            JacksonUtil.parseJson(teExamActivityList), MqTagEnum.EXAM_ACTIVITY,
                            String.valueOf(teExam.getId()), prop, tbUser.getName());
                    mqDtoService.assembleSendAsyncOrderMsg(mqDto);
                    //新增quartz任务,发送mq消息end
                }
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (Objects.nonNull(teExam.getId()) && redisUtil.lock(SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_PREFIX + teExam.getId(), SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_TIME_OUT)) {
                try {
                    Integer value = redisUtil.getRedisActivityCodeSequenceValue(teExam.getId());
                    if (!CollectionUtils.isEmpty(teExamActivityList)) {
                        value = value - teExamActivityList.size();
                    }
                    redisUtil.setRedisActivityCodeSequence(teExam.getId(), value);
                } finally {
                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_EXAM_ACTIVITY_CODE_PREFIX + teExam.getId());
                }
            }
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length())
                        .replaceAll("'", "");
                throw new BusinessException(
                        "机构id[" + teExam.getOrgId() + "]下的" + FieldUniqueEnum.convertToCode(columnStr) + "数据不允许重复插入");
            } else if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "监考端获取考试批次提醒接口")
    @RequestMapping(value = "/list/count", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试批次信息", response = Result.class)})
//    @RedisLimitAnnotation(key = "listCount", period = 1, count = 1)
    public Result listCount(@ApiParam(value = "用户id", required = false) @RequestParam(required = false) Long userId) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        return ResultUtil.ok(teExamService.examList(userId, tbUser.getOrgId()));
    }

    @ApiOperation(value = "考试属性统计接口")
    @RequestMapping(value = "/prop/count", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试属性信息", response = ExamPropCountDto.class)})
    public Result propCount(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                            @ApiParam(value = "考试场次id") @RequestParam(required = false) Long examActivityId,
                            @ApiParam(value = "是否刷新") @RequestParam(required = false) boolean freshen) {
        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
        if (Objects.isNull(tbSession)) {
            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
        }
        //首先查询当前用户所要监控的roomCode
        QueryWrapper<TBExamInvigilateUser> examInvigilateUserQueryWrapper = new QueryWrapper<>();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        examInvigilateUserQueryWrapper.lambda().eq(TBExamInvigilateUser::getOrgId, tbUser.getOrgId())
                .eq(TBExamInvigilateUser::getExamId, examId);
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            examInvigilateUserQueryWrapper.lambda().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());
        }
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name()) && CollectionUtils.isEmpty(roomCodeSet)) {
            throw new BusinessException("当前监考老师未设置任何监考考场");
        }

        List<TEExamSummary> teExamSummaryAllList = new ArrayList<>();
        if (Objects.nonNull(examId) && Objects.nonNull(examActivityId) && !CollectionUtils.isEmpty(roomCodeSet)) {
            for (String s : roomCodeSet) {
                if (freshen && authDto.getRoleCodes().toString().contains(RoleEnum.ADMIN.name())) {
                    teExamSummaryService.examSummary(examId, examActivityId, Objects.nonNull(s) ? new HashSet<>(Arrays.asList(s)) : null, true);
                }
                List<TEExamSummary> teExamSummaryList = themisCacheService.getExamSummaryCache(examId, examActivityId, s);
                if (!CollectionUtils.isEmpty(teExamSummaryList)) {
                    teExamSummaryService.examSummary(examId, examActivityId, Objects.nonNull(s) ? new HashSet<>(Arrays.asList(s)) : null, true);
                    teExamSummaryList = themisCacheService.getExamSummaryCache(examId, examActivityId, s);
                }
                teExamSummaryAllList.addAll(teExamSummaryList);
            }
        } else if (Objects.nonNull(examId) && Objects.nonNull(examActivityId)) {
            if (freshen && authDto.getRoleCodes().toString().contains(RoleEnum.ADMIN.name())) {
                teExamSummaryService.examSummary(examId, examActivityId, null, true);
            }
            List<TEExamSummary> teExamSummaryList = themisCacheService.getExamSummaryCache(examId, examActivityId);
            if (!CollectionUtils.isEmpty(teExamSummaryList)) {
                teExamSummaryService.examSummary(examId, examActivityId, null, true);
                teExamSummaryList = themisCacheService.getExamSummaryCache(examId, examActivityId);
            }
            teExamSummaryAllList.addAll(teExamSummaryList);
        } else {
            if (freshen && authDto.getRoleCodes().toString().contains(RoleEnum.ADMIN.name())) {
                teExamSummaryService.examSummary(examId, null, null, true);
            }
            List<TEExamSummary> teExamSummaryList = themisCacheService.getExamSummaryCache(examId);
            if (!CollectionUtils.isEmpty(teExamSummaryList)) {
                teExamSummaryService.examSummary(examId, null, null, true);
                teExamSummaryList = themisCacheService.getExamSummaryCache(examId);
            }
            teExamSummaryAllList.addAll(teExamSummaryList);
        }
        ExamPropCountDto examPropCountDto = new ExamPropCountDto(examId, 0, 0, 0, 0, 0, BigDecimal.valueOf(0));
        if (!CollectionUtils.isEmpty(teExamSummaryAllList)) {
            Integer allCount = 0, prepareCount = 0, examCount = 0, alreadyComplete = 0, notComplete = 0;
            for (TEExamSummary t : teExamSummaryAllList) {
                allCount = allCount + t.getTotalCount();
                prepareCount = prepareCount + t.getPrepareCount();
                examCount = examCount + t.getExamCount();
                alreadyComplete = alreadyComplete + t.getFinishCount();
                notComplete = notComplete + t.getAbsentCount();
            }
            examPropCountDto.updateSummary(allCount, prepareCount, examCount, alreadyComplete, notComplete);
        }
        return ResultUtil.ok(examPropCountDto);
    }

    @ApiOperation(value = "考试重新算分")
    @RequestMapping(value = "/score/calculate", method = RequestMethod.POST)
    @Transactional
    @ApiResponses({@ApiResponse(code = 200, message = "{\"taskId\":0}", response = Result.class)})
    public Result scoreCalculate(@ApiParam(value = "批次ID", required = true) @RequestParam Long id) {
        //先查询考试相关信息
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(id);
        TEExam teExam = teExamService.cacheConvert(examCacheBean);
        Optional.ofNullable(teExam).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));

        TBTaskHistory tbTaskHistory = null;
        Map<String, Object> transMap = new HashMap<String, Object>();
        try {
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            //往任务表里插一条数据
            tbTaskHistory = new TBTaskHistory(TaskTypeEnum.CALCULATE_EXAM_SCORE, TaskStatusEnum.INIT, "重新算分", 0d,
                    tbUser.getId(), tbUser.getOrgId());
            tbTaskHistory.setEntityId(id);
            tbTaskHistory.setExamId(id);
            taskHistoryService.save(tbTaskHistory);
            transMap.put(SystemConstant.EXAM_ID, String.valueOf(id));
            transMap.put(SystemConstant.ORG_ID, String.valueOf(tbUser.getOrgId()));
            transMap.put(SystemConstant.TASK_ID, String.valueOf(tbTaskHistory.getId()));
            //mq发送消息start
            MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.EXAM_SCORE_CALCULATE.name(), transMap,
                    MqTagEnum.EXAM_SCORE_CALCULATE, String.valueOf(tbTaskHistory.getId()), tbUser.getName());
            mqDtoService.assembleSendAsyncMsg(mqDto);
            //mq发送消息end
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return ResultUtil.ok(Collections.singletonMap(SystemConstant.TASK_ID, tbTaskHistory.getId()));
    }

    @ApiOperation(value = "推送考试数据到云阅卷")
    @ApiResponses({@ApiResponse(code = 200, message = "推送考试数据到云阅卷", response = Result.class)})
    @RequestMapping(value = "/cloud_mark/push/data", method = RequestMethod.POST)
    public Result pushData(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
                           @ApiParam(value = "是否推送客观分", required = true) @RequestParam Boolean objectiveScorePush,
                           @ApiParam(value = "是否推送违纪考生", required = true) @RequestParam Boolean examStudentBreachPush) {
        TBTaskHistory tbTaskHistory = null;
        try {
            ExamCacheBean examCacheBean = teExamService.getExamCacheBean(Long.parseLong(examId));
            Optional.ofNullable(examCacheBean).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));

            InvigilateMonitorStatusEnum invigilateMonitorStatusEnum = examCacheBean.getMonitorStatus();//监考状态
            if (Objects.nonNull(invigilateMonitorStatusEnum) && !Objects
                    .equals(invigilateMonitorStatusEnum, InvigilateMonitorStatusEnum.FINISHED)) {
                throw new BusinessException("当前批次监考未结束，请在结束后操作");
            }
            ScoreStatusEnum scoreStatusEnum = examCacheBean.getScoreStatus();//算分状态
            if (Objects.nonNull(scoreStatusEnum) && (Objects.equals(scoreStatusEnum, ScoreStatusEnum.CALCULATING) || Objects
                    .equals(scoreStatusEnum, ScoreStatusEnum.NEED_CALCULATE))) {
                throw new BusinessException("当前批次算分未结束，请在结束后操作");
            }
            QueryWrapper<TEExamCourse> teExamCourseQueryWrapper = new QueryWrapper<>();
            teExamCourseQueryWrapper.lambda().eq(TEExamCourse::getExamId, examId);
            List<TEExamCourse> teExamCourseList = teExamCourseService.list(teExamCourseQueryWrapper);
            Set answerList = null;
            if (Objects.nonNull(teExamCourseList) && teExamCourseList.size() > 0) {
                answerList = teExamCourseList.stream().filter(s -> {
                    if (Objects.nonNull(s.getHasAnswer()) && s.getHasAnswer().intValue() == 0) {
                        return true;
                    } else {
                        return false;
                    }
                }).collect(Collectors.toSet());
            }
            if (Objects.nonNull(answerList) && answerList.size() > 0) {
                throw new BusinessException("当前批次标答未补齐，请补齐后操作");
            }

            Map transMap = new HashMap();
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();

            QueryWrapper<TBTaskHistory> tbTaskHistoryQueryWrapper = new QueryWrapper<>();
            tbTaskHistoryQueryWrapper.lambda().eq(TBTaskHistory::getExamId, examCacheBean.getId())
                    .eq(TBTaskHistory::getType, TaskTypeEnum.CLOUD_MARK_DATA_PUSH)
                    .orderByDesc(TBTaskHistory::getCreateTime)
                    .last(" limit 1 ");
            tbTaskHistory = taskHistoryService.getOne(tbTaskHistoryQueryWrapper);

            if (Objects.isNull(tbTaskHistory) || (tbTaskHistory.getStatus() == TaskStatusEnum.FINISH && (Objects.nonNull(tbTaskHistory.getProgress()) && tbTaskHistory.getProgress().doubleValue() == 100D))) {
                //往任务表里插一条数据
                tbTaskHistory = new TBTaskHistory(TaskTypeEnum.CLOUD_MARK_DATA_PUSH, TaskStatusEnum.INIT,
                        SystemConstant.DATA_PUSH_INIT, 0d, tbUser.getId(), tbUser.getOrgId());
                tbTaskHistory.setExamId(examCacheBean.getId());
                taskHistoryService.save(tbTaskHistory);
            } else if (tbTaskHistory.getStatus() != TaskStatusEnum.FINISH) {
                throw new BusinessException("当前推送任务正在进行中，请稍后再试！");
            }

            TEExam teExam = teExamService.getById(Long.parseLong(examId));
            teExam.setObjectiveScorePush(objectiveScorePush);
            teExam.setExamStudentBreachPush(examStudentBreachPush);
            teExam.setUpdateTime(System.currentTimeMillis());
            teExamService.updateById(teExam);
            teExamService.updateExamCacheBean(Long.parseLong(examId));

            transMap.put("tbTaskHistory", tbTaskHistory);
            transMap.put(SystemConstant.CREATE_ID, tbUser.getId());
            transMap.put(SystemConstant.ORG_ID, tbUser.getOrgId());
            transMap.put(SystemConstant.EXAM_ID, Long.parseLong(examId));
            transMap.put(SystemConstant.EXAM_CODE, examCacheBean.getCode());
            transMap.put(SystemConstant.OBJECTIVE_SCORE_PUSH, objectiveScorePush);
            transMap.put(SystemConstant.EXAM_STUDENT_BREACH_PUSH, examStudentBreachPush);
            //mq发送消息start
            MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.CLOUD_MARK_DATA_PUSH.name(),
                    transMap, MqTagEnum.CLOUD_MARK_DATA_PUSH, String.valueOf(tbTaskHistory.getId()),
                    tbUser.getName());
            mqDtoService.assembleSendAsyncMsg(mqDto);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return ResultUtil.ok(Collections.singletonMap(SystemConstant.TASK_ID, tbTaskHistory.getId()));
    }

    @ApiOperation(value = "修正作答记录缓存到数据库")
    @RequestMapping(value = "/persisted_answer", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result persistedAnswer(@ApiParam(value = "考试记录id") @RequestParam(required = false) Long examRecordId,
                                  @ApiParam(value = "考试批次id") @RequestParam(required = false) Long examId,
                                  @ApiParam(value = "考试场次id") @RequestParam(required = false) Long examActivityId,
                                  @ApiParam(value = "考生id") @RequestParam(required = false) Long examStudentId,
                                  @ApiParam(value = "是否批量处理") @RequestParam(required = false) Boolean batch,
                                  @ApiParam(value = "补救措施", required = true) @RequestParam RepariEnum repariEnum,
                                  @ApiParam(value = "考试状态") @RequestParam(required = false) ExamRecordStatusEnum status) {
        List<TOeExamRecord> tOeExamRecordList = null;
        if (Objects.nonNull(batch) && batch) {
            if (repariEnum == RepariEnum.EXAM_STUDENT) {
                List<TEExamStudent> teExamStudentList = teExamStudentService.findAlreadyExamCountZero(examId, examActivityId, examStudentId, examRecordId);
                if (!CollectionUtils.isEmpty(teExamStudentList)) {
                    for (TEExamStudent t : teExamStudentList) {
                        teExamStudentService.updateExamStudentByCache(t.getId());
                    }
                }
                return ResultUtil.ok(true);
            } else {
                tOeExamRecordList = tOeExamRecordService.persistedAnswerBatch(examId, examActivityId, status);
            }
        } else {
            QueryWrapper<TOeExamRecord> tOeExamRecordQueryWrapper = new QueryWrapper<>();
            if (Objects.nonNull(examRecordId)) {
                tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getId, examRecordId);
            }
            if (Objects.nonNull(examId)) {
                tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamId, examId);
            }
            if (Objects.nonNull(examActivityId)) {
                tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamActivityId, examActivityId);
            }
            if (Objects.nonNull(examStudentId)) {
                tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamStudentId, examStudentId);
            }
            tOeExamRecordList = tOeExamRecordService.list(tOeExamRecordQueryWrapper);
        }
        if (!CollectionUtils.isEmpty(tOeExamRecordList)) {
            for (TOeExamRecord t : tOeExamRecordList) {
                try {
                    switch (repariEnum) {
                        case ANSWER:
                        case FINISHED_ANSWER:
                            tOeExamRecordService.saveDataByCachePersistedAnswer(t.getId());
                            break;
                        case FINISHED:
                            ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(t.getExamStudentId());
                            teExamService.finish(examStudentCacheBean.getStudentId(), t.getId(), FinishTypeEnum.AUTO.name(), t.getDurationSeconds());
                            break;
                        case EXAM_STUDENT:
                            teExamStudentService.updateExamStudentByCache(t.getExamStudentId());
                            break;
                        default:
                            break;
                    }
                } catch (Exception e) {
                    log.error(SystemConstant.LOG_ERROR, e);
                }
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "删除用户缓存")
    @RequestMapping(value = "/delete_cache", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result deleteCache(@ApiParam(value = "考试记录id") @RequestParam(required = false) Long examRecordId,
                              @ApiParam(value = "考试批次id") @RequestParam(required = false) Long examId,
                              @ApiParam(value = "考生id") @RequestParam(required = false) Long examStudentId) {
        QueryWrapper<TOeExamRecord> tOeExamRecordQueryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(examRecordId)) {
            tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getId, examRecordId);
        }
        if (Objects.nonNull(examId)) {
            tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamId, examId);
        }
        if (Objects.nonNull(examStudentId)) {
            tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamStudentId, examStudentId);
        }
//        List<TOeExamRecord> tOeExamRecordList = tOeExamRecordService.list(tOeExamRecordQueryWrapper);
//        if (!CollectionUtils.isEmpty(tOeExamRecordList)) {
//            for (TOeExamRecord t : tOeExamRecordList) {
//                ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(t.getExamStudentId());
//                TEStudentCacheDto teStudent = cacheService.addStudentAccountCache(examStudentCacheBean.getStudentId());
//                commonService.deleteExamStudentCache(t.getId(), teStudent.getId());
//            }
//        }
        tOeExamRecordQueryWrapper.lambda().isNotNull(TOeExamRecord::getFirstStartTime)
                .isNotNull(TOeExamRecord::getFinishTime)
                .eq(TOeExamRecord::getStatus, ExamRecordStatusEnum.FINISHED);
        List<TOeExamRecord> tOeExamRecordList = tOeExamRecordService.list(tOeExamRecordQueryWrapper);
        if (!CollectionUtils.isEmpty(tOeExamRecordList)) {
            for (TOeExamRecord t : tOeExamRecordList) {
                if ((System.currentTimeMillis() - t.getFinishTime().longValue()) / 1000 / 60 >= 30) {//超过30分钟则改状态
                    try {
                        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(t.getExamStudentId());
                        TEStudentCacheDto teStudent = themisCacheService.addStudentAccountCache(examStudentCacheBean.getStudentId());
                        commonService.persisted(t.getId(), teStudent.getId());
                    } catch (Exception e) {
                    }
                }
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "清除考试缓存")
    @RequestMapping(value = "/clean_exam_first_prepare_cache", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试信息")})
    public Result cleanExamFirstPrepareCache(@ApiParam(value = "考试批次ids") @RequestParam(required = false) Set<Long> examIds) {
        if (!CollectionUtils.isEmpty(examIds)) {
            List<Map> list = tOeExamRecordService.cleanExamFirstPrepareCache(examIds);
            if (!CollectionUtils.isEmpty(list)) {
                Set<Long> examRecordIds = new HashSet<>();
                for (Map map : list) {
                    try {
                        Long recordId = Long.parseLong(String.valueOf(map.get("examRecordId")));
                        Long studentId = Long.parseLong(String.valueOf(map.get("studentId")));
                        examRecordIds.add(recordId);
                        ExamingDataCacheUtil.deleteUnFinishedRecordId(studentId);
                        ExamingDataCacheUtil.deleteExamingRecordId(studentId);
                        redisUtil.delete(RedisKeyHelper.examRecordCacheKey(recordId));
                        redisUtil.delete(RedisKeyHelper.examAnswerKey(recordId));
                        redisUtil.delete(RedisKeyHelper.audioLeftPlayCountKey(recordId));
                        redisUtil.delete(RedisKeyHelper.livenessVerifyCacheKey(recordId));
                        redisUtil.delete(RedisKeyHelper.faceVerifyCacheKey(recordId));
                        redisUtil.delete(RedisKeyHelper.studentPaperStructKey(recordId));
                    } catch (Exception e) {
                    }
                }
                tOeExamRecordService.removeByIds(examRecordIds);
            }
        }
        return ResultUtil.ok(true);
    }
}