package com.qmth.distributed.print.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.distributed.print.business.bean.dto.*;
import com.qmth.distributed.print.business.bean.params.SerialNumberParams;
import com.qmth.distributed.print.business.bean.result.ExaminationDetailResult;
import com.qmth.distributed.print.business.bean.result.ExaminationResult;
import com.qmth.distributed.print.business.bean.result.SummarizedDataResult;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.ExamDataSourceEnum;
import com.qmth.distributed.print.business.enums.ExamDetailStatusEnum;
import com.qmth.distributed.print.business.enums.PrintPlanStatusEnum;
import com.qmth.distributed.print.business.mapper.ExamDetailMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.distributed.print.business.templete.execute.AsyncCreatePdfTempleteService;
import com.qmth.teachcloud.common.bean.dto.MqDto;
import com.qmth.teachcloud.common.bean.params.BasicStudentParams;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.MqTagEnum;
import com.qmth.teachcloud.common.enums.PdfTypeEnum;
import com.qmth.teachcloud.common.enums.TaskTypeEnum;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ConvertUtil;
import com.qmth.teachcloud.common.util.RedisUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 考务数据 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class ExamDetailServiceImpl extends ServiceImpl<ExamDetailMapper, ExamDetail> implements ExamDetailService {
    @Resource
    private ExamDetailCourseService examDetailCourseService;
    @Resource
    private BasicExamRuleService basicExamRuleService;
    @Resource
    private ExamDetailMapper examDetailMapper;
    @Resource
    private CommonCacheService commonCacheService;
    @Resource
    private ExamStudentService examStudentService;
    @Resource
    private ExamPrintPlanService examPrintPlanService;
    @Resource
    private BasicAttachmentService basicAttachmentService;
    @Resource
    private BasicStudentService basicStudentService;

    @Resource
    TBTaskService tbTaskService;
    @Resource
    ConvertUtil convertUtil;

    @Autowired
    private ClientPrintDataService clientPrintDataService;

    @Resource
    AsyncCreatePdfTempleteService asyncCreatePdfTempleteService;

    @Autowired
    ClientStatusService clientStatusService;

    @Resource
    TeachcloudCommonService teachcloudCommonService;

    @Autowired
    BasicCampusService basicCampusService;

    @Autowired
    ExamTaskService examTaskService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    BasicClazzService basicClazzService;

    @Resource
    TeachClazzService teachClazzService;

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

    @Transactional(rollbackFor = Exception.class)
    @Override
    public double calculateTotalSubjects(Long printPlanId) {
        List<ExamDetail> examDetailList = this.list(new QueryWrapper<ExamDetail>().lambda().eq(ExamDetail::getPrintPlanId, printPlanId));
        return examDetailList.stream().collect(Collectors.summarizingDouble(ExamDetail::getTotalSubjects)).getSum();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public double calculateTotalPackages(Long printPlanId) {
        List<ExamDetail> examDetailList = this.list(new QueryWrapper<ExamDetail>().lambda().eq(ExamDetail::getPrintPlanId, printPlanId));
        return examDetailList.size();
    }

    @Override
    public IPage<PrintTaskDto> listPrintTask(Long printPlanId, String status, String courseCode, String paperNumber, String examPlace, String examRoom, Long examStartTime, Long examEndTime, Long printStartTime, Long printEndTime, Long printHouseId, Integer pageNumber, Integer pageSize) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        Page<PrintTaskDto> page = new Page<>(pageNumber, pageSize);
        return this.baseMapper.listPrintTask(page, schoolId, printPlanId, status, courseCode, paperNumber, examPlace, examRoom, examStartTime, examEndTime, printStartTime, printEndTime, printHouseId, orgIds);
    }

    @Override
    public PrintTaskTotalDto taskTotalData(Long printPlanId, String status, String courseCode, String paperNumber, String examPlace, String examRoom, Long examStartTime, Long examEndTime, Long printStartTime, Long printEndTime, Long printHouseId) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        PrintTaskTotalDto printTaskTotalDto = this.baseMapper.taskTotalData(schoolId, printPlanId, status, courseCode, paperNumber, examPlace, examRoom, examStartTime, examEndTime, printStartTime, printEndTime, printHouseId, orgIds);

        if (printTaskTotalDto != null) {
            // 试卷总计
            Integer paperCount = this.baseMapper.selectPaperCount(schoolId, printPlanId, status, courseCode, paperNumber, examPlace, examRoom, examStartTime, examEndTime, printHouseId, orgIds);
            printTaskTotalDto.setPaperCount(paperCount == null ? 0 : paperCount);
        }
        return printTaskTotalDto;
    }

    @Override
    public List<JSONObject> taskViewPDF(Long examDetailId) {
        ExamDetail examDetail = this.getById(examDetailId);
        List<JSONObject> list = new ArrayList<>();
        if (examDetail.getAttachmentId() != null) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type", PdfTypeEnum.ALL_A3);
            BasicAttachment attachment = basicAttachmentService.getById(examDetail.getAttachmentId());
            jsonObject.put("url", attachment == null ? null : teachcloudCommonService.filePreview(attachment.getPath()));
            list.add(jsonObject);
        }
        if (examDetail.getCardAttachmentId() != null) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("type", PdfTypeEnum.CARD_A3);
            BasicAttachment attachment = basicAttachmentService.getById(examDetail.getCardAttachmentId());
            jsonObject.put("url", attachment == null ? null : teachcloudCommonService.filePreview(attachment.getPath()));
            list.add(jsonObject);
        }
        return list;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<FieldsDto> findExaminationFields(Long schoolId) {
        BasicExamRule basicExamRule = basicExamRuleService.list(new QueryWrapper<BasicExamRule>().lambda().eq(BasicExamRule::getSchoolId, schoolId)).get(0);
        if (basicExamRule == null) {
            throw ExceptionResultEnum.ERROR.exception("找不到该学校考务字段信息");
        }
        String requiredFields = basicExamRule.getRequiredFields();
        String extendFields = basicExamRule.getExtendFields();
        // 必选字段
        List<FieldsDto> requiredFieldsList = JSONObject.parseArray(requiredFields, FieldsDto.class);
        if (requiredFieldsList.stream().anyMatch(e -> !e.getEnable())) {
            throw ExceptionResultEnum.ERROR.exception("该学校考务字段设置存在必选字段禁用的情况");
        }
        for (FieldsDto fieldsDto : requiredFieldsList) {
            fieldsDto.setLevel("primary");
        }

        // 扩展字段
        List<FieldsDto> extendFieldsList = JSONObject.parseArray(extendFields, FieldsDto.class);
        List<String> extendCodeList = extendFieldsList.stream().map(FieldsDto::getCode).collect(Collectors.toList());
        if (extendCodeList.stream().anyMatch(e -> requiredFieldsList.stream().map(FieldsDto::getCode).collect(Collectors.toList()).contains(e))) {
            throw ExceptionResultEnum.ERROR.exception("该学校考务字段设置存在相同的代码");
        }
        // 有效的扩展字段
        List<FieldsDto> validExtendList = extendFieldsList.stream().filter(FieldsDto::getEnable).collect(Collectors.toList());
        for (FieldsDto fieldsDto : validExtendList) {
            fieldsDto.setLevel("secondary");
        }

        // 有效的考务字段
        List<FieldsDto> validExaminationFieldList = new ArrayList<>();
        validExaminationFieldList.addAll(requiredFieldsList);
        validExaminationFieldList.addAll(validExtendList);

        List<String> fieldsName = validExaminationFieldList.stream().map(FieldsDto::getName).collect(Collectors.toList());
        return validExaminationFieldList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void downLoadExaminationTemplate(Long schoolId, HttpServletResponse response) throws IOException {
        final String DEFALUT_EXT = ".xlsx";
        final String DEFALUT_CONTENT_TYPE = "application/vnd.ms-excel";
        List<String> fieldsNameList = this.findExaminationFields(schoolId).stream().map(FieldsDto::getName).distinct().collect(Collectors.toList());

        log.debug("导出Excel开始...");
        XSSFWorkbook wb = new XSSFWorkbook();
        String fileName = "考务数据模板";
        response.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, SystemConstant.CHARSET_NAME) + DEFALUT_EXT);
        response.setContentType(DEFALUT_CONTENT_TYPE);
        XSSFSheet sheet = wb.createSheet(commonCacheService.schoolCache(schoolId).getName() + "考务数据模板");

        XSSFFont font = wb.createFont();
        font.setFontHeightInPoints((short) 11);
        font.setFontName("宋体"); //什么字体

        XSSFRow row = sheet.createRow(0);
        XSSFCellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setFont(font);

        for (int i = 0; i < fieldsNameList.size(); i++) {
            XSSFCell cell = row.createCell(i);
            cell.setCellValue(fieldsNameList.get(i));
            cell.setCellStyle(style);
        }
        ServletOutputStream outputStream = response.getOutputStream();
        wb.write(outputStream);
        outputStream.flush();
        outputStream.close();
        log.debug("导出Excel结束...");

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public IPage<ExaminationResult> findExaminationBriefPage(Long schoolId, List<Long> printPlanIdList, String courseCode, String paperNumber, String examPlace, String examRoom, String packageCode, Long startDate, Long endDate, int pageNumber, int pageSize, Set<Long> orgIds) {
        IPage<ExaminationResult> page = examDetailMapper.findBriefPage(new Page<>(pageNumber, pageSize), schoolId, printPlanIdList, courseCode, paperNumber, examPlace, examRoom, packageCode, startDate, endDate, orgIds);
        List<ExaminationResult> list = page.getRecords();
        for (ExaminationResult examinationResult : list) {
            String examDetailCourseIds = examinationResult.getExamDetailCourseIds();
            if (examDetailCourseIds == null || examDetailCourseIds.length() == 0) {
                throw ExceptionResultEnum.ERROR.exception("考务数据缺失课程信息");
            }
            // 处理课程和科目
            String[] arr = examDetailCourseIds.split(",");

            final String separator = "、";
            String courseNameCodeStr = "";
            String paperNumberStr = "";
            for (String examDetailCourseId : arr) {
                ExamDetailCourse examDetailCourse = examDetailCourseService.getById(examDetailCourseId);
                String code = examDetailCourse.getCourseCode();
                String name = examDetailCourse.getCourseName();
                String number = examDetailCourse.getPaperNumber();
                courseNameCodeStr = courseNameCodeStr + name + "(" + code + ")" + separator;
                paperNumberStr = paperNumberStr + number + separator;
            }
            courseNameCodeStr = courseNameCodeStr.substring(0, courseNameCodeStr.length() - separator.length());
            paperNumberStr = paperNumberStr.substring(0, paperNumberStr.length() - separator.length());
            examinationResult.setCourseNameCode(courseNameCodeStr);
            examinationResult.setPaperNumber(paperNumberStr);

            // 处理时间
            Long startTime = examinationResult.getExamStartTime();
            Long endTime = examinationResult.getExamEndTime();
            Map<String, Object> dateMap = ConvertUtil.analyzeDateAndTime(startTime, endTime);
            String date = String.valueOf(dateMap.get("date"));
            String time = String.valueOf(dateMap.get("time"));
            examinationResult.setExamDate(date);
            examinationResult.setExamTime(time);
        }
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SummarizedDataResult findSummarizedData(Long schoolId, List<Long> printPlanIdList, String courseCode, String paperNumber, String examPlace, String examRoom, String packageCode, Long startDate, Long endDate, Set<Long> orgIds) {
        IPage<ExaminationResult> page = this.findExaminationBriefPage(schoolId, printPlanIdList, courseCode, paperNumber, examPlace, examRoom, packageCode, startDate, endDate, SystemConstant.PAGE_NUMBER, SystemConstant.PAGE_SIZE, orgIds);
        List<ExaminationResult> list = page.getRecords();
        int totalSubjects = 0;
        for (ExaminationResult examinationResult : list) {
            int subjects = examinationResult.getTotalSubjects();
            totalSubjects = totalSubjects + subjects;
        }
        SummarizedDataResult summarizedDataResult = new SummarizedDataResult();
        summarizedDataResult.setSchoolId(schoolId);
        summarizedDataResult.setTotalSubjects(totalSubjects);
        summarizedDataResult.setPackageCount(list.size());
        return summarizedDataResult;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public IPage<ExaminationDetailResult> findExaminationDetail(Long schoolId, List<Long> printPlanIdList, String courseCode, String paperNumber, String examPlace, String examRoom, String studentParams, Long startDate, Long endDate, int pageNumber, int pageSize) {
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        return examDetailMapper.findDetailPage(new Page<>(pageNumber, pageSize), schoolId, printPlanIdList, courseCode, paperNumber, examPlace, examRoom, SystemConstant.translateSpecificSign(studentParams), startDate, endDate, orgIds);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public IPage<ExaminationDetailResult> findExaminationDetail(Long examDetailId, int pageNumber, int pageSize) {
        return examDetailMapper.findDetailPageById(new Page<>(pageNumber, pageSize), examDetailId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> findExamPlaceDatasource(String param, List<Long> printPlanIdList) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        QueryWrapper<ExamDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamDetail::getSchoolId, schoolId).in(ExamDetail::getOrgId, orgIds);
        if (StringUtils.isNotBlank(param)) {
            queryWrapper.lambda().like(ExamDetail::getExamPlace, SystemConstant.translateSpecificSign(param));
        }
        if (printPlanIdList.size() > 0) {
            queryWrapper.lambda().in(ExamDetail::getPrintPlanId, printPlanIdList);
        }
        List<ExamDetail> examDetail = this.list(queryWrapper);
        return examDetail.stream().map(ExamDetail::getExamPlace).distinct().collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> findExamRoomDatasource(String param, List<Long> printPlanIdList) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        QueryWrapper<ExamDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamDetail::getSchoolId, schoolId).in(ExamDetail::getOrgId, orgIds);
        if (StringUtils.isNotBlank(param)) {
            queryWrapper.lambda().like(ExamDetail::getExamRoom, SystemConstant.translateSpecificSign(param));
        }
        if (printPlanIdList.size() > 0) {
            queryWrapper.lambda().in(ExamDetail::getPrintPlanId, printPlanIdList);
        }
        List<ExamDetail> examDetail = this.list(queryWrapper);
        return examDetail.stream().map(ExamDetail::getExamRoom).distinct().collect(Collectors.toList());
    }

    @Transactional
    @Override
    public boolean submitTask(ExamDetail examDetail) {
        // 检查前置操作是否完成(是否生成pdf)
        ExamDetail detail = this.getById(examDetail.getId());
        if (detail == null) {
            throw ExceptionResultEnum.ERROR.exception("考场数据异常");
        }

        // 印刷计划必须为就绪才可提交印刷
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(detail.getPrintPlanId());
        if (!PrintPlanStatusEnum.READY.equals(examPrintPlan.getStatus()) && !PrintPlanStatusEnum.PRINTING.equals(examPrintPlan.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("印刷计划就绪、印刷中状态才可提交印刷");
        }

        // 只有就绪状态并且附件生成才可提交
        if (!ExamDetailStatusEnum.READY.equals(detail.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("考场就绪状态才可提交印刷");
        } else if (detail.getAttachmentId() == null) {
            throw ExceptionResultEnum.ERROR.exception("考场文件未生成");
        }

        // 修改印刷计划状态为印刷中
        UpdateWrapper<ExamPrintPlan> examPrintPlanUpdateWrapper = new UpdateWrapper<>();
        examPrintPlanUpdateWrapper.lambda().set(ExamPrintPlan::getStatus, PrintPlanStatusEnum.PRINTING).eq(ExamPrintPlan::getId, detail.getPrintPlanId());
        examPrintPlanService.update(examPrintPlanUpdateWrapper);

        // 修改考场状态为待印刷
        UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamDetail::getStatus, ExamDetailStatusEnum.WAITING).eq(ExamDetail::getId, examDetail.getId());

        return this.update(updateWrapper);
    }

    @Transactional
    @Override
    public boolean taskCancel(ExamDetail examDetail) {
        ExamDetail detail = this.getById(examDetail.getId());
        if (!detail.getStatus().name().equals(ExamDetailStatusEnum.WAITING.name())) {
            throw ExceptionResultEnum.ERROR.exception("印刷状态为“待印刷”的任务可撤回提交");
        }
        UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamDetail::getStatus, ExamDetailStatusEnum.READY).eq(ExamDetail::getId, examDetail.getId());
        this.update(updateWrapper);

        //所有考场都撤回，印刷任务状态改为就绪
        QueryWrapper<ExamDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamDetail::getPrintPlanId, detail.getPrintPlanId()).notIn(ExamDetail::getStatus, ExamDetailStatusEnum.NEW, ExamDetailStatusEnum.READY);
        List<ExamDetail> examDetails = this.list(queryWrapper);
        if (examDetails.isEmpty()) {
            UpdateWrapper<ExamPrintPlan> printPlanUpdateWrapper = new UpdateWrapper<>();
            printPlanUpdateWrapper.lambda().set(ExamPrintPlan::getStatus, PrintPlanStatusEnum.READY).eq(ExamPrintPlan::getId, detail.getPrintPlanId());
            examPrintPlanService.update(printPlanUpdateWrapper);
        }

        // 当关联课程所有考场都撤回，清除提当前计划下打样记录
        QueryWrapper<ExamDetailCourse> examDetailCourseQueryWrapper = new QueryWrapper<>();
        examDetailCourseQueryWrapper.lambda().eq(ExamDetailCourse::getExamDetailId, detail.getId());
        List<ExamDetailCourse> examDetailCourses = examDetailCourseService.list(examDetailCourseQueryWrapper);
        for (ExamDetailCourse examDetailCours : examDetailCourses) {
            // 清空审核表附件ID
            UpdateWrapper<ExamTask> examTaskUpdateWrapper = new UpdateWrapper<>();
            examTaskUpdateWrapper.lambda().set(ExamTask::getApproveFormAttachmentId, null)
                    .eq(ExamTask::getSchoolId, examDetailCours.getSchoolId())
                    .eq(ExamTask::getCourseCode, examDetailCours.getCourseCode())
                    .eq(ExamTask::getPaperNumber, examDetailCours.getPaperNumber());
            examTaskService.update(examTaskUpdateWrapper);

            List<ExamDetail> examDetailList = this.listByPrintPlanIdAndCourseCodeAndPaperNumber(detail.getSchoolId(), detail.getPrintPlanId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber());
            long count = examDetailList.stream().filter(m -> !m.getStatus().name().equals(ExamDetailStatusEnum.NEW.name()) && !m.getStatus().name().equals(ExamDetailStatusEnum.READY.name())).count();
            if (count == 0) {
                UpdateWrapper<ClientStatus> clientStatusUpdateWrapper = new UpdateWrapper<>();
                clientStatusUpdateWrapper.lambda().eq(ClientStatus::getSchoolId, detail.getSchoolId())
                        .eq(ClientStatus::getPrintPlanId, detail.getPrintPlanId())
                        .eq(ClientStatus::getCourseCode, examDetailCours.getCourseCode())
                        .eq(ClientStatus::getPaperNumber, examDetailCours.getPaperNumber());
                clientStatusService.remove(clientStatusUpdateWrapper);
            }
        }
        return true;
    }

    @Override
    public boolean resubmitTask(ExamDetail examDetail) throws IOException {
        ExamDetail detail = this.getById(examDetail.getId());
        if (!detail.getStatus().name().equals(ExamDetailStatusEnum.PRINTING.name())
                && !detail.getStatus().name().equals(ExamDetailStatusEnum.FINISH.name())) {
            throw ExceptionResultEnum.ERROR.exception("印刷状态为“印刷中”、“已完成”的任务可重新提交印刷");
        }

        // 调用生成pdf方法
        Map<String, Object> map = tbTaskService.saveTask(TaskTypeEnum.CREATE_PDF, detail.getPrintPlanId(), (SysUser) ServletUtil.getRequestUser(), detail.getId());
//        asyncCreatePdfTempleteService.createPdf(map, null);
        MqDto mqDto = new MqDto(MqTagEnum.PDF.getCode(), map, String.valueOf(map.get(SystemConstant.TB_TASK_ID)));
        redisUtil.sendMessage(mqDto.getTopic(), mqDto);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<Long> disposeExamDetailByExaminationExcel(List<ExaminationImportDto> dataList, Long userId, SerialNumberParams serialNumberParams, SysUser sysUser) {
        List<Map<String, Object>> examDetailKeyList = dataList.stream().flatMap(e -> {
            String campusName = e.getCampusName();
            Long schoolIdId = e.getSchoolId();
            Long printHouseId = basicCampusService.getPrintHouseIdBySchoolIdAndCampusName(schoolIdId, campusName);
            Map<String, Object> map = new HashMap<>();
            map.put("schoolId", schoolIdId);
            map.put("printPlanId", e.getPrintPlanId());
            map.put("printPlanName", e.getPrintPlanName());
            map.put("examPlace", e.getExamPlace());
            map.put("examRoom", e.getExamRoom());
            map.put("examStartTime", e.getExamStartTime());
            map.put("examEndTime", e.getExamEndTime());
            map.put("printHouseId", printHouseId);
            return Stream.of(map);
        }).distinct().collect(Collectors.toList());

        // 同一任务是否有多个校区
        Map<String, List<Map<String, Object>>> stringListMap = examDetailKeyList.stream().collect(Collectors.groupingBy(m -> m.get("schoolId") + "#" + m.get("printPlanId") + "#" + m.get("printPlanName") + "#" + m.get("examPlace") + "#" + m.get("examRoom") + "#" + m.get("examStartTime") + "#" + m.get("examEndTime")));
        for (Map.Entry<String, List<Map<String, Object>>> entry : stringListMap.entrySet()) {
            if (entry.getValue().size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("同一考场对应校区指派的印刷室只能为一个");
            }
        }

        List<ExamDetail> examDetailList = new ArrayList<>();
        for (Map<String, Object> map : examDetailKeyList) {
            Long schoolId = Long.valueOf(String.valueOf(map.get("schoolId")));
            String examPlace = String.valueOf(map.get("examPlace"));
            String examRoom = String.valueOf(map.get("examRoom"));
            String examStartTime = String.valueOf(map.get("examStartTime"));
            String examEndTime = String.valueOf(map.get("examEndTime"));
            Long printHouseId = Long.valueOf(String.valueOf(map.get("printHouseId")));

            long totalSubjects = dataList.stream()
                    .filter(e -> e.getExamPlace().equals(examPlace) &&
                            e.getExamRoom().equals(examRoom) &&
                            e.getExamStartTime().equals(examStartTime) &&
                            e.getExamEndTime().equals(examEndTime)).count();

            ExamDetail examDetail = new ExamDetail();
            examDetail.setId(SystemConstant.getDbUuid());
            examDetail.setPackageCode(convertUtil.getIncre(serialNumberParams.getPrefix(), serialNumberParams.getModel(), serialNumberParams.getDigit()));
            examDetail.setSchoolId(schoolId);
            examDetail.setOrgId(sysUser.getOrgId());
            examDetail.setPrintPlanId(Long.valueOf(String.valueOf(map.get("printPlanId"))));
            examDetail.setPrintPlanName(String.valueOf(map.get("printPlanName")));
            examDetail.setExamPlace(examPlace);
            examDetail.setExamRoom(examRoom);
            examDetail.setStatus(ExamDetailStatusEnum.NEW);
            examDetail.setTotalSubjects(Math.toIntExact(totalSubjects));
            examDetail.setExamStartTime(SystemConstant.convertIdToLong(examStartTime));
            examDetail.setExamEndTime(SystemConstant.convertIdToLong(examEndTime));
            examDetail.setExamDataSource(ExamDataSourceEnum.FILE_IMPORT);
            examDetail.setNormal(true);
            examDetail.setPrintHouseId(printHouseId);
            examDetail.setCreateId(userId);
            examDetail.setUpdateId(userId);
            examDetailList.add(examDetail);
        }
        this.saveBatch(examDetailList);
        return examDetailList.stream().map(ExamDetail::getId).distinct().collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void disposeExamDetailCourseByExaminationExcel(List<ExaminationImportDto> dataList, Long userId, SysUser sysUser) {
        List<Map<String, Object>> examDetailKeyList = dataList.stream().flatMap(e -> {
            Map<String, Object> map = new HashMap<>();
            map.put("schoolId", e.getSchoolId());
            map.put("printPlanId", e.getPrintPlanId());
            map.put("examPlace", e.getExamPlace());
            map.put("examRoom", e.getExamRoom());
            map.put("examStartTime", e.getExamStartTime());
            map.put("examEndTime", e.getExamEndTime());
            map.put("courseCode", e.getCourseCode());
            map.put("courseName", e.getCourseName());
            map.put("paperNumber", e.getPaperNumber());
            return Stream.of(map);
        }).distinct().collect(Collectors.toList());

        List<ExamDetailCourse> examDetailCourseList = new ArrayList<>();
        for (Map<String, Object> map : examDetailKeyList) {
            Long schoolId = Long.valueOf(String.valueOf(map.get("schoolId")));
            Long printPlanId = Long.valueOf(String.valueOf(map.get("printPlanId")));
            String examPlace = String.valueOf(map.get("examPlace"));
            String examRoom = String.valueOf(map.get("examRoom"));
            Long examStartTime = SystemConstant.convertIdToLong(String.valueOf(map.get("examStartTime")));
            Long examEndTime = SystemConstant.convertIdToLong(String.valueOf(map.get("examEndTime")));
            String courseCode = String.valueOf(map.get("courseCode"));
            String courseName = String.valueOf(map.get("courseName"));
            String paperNumber = String.valueOf(map.get("paperNumber"));

            // 获取该科目下的考生人数
            List<ExaminationImportDto> sameCourseList = dataList.stream().filter(e -> e.getSchoolId().equals(schoolId) &&
                    e.getPrintPlanId().equals(printPlanId) &&
                    e.getExamPlace().equals(examPlace) &&
                    e.getExamRoom().equals(examRoom) &&
                    Long.valueOf(e.getExamStartTime()).equals(examStartTime) &&
                    Long.valueOf(e.getExamEndTime()).equals(examEndTime) &&
                    e.getCourseCode().equals(courseCode) &&
                    e.getCourseName().equals(courseName) &&
                    e.getPaperNumber().equals(paperNumber)).collect(Collectors.toList());

            System.out.println(JSON.toJSONString(sameCourseList));

            int totalSubjects = sameCourseList.size();


            List<ExamDetail> examDetailList = this.list(new QueryWrapper<ExamDetail>().lambda()
                    .eq(ExamDetail::getPrintPlanId, printPlanId)
                    .eq(ExamDetail::getExamPlace, examPlace)
                    .eq(ExamDetail::getExamRoom, examRoom)
                    .eq(ExamDetail::getExamStartTime, examStartTime)
                    .eq(ExamDetail::getExamEndTime, examEndTime));
            if (examDetailList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("无法找到新增的考务场次数据");
            }
            Long examDetailId = examDetailList.get(0).getId();
            ExamDetailCourse examDetailCourse = new ExamDetailCourse();
            examDetailCourse.setId(SystemConstant.getDbUuid());
            examDetailCourse.setSchoolId(schoolId);
            examDetailCourse.setOrgId(sysUser.getOrgId());
            examDetailCourse.setExamDetailId(examDetailId);
            examDetailCourse.setCourseCode(courseCode);
            examDetailCourse.setCourseName(courseName);
            examDetailCourse.setPaperNumber(paperNumber);
            examDetailCourse.setTotalSubjects(totalSubjects);
            examDetailCourse.setCreateId(userId);
            examDetailCourse.setUpdateId(userId);
            examDetailCourseList.add(examDetailCourse);
        }
        examDetailCourseService.saveBatch(examDetailCourseList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void disposeExamStudentByExaminationExcel(List<ExaminationImportDto> dataList, Long userId, SysUser sysUser) {
        List<ExamStudent> examStudentList = new ArrayList<>();
        List<BasicStudentParams> basicStudentParamsList = new ArrayList<>();
        for (ExaminationImportDto examinationImportDto : dataList) {
            Long schoolId = examinationImportDto.getSchoolId();
            Long printPlanId = examinationImportDto.getPrintPlanId();
            String examPlace = examinationImportDto.getExamPlace();
            String examRoom = examinationImportDto.getExamRoom();
            Long examStartTime = SystemConstant.convertIdToLong(examinationImportDto.getExamStartTime());
            Long examEndTime = SystemConstant.convertIdToLong(examinationImportDto.getExamEndTime());
            String courseCode = examinationImportDto.getCourseCode();
            String courseName = examinationImportDto.getCourseName();
            String paperNumber = examinationImportDto.getPaperNumber();
            String studentName = examinationImportDto.getStudentName();
            String studentCode = examinationImportDto.getStudentCode();
            String ticketNumber = examinationImportDto.getTicketNumber();
            String siteNumber = examinationImportDto.getSiteNumber();
            String campusName = examinationImportDto.getCampusName();

            List<FieldsDto> fieldsDtoList = examinationImportDto.getSecondaryFieldList();
            List<ExtendFieldsDto> extendFieldsDtoList = this.getExtendFieldsByFields(fieldsDtoList);
            List<ExamDetail> examDetailList = this.list(new QueryWrapper<ExamDetail>().lambda()
                    .eq(ExamDetail::getPrintPlanId, printPlanId)
                    .eq(ExamDetail::getExamPlace, examPlace)
                    .eq(ExamDetail::getExamRoom, examRoom)
                    .eq(ExamDetail::getExamStartTime, examStartTime)
                    .eq(ExamDetail::getExamEndTime, examEndTime));
            if (examDetailList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("无法找到新增的考务场次数据");
            }
            Long examDetailId = examDetailList.get(0).getId();
            List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(new QueryWrapper<ExamDetailCourse>().lambda()
                    .eq(ExamDetailCourse::getExamDetailId, examDetailId)
                    .eq(ExamDetailCourse::getCourseCode, courseCode)
                    .eq(ExamDetailCourse::getCourseName, courseName)
                    .eq(ExamDetailCourse::getPaperNumber, paperNumber));
            if (examDetailCourseList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("无法找到新增的考务科目数据");
            }
            Long examDetailCourseId = examDetailCourseList.get(0).getId();

            ExamStudent examStudent = new ExamStudent();
            examStudent.setId(SystemConstant.getDbUuid());
            examStudent.setSchoolId(schoolId);
            examStudent.setOrgId(sysUser.getOrgId());
            examStudent.setExamDetailCourseId(examDetailCourseId);
            examStudent.setStudentName(studentName);
            examStudent.setStudentCode(studentCode);
            examStudent.setTicketNumber(ticketNumber);
            examStudent.setExtendFields(JSON.toJSONString(extendFieldsDtoList));
            examStudent.setSiteNumber(siteNumber);
            examStudent.setCreateId(userId);
            examStudent.setUpdateId(userId);
            examStudentList.add(examStudent);


            BasicStudentParams basicStudentParams = new BasicStudentParams();
            BasicStudent tmp = basicStudentService.getOne(new QueryWrapper<BasicStudent>().lambda()
                    .eq(BasicStudent::getSchoolId, sysUser.getSchoolId())
                    .eq(BasicStudent::getEnable, true)
                    .eq(BasicStudent::getStudentCode, studentCode));
            if (Objects.nonNull(tmp)) {
                basicStudentParams.setId(tmp.getId());
                basicStudentParams.setCollegeId(tmp.getBelongOrgId());
                basicStudentParams.setMajorId(tmp.getMajorId());
                basicStudentParams.setClazzId(tmp.getClazzId());
                basicStudentParams.setPhoneNumber(tmp.getPhoneNumber());
            }
            basicStudentParams.setStudentCode(studentCode);
            basicStudentParams.setStudentName(studentName);
            basicStudentParams.setCampusId(basicCampusService.getOne(new QueryWrapper<BasicCampus>()
                            .lambda()
                            .eq(BasicCampus::getSchoolId, schoolId)
                            .eq(BasicCampus::getCampusName, campusName))
                    .getId());
            basicStudentParamsList.add(basicStudentParams);
        }
        examStudentService.saveBatch(examStudentList);
        // 更新基础学生表数据
        basicStudentService.saveOrUpdateBasicStudentBatch(basicStudentParamsList, sysUser);
    }

    @Override
    public List<ExamDetail> listByPrintPlanIdAndCourseCodeAndPaperNumber(Long schoolId, Long printPlanId, String courseCode, String paperNumber) {
        List<ExamDetailCourse> examDetailCourses = examDetailCourseService.listByPrintPlanIdAndCourseCodeAndPaperNumber(schoolId, printPlanId, courseCode, paperNumber);
        if (examDetailCourses != null && examDetailCourses.size() > 0) {
            Set<Long> examDetailIds = examDetailCourses.stream().map(ExamDetailCourse::getExamDetailId).collect(Collectors.toSet());
            return this.listByIds(examDetailIds);
        }
        return null;
    }

    @Override
    public List<ExamDetail> listByExamPrintId(Long printPlanId) {
        QueryWrapper<ExamDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamDetail::getPrintPlanId, printPlanId)
                .ne(ExamDetail::getStatus, ExamDetailStatusEnum.READY);
        return this.list(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<ExaminationExportDto> findExaminationExportDtoDatasource(Map<String, Object> map) {
        List<Long> printPlanIdList = null;
        if (map.get("printPlanIdList") != null) {
            printPlanIdList = (List<Long>) map.get("printPlanIdList");
        }
        String courseCode = null;
        if (map.get("courseCode") != null) {
            courseCode = String.valueOf(map.get("courseCode"));
        }
        String paperNumber = null;
        if (map.get("paperNumber") != null) {
            paperNumber = String.valueOf(map.get("paperNumber"));
        }
        String examPlace = null;
        if (map.get("examPlace") != null) {
            examPlace = String.valueOf(map.get("examPlace"));
        }
        String examRoom = null;
        if (map.get("examRoom") != null) {
            examRoom = String.valueOf(map.get("examRoom"));
        }
        String packageCode = null;
        if (map.get("packageCode") != null) {
            packageCode = String.valueOf(map.get("packageCode"));
        }
        Long startDate = null;
        if (map.get("startDate") != null) {
            startDate = Long.valueOf(String.valueOf(map.get("startDate")));
        }
        Long endDate = null;
        if (map.get("endDate") != null) {
            endDate = Long.valueOf(String.valueOf(map.get("endDate")));
        }
        Set<Long> orgIds = new HashSet<>();
        if (Objects.nonNull(map.get("orgIds"))) {
            orgIds = (Set<Long>) map.get("orgIds");
        }
        TBTask tbTask = (TBTask) map.get(SystemConstant.TASK);
        Long userId = tbTask.getCreateId();
        Long schoolId = tbTask.getSchoolId();

        List<ExaminationResult> examinationResultList = this.findExaminationBriefPage(schoolId,
                printPlanIdList, courseCode, paperNumber, examPlace, examRoom, packageCode, startDate, endDate,
                SystemConstant.PAGE_NUMBER, SystemConstant.PAGE_SIZE, orgIds).getRecords();
        int index = 0;
        List<ExaminationExportDto> examinationExportDtoList = new ArrayList<>();
        for (ExaminationResult examinationResult : examinationResultList) {
            index++;
            ExaminationExportDto examinationExportDto = new ExaminationExportDto();
            examinationExportDto.setIndex((long) index);
            examinationExportDto.setPrintPlanName(examinationResult.getPrintPlanName());
            examinationExportDto.setExamDate(examinationResult.getExamDate());
            examinationExportDto.setExamTime(examinationResult.getExamTime());
            examinationExportDto.setExamPlace(examinationResult.getExamPlace());
            examinationExportDto.setExamRoom(examinationResult.getExamRoom());
            examinationExportDto.setPackageCode(examinationResult.getPackageCode());
            examinationExportDto.setCourseNameCode(examinationResult.getCourseNameCode());
            examinationExportDto.setPaperNumber(examinationResult.getPaperNumber());
            examinationExportDto.setTotalSubjects(examinationResult.getTotalSubjects());
            examinationExportDtoList.add(examinationExportDto);
        }
        return examinationExportDtoList;
    }

    @Transactional
    @Override
    public Boolean updatePrintProgress(Long schoolId, Long examDetailId, String machineCode, Integer printProgress, Boolean isPrint) {

        ExamDetail examDetail = this.getById(examDetailId);
        // 撤回提示
        if (ExamDetailStatusEnum.NEW.name().equals(examDetail.getStatus())
                || ExamDetailStatusEnum.READY.name().equals(examDetail.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("该任务已被撤回");
        }

        if (isPrint) {
            // 更新考场进度、状态、打印结束时间
            UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamDetail::getPrintProgress, printProgress).set(ExamDetail::getPrintEndTime, System.currentTimeMillis()).set(ExamDetail::getStatus, ExamDetailStatusEnum.FINISH).eq(ExamDetail::getId, examDetailId);
            this.update(updateWrapper);

            // 所有考场打印完成，更新印刷计划状态
            QueryWrapper<ExamDetail> examDetailQueryWrapper = new QueryWrapper<>();
            examDetailQueryWrapper.lambda().eq(ExamDetail::getPrintPlanId, examDetail.getPrintPlanId()).ne(ExamDetail::getStatus, ExamDetailStatusEnum.FINISH);
            int count = this.count(examDetailQueryWrapper);
            if (count == 0) {
                UpdateWrapper<ExamPrintPlan> examPrintPlanUpdateWrapper = new UpdateWrapper<>();
                examPrintPlanUpdateWrapper.lambda().set(ExamPrintPlan::getStatus, PrintPlanStatusEnum.PRINT_FINISH).eq(ExamPrintPlan::getId, examDetail.getPrintPlanId());
                examPrintPlanService.update(examPrintPlanUpdateWrapper);
            }

            // 记录机器打印数量
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            ClientPrintData clientPrintData = new ClientPrintData();
            clientPrintData.setId(SystemConstant.getDbUuid());
            clientPrintData.setSchoolId(schoolId);
            clientPrintData.setExamDetailId(examDetailId);
            clientPrintData.setMachineCode(machineCode);
            clientPrintData.setPagesA3(examDetail.getPagesA3());
            clientPrintData.setPagesA4(examDetail.getPagesA4());
            clientPrintData.setCreateId(sysUser.getId());
            clientPrintData.setCreateTime(System.currentTimeMillis());
            clientPrintDataService.save(clientPrintData);
        } else {
            // 更新打印结束时间
            UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamDetail::getPrintEndTime, System.currentTimeMillis()).eq(ExamDetail::getId, examDetailId);
            this.update(updateWrapper);
        }
        return true;
    }

    @Override
    public IPage<ClientExamStudentDto> listStudent(Long schoolId, Long examDetailId, String ticketNumber, String studentName, String courseCode, Integer pageNumber, Integer pageSize) {
        // 撤回提示
        ExamDetail examDetail = this.getById(examDetailId);
        if (ExamDetailStatusEnum.NEW.name().equals(examDetail.getStatus())
                || ExamDetailStatusEnum.READY.name().equals(examDetail.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("该任务已被撤回");
        }
        Page<ClientExamStudentDto> page = new Page<>(pageNumber, pageSize);
        return this.baseMapper.listClientExamStudentPage(page, schoolId, examDetailId, SystemConstant.translateSpecificSign(ticketNumber), SystemConstant.translateSpecificSign(studentName), courseCode);
    }

    @Override
    public List<Map> listStudentByExamDetailCourseId(Long schoolId, Long examDetailCourseId, String ticketNumber, String type) {
        return this.baseMapper.listStudentByExamDetailCourseId(schoolId, examDetailCourseId, ticketNumber, type);
    }

    @Override
    public List<ExamDetail> listByCourseCodeAndPaperNumberAndPaperTypeIsNull(Long schoolId, String courseCode, String paperNumber) {
        List<ExamDetailCourse> examDetailCourses = examDetailCourseService.listByCourseCodeAndPaperNumber(schoolId, courseCode, paperNumber);
        examDetailCourses = examDetailCourses.stream().filter(m -> StringUtils.isBlank(m.getPaperType())).collect(Collectors.toList());
        if (examDetailCourses.size() > 0) {
            Set<Long> examDetailIds = examDetailCourses.stream().map(ExamDetailCourse::getExamDetailId).collect(Collectors.toSet());
            return this.listByIds(examDetailIds);
        }
        return null;
    }

    @Override
    public List<ExamDetail> listByCourseCodeAndPaperNumber(Long schoolId, String courseCode, String paperNumber) {
        List<ExamDetailCourse> examDetailCourses = examDetailCourseService.listByCourseCodeAndPaperNumber(schoolId, courseCode, paperNumber);
        if (examDetailCourses != null && examDetailCourses.size() > 0) {
            Set<Long> examDetailIds = examDetailCourses.stream().map(m -> m.getExamDetailId()).collect(Collectors.toSet());
            List<ExamDetail> examDetails = this.listByIds(examDetailIds);
            return examDetails;
        }
        return null;
    }

    @Override
    public List<ExamDetailCourse> listSyncPaperNumberByPrintPlanId(Long printPlanId) {
        return this.baseMapper.listSyncPaperNumberByPrintPlanId(printPlanId);
    }

    @Override
    public boolean taskNormal(ExamDetail examDetail) {
        UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamDetail::getNormal, examDetail.getNormal()).eq(ExamDetail::getId, examDetail.getId());
        return this.update(updateWrapper);
    }

    @Override
    public String findClazzNamesByClazzIds(String clazzIds, String separator) {
        if (!SystemConstant.strNotNull(separator)) {
            separator = ",";
        }
        if (StringUtils.isBlank(clazzIds)) {
            return null;
        }
        String[] strArr = clazzIds.split(separator);
        Set<Long> clazzIdSet = Arrays.stream(strArr).map(SystemConstant::convertIdToLong).collect(Collectors.toSet());
        List<String> clazzNameList = this.findClazzNamesByClazzIdSet(clazzIdSet);
        return String.join(separator, clazzNameList);
    }

    @Override
    public String findClazzNamesByClazzIds(List<Long> clazzIdList, String separator) {
        if (!SystemConstant.strNotNull(separator)) {
            separator = ",";
        }
        Set<Long> clazzIdSet = new HashSet<>(clazzIdList);
        List<String> clazzNameList = this.findClazzNamesByClazzIdSet(clazzIdSet);
        return String.join(separator, clazzNameList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteExaminationData(Long printPlanId, ExamDataSourceEnum source) {
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
        if (examPrintPlan == null) {
            throw ExceptionResultEnum.ERROR.exception("未找到印刷计划信息");
        }
        PrintPlanStatusEnum status = examPrintPlan.getStatus();
        if (PrintPlanStatusEnum.NEW != status && PrintPlanStatusEnum.READY != status) {
            throw ExceptionResultEnum.ERROR.exception(status.getDesc() + "状态下的印刷计划不能删除考务数据");
        }
        // 准备删除的考务-场次表id
        LambdaQueryWrapper<ExamDetail> examDetailQueryWrapper = new QueryWrapper<ExamDetail>().lambda().eq(ExamDetail::getPrintPlanId, printPlanId);
        if (Objects.nonNull(source)){
            examDetailQueryWrapper.eq(ExamDetail::getExamDataSource, source);
        }
        List<Long> examDetailIds = this.list(examDetailQueryWrapper).stream().map(ExamDetail::getId).collect(Collectors.toList());
        if (examDetailIds.size() == 0) {
            return;
        }
        // 删除exam_detail 表数据
        this.remove(new QueryWrapper<ExamDetail>().lambda().in(ExamDetail::getId, examDetailIds));

        // 准备删除的考务-科目表id
        List<Long> examDetailCourseIds = examDetailCourseService.list(new QueryWrapper<ExamDetailCourse>().lambda().in(ExamDetailCourse::getExamDetailId, examDetailIds))
                .stream().map(ExamDetailCourse::getId).collect(Collectors.toList());
        if (examDetailCourseIds.size() == 0) {
            return;
        }
        // 删除exam_detail_course 表数据
        examDetailCourseService.remove(new QueryWrapper<ExamDetailCourse>().lambda().in(ExamDetailCourse::getId, examDetailCourseIds));

        // 准备删除的考务-考生表id
        List<Long> examStudentIds = examStudentService.list(new QueryWrapper<ExamStudent>().lambda().in(ExamStudent::getExamDetailCourseId, examDetailCourseIds))
                .stream().map(ExamStudent::getId).collect(Collectors.toList());
        if (examStudentIds.size() == 0) {
            return;
        }
        // 删除exam_student 表数据
        examStudentService.remove(new QueryWrapper<ExamStudent>().lambda().in(ExamStudent::getId, examStudentIds));
    }

    private List<ExtendFieldsDto> getExtendFieldsByFields(List<FieldsDto> fieldsDtoList) {
        List<ExtendFieldsDto> extendFieldsDtoList = new ArrayList<>();
        for (FieldsDto fieldsDto : fieldsDtoList) {
            ExtendFieldsDto extendFieldsDto = new ExtendFieldsDto();
            BeanUtils.copyProperties(fieldsDto, extendFieldsDto);
            extendFieldsDtoList.add(extendFieldsDto);
        }
        return extendFieldsDtoList;
    }

    /**
     * 根据clazzIdSet集合查询班级名称集合
     *
     * @param clazzIdSet 班级idset
     * @return 班级名称集合
     */
    private List<String> findClazzNamesByClazzIdSet(Set<Long> clazzIdSet) {
        Set<String> clazzNameSet = new HashSet<>();
        if (clazzIdSet.size() > 0) {
            Set<String> basicClazzSet = basicClazzService.list(new QueryWrapper<BasicClazz>().lambda().in(BasicClazz::getId, clazzIdSet)).stream().map(BasicClazz::getClazzName).collect(Collectors.toSet());
            Set<String> teachClazzSet = teachClazzService.list(new QueryWrapper<TeachClazz>().lambda().in(TeachClazz::getId, clazzIdSet)).stream().map(TeachClazz::getTeachClazzName).collect(Collectors.toSet());
            clazzNameSet.addAll(basicClazzSet);
            clazzNameSet.addAll(teachClazzSet);
        }
        return new ArrayList<>(clazzNameSet);
    }
}
